From 2bd7fd00420044142279399d522b449ddb5c399a Mon Sep 17 00:00:00 2001 From: liverbool Date: Thu, 18 Oct 2018 16:59:15 +0700 Subject: [PATCH] Initial commit --- .gitattributes | 2 + AbstractExampleFactory.php | 27 + AbstractResourceFixture.php | 122 + ExampleFactoryInterface.php | 27 + LICENSE | 21 + LocaleAwareFactoryTrait.php | 60 + OptionsResolver/LazyOption.php | 170 + README.md | 1 + composer.json | 32 + composer.lock | 2809 ++++++++++++++++ vendor/autoload.php | 7 + vendor/behat/transliterator/CHANGELOG.md | 22 + vendor/behat/transliterator/CONTRIBUTING.md | 18 + vendor/behat/transliterator/LICENSE | 128 + vendor/behat/transliterator/README.md | 27 + vendor/behat/transliterator/composer.json | 34 + .../src/Behat/Transliterator/SyncTool.php | 213 ++ .../Behat/Transliterator/Transliterator.php | 590 ++++ .../src/Behat/Transliterator/data/x00.php | 19 + .../src/Behat/Transliterator/data/x01.php | 19 + .../src/Behat/Transliterator/data/x02.php | 19 + .../src/Behat/Transliterator/data/x03.php | 19 + .../src/Behat/Transliterator/data/x04.php | 19 + .../src/Behat/Transliterator/data/x05.php | 19 + .../src/Behat/Transliterator/data/x06.php | 19 + .../src/Behat/Transliterator/data/x07.php | 19 + .../src/Behat/Transliterator/data/x09.php | 19 + .../src/Behat/Transliterator/data/x0a.php | 19 + .../src/Behat/Transliterator/data/x0b.php | 19 + .../src/Behat/Transliterator/data/x0c.php | 19 + .../src/Behat/Transliterator/data/x0d.php | 19 + .../src/Behat/Transliterator/data/x0e.php | 19 + .../src/Behat/Transliterator/data/x0f.php | 19 + .../src/Behat/Transliterator/data/x10.php | 19 + .../src/Behat/Transliterator/data/x11.php | 19 + .../src/Behat/Transliterator/data/x12.php | 19 + .../src/Behat/Transliterator/data/x13.php | 19 + .../src/Behat/Transliterator/data/x14.php | 19 + .../src/Behat/Transliterator/data/x15.php | 19 + .../src/Behat/Transliterator/data/x16.php | 19 + .../src/Behat/Transliterator/data/x17.php | 19 + .../src/Behat/Transliterator/data/x18.php | 19 + .../src/Behat/Transliterator/data/x1e.php | 19 + .../src/Behat/Transliterator/data/x1f.php | 19 + .../src/Behat/Transliterator/data/x20.php | 19 + .../src/Behat/Transliterator/data/x21.php | 19 + .../src/Behat/Transliterator/data/x24.php | 19 + .../src/Behat/Transliterator/data/x25.php | 19 + .../src/Behat/Transliterator/data/x26.php | 19 + .../src/Behat/Transliterator/data/x27.php | 19 + .../src/Behat/Transliterator/data/x28.php | 19 + .../src/Behat/Transliterator/data/x30.php | 19 + .../src/Behat/Transliterator/data/x31.php | 19 + .../src/Behat/Transliterator/data/x32.php | 19 + .../src/Behat/Transliterator/data/x33.php | 19 + .../src/Behat/Transliterator/data/x4e.php | 19 + .../src/Behat/Transliterator/data/x4f.php | 19 + .../src/Behat/Transliterator/data/x50.php | 19 + .../src/Behat/Transliterator/data/x51.php | 19 + .../src/Behat/Transliterator/data/x52.php | 19 + .../src/Behat/Transliterator/data/x53.php | 19 + .../src/Behat/Transliterator/data/x54.php | 19 + .../src/Behat/Transliterator/data/x55.php | 19 + .../src/Behat/Transliterator/data/x56.php | 19 + .../src/Behat/Transliterator/data/x57.php | 19 + .../src/Behat/Transliterator/data/x58.php | 19 + .../src/Behat/Transliterator/data/x59.php | 19 + .../src/Behat/Transliterator/data/x5a.php | 19 + .../src/Behat/Transliterator/data/x5b.php | 19 + .../src/Behat/Transliterator/data/x5c.php | 19 + .../src/Behat/Transliterator/data/x5d.php | 19 + .../src/Behat/Transliterator/data/x5e.php | 19 + .../src/Behat/Transliterator/data/x5f.php | 19 + .../src/Behat/Transliterator/data/x60.php | 19 + .../src/Behat/Transliterator/data/x61.php | 19 + .../src/Behat/Transliterator/data/x62.php | 19 + .../src/Behat/Transliterator/data/x63.php | 19 + .../src/Behat/Transliterator/data/x64.php | 19 + .../src/Behat/Transliterator/data/x65.php | 19 + .../src/Behat/Transliterator/data/x66.php | 19 + .../src/Behat/Transliterator/data/x67.php | 19 + .../src/Behat/Transliterator/data/x68.php | 19 + .../src/Behat/Transliterator/data/x69.php | 19 + .../src/Behat/Transliterator/data/x6a.php | 19 + .../src/Behat/Transliterator/data/x6b.php | 19 + .../src/Behat/Transliterator/data/x6c.php | 19 + .../src/Behat/Transliterator/data/x6d.php | 19 + .../src/Behat/Transliterator/data/x6e.php | 19 + .../src/Behat/Transliterator/data/x6f.php | 19 + .../src/Behat/Transliterator/data/x70.php | 19 + .../src/Behat/Transliterator/data/x71.php | 19 + .../src/Behat/Transliterator/data/x72.php | 19 + .../src/Behat/Transliterator/data/x73.php | 19 + .../src/Behat/Transliterator/data/x74.php | 19 + .../src/Behat/Transliterator/data/x75.php | 19 + .../src/Behat/Transliterator/data/x76.php | 19 + .../src/Behat/Transliterator/data/x77.php | 19 + .../src/Behat/Transliterator/data/x78.php | 19 + .../src/Behat/Transliterator/data/x79.php | 19 + .../src/Behat/Transliterator/data/x7a.php | 19 + .../src/Behat/Transliterator/data/x7b.php | 19 + .../src/Behat/Transliterator/data/x7c.php | 19 + .../src/Behat/Transliterator/data/x7d.php | 19 + .../src/Behat/Transliterator/data/x7e.php | 19 + .../src/Behat/Transliterator/data/x7f.php | 19 + .../src/Behat/Transliterator/data/x80.php | 19 + .../src/Behat/Transliterator/data/x81.php | 19 + .../src/Behat/Transliterator/data/x82.php | 19 + .../src/Behat/Transliterator/data/x83.php | 19 + .../src/Behat/Transliterator/data/x84.php | 19 + .../src/Behat/Transliterator/data/x85.php | 19 + .../src/Behat/Transliterator/data/x86.php | 19 + .../src/Behat/Transliterator/data/x87.php | 19 + .../src/Behat/Transliterator/data/x88.php | 19 + .../src/Behat/Transliterator/data/x89.php | 19 + .../src/Behat/Transliterator/data/x8a.php | 19 + .../src/Behat/Transliterator/data/x8b.php | 19 + .../src/Behat/Transliterator/data/x8c.php | 19 + .../src/Behat/Transliterator/data/x8d.php | 19 + .../src/Behat/Transliterator/data/x8e.php | 19 + .../src/Behat/Transliterator/data/x8f.php | 19 + .../src/Behat/Transliterator/data/x90.php | 19 + .../src/Behat/Transliterator/data/x91.php | 19 + .../src/Behat/Transliterator/data/x92.php | 19 + .../src/Behat/Transliterator/data/x93.php | 19 + .../src/Behat/Transliterator/data/x94.php | 19 + .../src/Behat/Transliterator/data/x95.php | 19 + .../src/Behat/Transliterator/data/x96.php | 19 + .../src/Behat/Transliterator/data/x97.php | 19 + .../src/Behat/Transliterator/data/x98.php | 19 + .../src/Behat/Transliterator/data/x99.php | 19 + .../src/Behat/Transliterator/data/x9a.php | 19 + .../src/Behat/Transliterator/data/x9b.php | 19 + .../src/Behat/Transliterator/data/x9c.php | 19 + .../src/Behat/Transliterator/data/x9d.php | 19 + .../src/Behat/Transliterator/data/x9e.php | 19 + .../src/Behat/Transliterator/data/x9f.php | 19 + .../src/Behat/Transliterator/data/xa0.php | 19 + .../src/Behat/Transliterator/data/xa1.php | 19 + .../src/Behat/Transliterator/data/xa2.php | 19 + .../src/Behat/Transliterator/data/xa3.php | 19 + .../src/Behat/Transliterator/data/xa4.php | 19 + .../src/Behat/Transliterator/data/xac.php | 19 + .../src/Behat/Transliterator/data/xad.php | 19 + .../src/Behat/Transliterator/data/xae.php | 19 + .../src/Behat/Transliterator/data/xaf.php | 19 + .../src/Behat/Transliterator/data/xb0.php | 19 + .../src/Behat/Transliterator/data/xb1.php | 19 + .../src/Behat/Transliterator/data/xb2.php | 19 + .../src/Behat/Transliterator/data/xb3.php | 19 + .../src/Behat/Transliterator/data/xb4.php | 19 + .../src/Behat/Transliterator/data/xb5.php | 19 + .../src/Behat/Transliterator/data/xb6.php | 19 + .../src/Behat/Transliterator/data/xb7.php | 19 + .../src/Behat/Transliterator/data/xb8.php | 19 + .../src/Behat/Transliterator/data/xb9.php | 19 + .../src/Behat/Transliterator/data/xba.php | 19 + .../src/Behat/Transliterator/data/xbb.php | 19 + .../src/Behat/Transliterator/data/xbc.php | 19 + .../src/Behat/Transliterator/data/xbd.php | 19 + .../src/Behat/Transliterator/data/xbe.php | 19 + .../src/Behat/Transliterator/data/xbf.php | 19 + .../src/Behat/Transliterator/data/xc0.php | 19 + .../src/Behat/Transliterator/data/xc1.php | 19 + .../src/Behat/Transliterator/data/xc2.php | 19 + .../src/Behat/Transliterator/data/xc3.php | 19 + .../src/Behat/Transliterator/data/xc4.php | 19 + .../src/Behat/Transliterator/data/xc5.php | 19 + .../src/Behat/Transliterator/data/xc6.php | 19 + .../src/Behat/Transliterator/data/xc7.php | 19 + .../src/Behat/Transliterator/data/xc8.php | 19 + .../src/Behat/Transliterator/data/xc9.php | 19 + .../src/Behat/Transliterator/data/xca.php | 19 + .../src/Behat/Transliterator/data/xcb.php | 19 + .../src/Behat/Transliterator/data/xcc.php | 19 + .../src/Behat/Transliterator/data/xcd.php | 19 + .../src/Behat/Transliterator/data/xce.php | 19 + .../src/Behat/Transliterator/data/xcf.php | 19 + .../src/Behat/Transliterator/data/xd0.php | 19 + .../src/Behat/Transliterator/data/xd1.php | 19 + .../src/Behat/Transliterator/data/xd2.php | 19 + .../src/Behat/Transliterator/data/xd3.php | 19 + .../src/Behat/Transliterator/data/xd4.php | 19 + .../src/Behat/Transliterator/data/xd5.php | 19 + .../src/Behat/Transliterator/data/xd6.php | 19 + .../src/Behat/Transliterator/data/xd7.php | 19 + .../src/Behat/Transliterator/data/xf9.php | 19 + .../src/Behat/Transliterator/data/xfa.php | 19 + .../src/Behat/Transliterator/data/xfb.php | 19 + .../src/Behat/Transliterator/data/xfc.php | 19 + .../src/Behat/Transliterator/data/xfd.php | 19 + .../src/Behat/Transliterator/data/xfe.php | 19 + .../src/Behat/Transliterator/data/xff.php | 19 + vendor/composer/ClassLoader.php | 445 +++ vendor/composer/LICENSE | 21 + vendor/composer/autoload_classmap.php | 16 + vendor/composer/autoload_files.php | 12 + vendor/composer/autoload_namespaces.php | 15 + vendor/composer/autoload_psr4.php | 44 + vendor/composer/autoload_real.php | 90 + vendor/composer/autoload_static.php | 279 ++ vendor/composer/installed.json | 2879 +++++++++++++++++ vendor/doctrine/annotations/CHANGELOG.md | 130 + vendor/doctrine/annotations/LICENSE | 19 + vendor/doctrine/annotations/README.md | 17 + vendor/doctrine/annotations/composer.json | 34 + .../Common/Annotations/Annotation.php | 79 + .../Annotations/Annotation/Attribute.php | 47 + .../Annotations/Annotation/Attributes.php | 37 + .../Common/Annotations/Annotation/Enum.php | 84 + .../Annotation/IgnoreAnnotation.php | 54 + .../Annotations/Annotation/Required.php | 33 + .../Common/Annotations/Annotation/Target.php | 107 + .../Annotations/AnnotationException.php | 197 ++ .../Common/Annotations/AnnotationReader.php | 425 +++ .../Common/Annotations/AnnotationRegistry.php | 174 + .../Common/Annotations/CachedReader.php | 262 ++ .../Doctrine/Common/Annotations/DocLexer.php | 134 + .../Doctrine/Common/Annotations/DocParser.php | 1190 +++++++ .../Common/Annotations/FileCacheReader.php | 290 ++ .../Common/Annotations/IndexedReader.php | 119 + .../Doctrine/Common/Annotations/PhpParser.php | 91 + .../Doctrine/Common/Annotations/Reader.php | 89 + .../Annotations/SimpleAnnotationReader.php | 127 + .../Common/Annotations/TokenParser.php | 194 ++ vendor/doctrine/annotations/phpstan.neon | 17 + vendor/doctrine/cache/LICENSE | 19 + vendor/doctrine/cache/README.md | 10 + vendor/doctrine/cache/UPGRADE.md | 16 + vendor/doctrine/cache/composer.json | 42 + vendor/doctrine/cache/docs/en/index.rst | 274 ++ .../lib/Doctrine/Common/Cache/ApcCache.php | 104 + .../lib/Doctrine/Common/Cache/ApcuCache.php | 106 + .../lib/Doctrine/Common/Cache/ArrayCache.php | 113 + .../cache/lib/Doctrine/Common/Cache/Cache.php | 90 + .../Doctrine/Common/Cache/CacheProvider.php | 325 ++ .../lib/Doctrine/Common/Cache/ChainCache.php | 186 ++ .../Doctrine/Common/Cache/ClearableCache.php | 21 + .../Common/Cache/CouchbaseBucketCache.php | 195 ++ .../Doctrine/Common/Cache/CouchbaseCache.php | 102 + .../Doctrine/Common/Cache/ExtMongoDBCache.php | 196 ++ .../lib/Doctrine/Common/Cache/FileCache.php | 276 ++ .../Doctrine/Common/Cache/FilesystemCache.php | 102 + .../Doctrine/Common/Cache/FlushableCache.php | 18 + .../Common/Cache/LegacyMongoDBCache.php | 174 + .../Doctrine/Common/Cache/MemcacheCache.php | 102 + .../Doctrine/Common/Cache/MemcachedCache.php | 130 + .../Doctrine/Common/Cache/MongoDBCache.php | 110 + .../Common/Cache/MultiDeleteCache.php | 21 + .../Doctrine/Common/Cache/MultiGetCache.php | 21 + .../Common/Cache/MultiOperationCache.php | 12 + .../Doctrine/Common/Cache/MultiPutCache.php | 23 + .../Doctrine/Common/Cache/PhpFileCache.php | 118 + .../lib/Doctrine/Common/Cache/PredisCache.php | 143 + .../lib/Doctrine/Common/Cache/RedisCache.php | 175 + .../lib/Doctrine/Common/Cache/RiakCache.php | 228 ++ .../Doctrine/Common/Cache/SQLite3Cache.php | 206 ++ .../lib/Doctrine/Common/Cache/Version.php | 8 + .../lib/Doctrine/Common/Cache/VoidCache.php | 59 + .../Doctrine/Common/Cache/WinCacheCache.php | 106 + .../lib/Doctrine/Common/Cache/XcacheCache.php | 102 + .../Doctrine/Common/Cache/ZendDataCache.php | 68 + vendor/doctrine/collections/CONTRIBUTING.md | 67 + vendor/doctrine/collections/LICENSE | 19 + vendor/doctrine/collections/README.md | 27 + vendor/doctrine/collections/composer.json | 35 + .../Collections/AbstractLazyCollection.php | 343 ++ .../Common/Collections/ArrayCollection.php | 413 +++ .../Common/Collections/Collection.php | 263 ++ .../Doctrine/Common/Collections/Criteria.php | 261 ++ .../Expr/ClosureExpressionVisitor.php | 267 ++ .../Common/Collections/Expr/Comparison.php | 106 + .../Collections/Expr/CompositeExpression.php | 90 + .../Common/Collections/Expr/Expression.php | 35 + .../Collections/Expr/ExpressionVisitor.php | 82 + .../Common/Collections/Expr/Value.php | 52 + .../Common/Collections/ExpressionBuilder.php | 200 ++ .../Common/Collections/Selectable.php | 48 + vendor/doctrine/common/LICENSE | 19 + vendor/doctrine/common/README.md | 11 + vendor/doctrine/common/UPGRADE_TO_2_1 | 39 + vendor/doctrine/common/UPGRADE_TO_2_2 | 61 + vendor/doctrine/common/composer.json | 48 + vendor/doctrine/common/docs/en/index.rst | 10 + .../docs/en/reference/class-loading.rst | 242 ++ vendor/doctrine/common/humbug.json.dist | 11 + .../lib/Doctrine/Common/ClassLoader.php | 267 ++ .../lib/Doctrine/Common/CommonException.php | 13 + .../common/lib/Doctrine/Common/Comparable.php | 28 + .../common/lib/Doctrine/Common/Lexer.php | 25 + .../Doctrine/Common/NotifyPropertyChanged.php | 24 + .../Common/PropertyChangedListener.php | 27 + .../Common/Proxy/AbstractProxyFactory.php | 245 ++ .../lib/Doctrine/Common/Proxy/Autoloader.php | 82 + .../Exception/InvalidArgumentException.php | 106 + .../Proxy/Exception/OutOfBoundsException.php | 26 + .../Common/Proxy/Exception/ProxyException.php | 15 + .../Exception/UnexpectedValueException.php | 72 + .../lib/Doctrine/Common/Proxy/Proxy.php | 74 + .../Doctrine/Common/Proxy/ProxyDefinition.php | 53 + .../Doctrine/Common/Proxy/ProxyGenerator.php | 1064 ++++++ .../lib/Doctrine/Common/Util/ClassUtils.php | 93 + .../common/lib/Doctrine/Common/Util/Debug.php | 167 + .../lib/Doctrine/Common/Util/Inflector.php | 19 + .../common/lib/Doctrine/Common/Version.php | 37 + vendor/doctrine/common/phpstan.neon | 10 + vendor/doctrine/data-fixtures/.gitignore | 3 + vendor/doctrine/data-fixtures/.travis.yml | 32 + vendor/doctrine/data-fixtures/CHANGELOG.md | 170 + vendor/doctrine/data-fixtures/LICENSE | 20 + vendor/doctrine/data-fixtures/README.md | 201 ++ vendor/doctrine/data-fixtures/UPGRADE | 17 + vendor/doctrine/data-fixtures/composer.json | 40 + .../Common/DataFixtures/AbstractFixture.php | 89 + .../DependentFixtureInterface.php | 37 + .../Listener/MongoDBReferenceListener.php | 62 + .../Event/Listener/ORMReferenceListener.php | 62 + .../Exception/CircularReferenceException.php | 9 + .../Executor/AbstractExecutor.php | 143 + .../DataFixtures/Executor/MongoDBExecutor.php | 67 + .../DataFixtures/Executor/ORMExecutor.php | 75 + .../DataFixtures/Executor/PHPCRExecutor.php | 63 + .../Common/DataFixtures/FixtureInterface.php | 20 + .../Doctrine/Common/DataFixtures/Loader.php | 381 +++ .../DataFixtures/OrderedFixtureInterface.php | 21 + .../DataFixtures/ProxyReferenceRepository.php | 121 + .../DataFixtures/Purger/MongoDBPurger.php | 58 + .../Common/DataFixtures/Purger/ORMPurger.php | 248 ++ .../DataFixtures/Purger/PHPCRPurger.php | 45 + .../DataFixtures/Purger/PurgerInterface.php | 18 + .../DataFixtures/ReferenceRepository.php | 240 ++ .../DataFixtures/SharedFixtureInterface.php | 23 + .../DataFixtures/Sorter/TopologicalSorter.php | 180 ++ .../Common/DataFixtures/Sorter/Vertex.php | 43 + .../doctrine/data-fixtures/phpunit.xml.dist | 24 + .../Tests/Common/DataFixtures/BaseTest.php | 43 + .../DataFixtures/DependentFixtureTest.php | 363 +++ .../Executor/ORMExecutorSharedFixtureTest.php | 79 + .../DataFixtures/Executor/ORMExecutorTest.php | 64 + .../Executor/PHPCRExecutorTest.php | 186 ++ .../Tests/Common/DataFixtures/FixtureTest.php | 33 + .../Tests/Common/DataFixtures/LoaderTest.php | 61 + .../DataFixtures/OrderedFixtureTest.php | 72 + .../ProxyReferenceRepositoryTest.php | 141 + .../DataFixtures/Purger/MongoDBPurgerTest.php | 67 + .../Purger/ORMPurgerExcludeTest.php | 100 + .../DataFixtures/Purger/ORMPurgerTest.php | 63 + .../DataFixtures/ReferenceRepositoryTest.php | 212 ++ .../Sorter/TopologicalSorterTest.php | 166 + .../Common/DataFixtures/Sorter/VertexTest.php | 26 + .../Common/DataFixtures/TestDocument/Role.php | 37 + .../Common/DataFixtures/TestEntity/Quoted.php | 31 + .../Common/DataFixtures/TestEntity/Role.php | 36 + .../Common/DataFixtures/TestEntity/User.php | 132 + .../TestEntity/UserWithSchema.php | 133 + .../DataFixtures/TestFixtures/MyFixture1.php | 13 + .../DataFixtures/TestFixtures/MyFixture2.php | 13 + .../DataFixtures/TestFixtures/NotAFixture.php | 7 + .../DataFixtures/TestFixtures/RoleFixture.php | 28 + .../DataFixtures/TestFixtures/UserFixture.php | 25 + .../TestPurgeEntity/ExcludedEntity.php | 25 + .../TestPurgeEntity/IncludedEntity.php | 25 + .../tests/Doctrine/Tests/Mock/Node.php | 27 + .../doctrine/event-manager/.scrutinizer.yml | 29 + vendor/doctrine/event-manager/LICENSE | 19 + vendor/doctrine/event-manager/README.md | 13 + vendor/doctrine/event-manager/composer.json | 41 + .../doctrine/event-manager/docs/en/index.rst | 22 + .../event-manager/docs/en/reference/index.rst | 133 + .../event-manager/docs/en/sidebar.rst | 4 + .../lib/Doctrine/Common/EventArgs.php | 46 + .../lib/Doctrine/Common/EventManager.php | 131 + .../lib/Doctrine/Common/EventSubscriber.php | 21 + vendor/doctrine/inflector/LICENSE | 19 + vendor/doctrine/inflector/README.md | 6 + vendor/doctrine/inflector/composer.json | 32 + .../Doctrine/Common/Inflector/Inflector.php | 490 +++ vendor/doctrine/lexer/LICENSE | 19 + vendor/doctrine/lexer/README.md | 5 + vendor/doctrine/lexer/composer.json | 24 + .../Doctrine/Common/Lexer/AbstractLexer.php | 327 ++ vendor/doctrine/persistence/.scrutinizer.yml | 29 + vendor/doctrine/persistence/LICENSE | 19 + vendor/doctrine/persistence/README.md | 13 + vendor/doctrine/persistence/composer.json | 47 + vendor/doctrine/persistence/docs/en/index.rst | 22 + .../persistence/docs/en/reference/index.rst | 375 +++ .../doctrine/persistence/docs/en/sidebar.rst | 4 + .../Persistence/AbstractManagerRegistry.php | 233 ++ .../Common/Persistence/ConnectionRegistry.php | 39 + .../Persistence/Event/LifecycleEventArgs.php | 60 + .../Event/LoadClassMetadataEventArgs.php | 45 + .../Persistence/Event/ManagerEventArgs.php | 30 + .../Persistence/Event/OnClearEventArgs.php | 58 + .../Persistence/Event/PreUpdateEventArgs.php | 112 + .../Common/Persistence/ManagerRegistry.php | 88 + .../Mapping/AbstractClassMetadataFactory.php | 413 +++ .../Persistence/Mapping/ClassMetadata.php | 152 + .../Mapping/ClassMetadataFactory.php | 54 + .../Mapping/Driver/AnnotationDriver.php | 248 ++ .../Mapping/Driver/DefaultFileLocator.php | 159 + .../Persistence/Mapping/Driver/FileDriver.php | 193 ++ .../Mapping/Driver/FileLocator.php | 53 + .../Mapping/Driver/MappingDriver.php | 37 + .../Mapping/Driver/MappingDriverChain.php | 142 + .../Persistence/Mapping/Driver/PHPDriver.php | 44 + .../Mapping/Driver/StaticPHPDriver.php | 125 + .../Mapping/Driver/SymfonyFileLocator.php | 227 ++ .../Persistence/Mapping/MappingException.php | 95 + .../Persistence/Mapping/ReflectionService.php | 68 + .../Mapping/RuntimeReflectionService.php | 87 + .../Mapping/StaticReflectionService.php | 69 + .../Common/Persistence/ObjectManager.php | 150 + .../Common/Persistence/ObjectManagerAware.php | 29 + .../Persistence/ObjectManagerDecorator.php | 116 + .../Common/Persistence/ObjectRepository.php | 59 + .../Common/Persistence/PersistentObject.php | 227 ++ .../lib/Doctrine/Common/Persistence/Proxy.php | 39 + vendor/doctrine/persistence/phpstan.neon | 10 + vendor/doctrine/reflection/.scrutinizer.yml | 29 + vendor/doctrine/reflection/LICENSE | 19 + vendor/doctrine/reflection/README.md | 13 + vendor/doctrine/reflection/composer.json | 44 + vendor/doctrine/reflection/docs/en/index.rst | 22 + .../reflection/docs/en/reference/index.rst | 85 + .../doctrine/reflection/docs/en/sidebar.rst | 4 + .../Reflection/ClassFinderInterface.php | 18 + .../Common/Reflection/Psr0FindFile.php | 70 + .../ReflectionProviderInterface.php | 31 + .../RuntimePublicReflectionProperty.php | 57 + .../Reflection/StaticReflectionClass.php | 413 +++ .../Reflection/StaticReflectionMethod.php | 344 ++ .../Reflection/StaticReflectionParser.php | 328 ++ .../Reflection/StaticReflectionProperty.php | 160 + vendor/doctrine/reflection/phpstan.neon | 6 + .../gedmo/doctrine-extensions/.gitattributes | 1 + vendor/gedmo/doctrine-extensions/.gitignore | 8 + vendor/gedmo/doctrine-extensions/.travis.yml | 48 + vendor/gedmo/doctrine-extensions/LICENSE | 21 + vendor/gedmo/doctrine-extensions/README.md | 136 + .../gedmo/doctrine-extensions/composer.json | 70 + .../gedmo/doctrine-extensions/composer7.json | 76 + .../doctrine-extensions/doc/annotations.md | 566 ++++ .../doctrine-extensions/doc/blameable.md | 644 ++++ .../doctrine-extensions/doc/ip_traceable.md | 660 ++++ .../gedmo/doctrine-extensions/doc/loggable.md | 266 ++ .../gedmo/doctrine-extensions/doc/mapping.md | 491 +++ .../doc/reference_integrity.md | 171 + .../doctrine-extensions/doc/references.md | 219 ++ .../doctrine-extensions/doc/sluggable.md | 810 +++++ .../doctrine-extensions/doc/softdeleteable.md | 286 ++ .../gedmo/doctrine-extensions/doc/sortable.md | 328 ++ .../gedmo/doctrine-extensions/doc/symfony2.md | 500 +++ .../doctrine-extensions/doc/timestampable.md | 674 ++++ .../doc/transaction-safety.md | 234 ++ .../doctrine-extensions/doc/translatable.md | 894 +++++ vendor/gedmo/doctrine-extensions/doc/tree.md | 1339 ++++++++ .../doctrine-extensions/doc/uploadable.md | 467 +++ .../doctrine-extensions/doc/zendframework2.md | 97 + .../example/app/Entity/Category.php | 219 ++ .../app/Entity/CategoryTranslation.php | 37 + .../Entity/Repository/CategoryRepository.php | 9 + .../doctrine-extensions/example/bin/console | 6 + .../example/bin/console.php | 38 + .../gedmo/doctrine-extensions/example/em.php | 122 + .../gedmo/doctrine-extensions/example/run.php | 79 + .../lib/Gedmo/AbstractTrackingListener.php | 227 ++ .../lib/Gedmo/Blameable/Blameable.php | 50 + .../lib/Gedmo/Blameable/BlameableListener.php | 72 + .../Blameable/Mapping/Driver/Annotation.php | 86 + .../Gedmo/Blameable/Mapping/Driver/Xml.php | 129 + .../Gedmo/Blameable/Mapping/Driver/Yaml.php | 129 + .../Blameable/Mapping/Event/Adapter/ODM.php | 17 + .../Blameable/Mapping/Event/Adapter/ORM.php | 17 + .../Mapping/Event/BlameableAdapter.php | 16 + .../lib/Gedmo/Blameable/Traits/Blameable.php | 68 + .../Blameable/Traits/BlameableDocument.php | 75 + .../Blameable/Traits/BlameableEntity.php | 75 + .../lib/Gedmo/DoctrineExtensions.php | 117 + .../lib/Gedmo/Exception.php | 19 + .../Exception/BadMethodCallException.php | 17 + .../FeatureNotImplementedException.php | 18 + .../Exception/InvalidArgumentException.php | 17 + .../Exception/InvalidMappingException.php | 20 + .../ReferenceIntegrityStrictException.php | 15 + .../lib/Gedmo/Exception/RuntimeException.php | 17 + .../Gedmo/Exception/TreeLockingException.php | 16 + .../Exception/UnexpectedValueException.php | 17 + .../UnsupportedObjectManagerException.php | 17 + .../UploadableCantWriteException.php | 18 + ...ploadableCouldntGuessMimeTypeException.php | 18 + .../UploadableDirectoryNotFoundException.php | 18 + .../Gedmo/Exception/UploadableException.php | 18 + .../UploadableExtensionException.php | 18 + .../UploadableFileAlreadyExistsException.php | 18 + .../UploadableFileNotReadableException.php | 18 + .../Exception/UploadableFormSizeException.php | 18 + .../Exception/UploadableIniSizeException.php | 18 + .../UploadableInvalidFileException.php | 18 + .../UploadableInvalidMimeTypeException.php | 18 + .../UploadableInvalidPathException.php | 18 + .../Exception/UploadableMaxSizeException.php | 18 + .../Exception/UploadableNoFileException.php | 18 + .../UploadableNoPathDefinedException.php | 18 + .../Exception/UploadableNoTmpDirException.php | 18 + .../Exception/UploadablePartialException.php | 18 + .../Exception/UploadableUploadException.php | 18 + .../lib/Gedmo/IpTraceable/IpTraceable.php | 50 + .../Gedmo/IpTraceable/IpTraceableListener.php | 59 + .../IpTraceable/Mapping/Driver/Annotation.php | 77 + .../Gedmo/IpTraceable/Mapping/Driver/Xml.php | 133 + .../Gedmo/IpTraceable/Mapping/Driver/Yaml.php | 127 + .../IpTraceable/Mapping/Event/Adapter/ODM.php | 17 + .../IpTraceable/Mapping/Event/Adapter/ORM.php | 17 + .../Mapping/Event/IpTraceableAdapter.php | 16 + .../Gedmo/IpTraceable/Traits/IpTraceable.php | 68 + .../Traits/IpTraceableDocument.php | 75 + .../IpTraceable/Traits/IpTraceableEntity.php | 75 + .../lib/Gedmo/Loggable/Document/LogEntry.php | 25 + .../MappedSuperclass/AbstractLogEntry.php | 217 ++ .../Repository/LogEntryRepository.php | 161 + .../lib/Gedmo/Loggable/Entity/LogEntry.php | 27 + .../MappedSuperclass/AbstractLogEntry.php | 219 ++ .../Entity/Repository/LogEntryRepository.php | 164 + .../lib/Gedmo/Loggable/Loggable.php | 28 + .../lib/Gedmo/Loggable/LoggableListener.php | 324 ++ .../Loggable/Mapping/Driver/Annotation.php | 140 + .../lib/Gedmo/Loggable/Mapping/Driver/Xml.php | 104 + .../Gedmo/Loggable/Mapping/Driver/Yaml.php | 149 + .../Loggable/Mapping/Event/Adapter/ODM.php | 59 + .../Loggable/Mapping/Event/Adapter/ORM.php | 55 + .../Mapping/Event/LoggableAdapter.php | 39 + .../lib/Gedmo/Mapping/Annotation/All.php | 15 + .../Gedmo/Mapping/Annotation/Blameable.php | 24 + .../Gedmo/Mapping/Annotation/IpTraceable.php | 24 + .../lib/Gedmo/Mapping/Annotation/Language.php | 18 + .../lib/Gedmo/Mapping/Annotation/Locale.php | 18 + .../lib/Gedmo/Mapping/Annotation/Loggable.php | 20 + .../Gedmo/Mapping/Annotation/Reference.php | 22 + .../Mapping/Annotation/ReferenceIntegrity.php | 18 + .../Mapping/Annotation/ReferenceMany.php | 15 + .../Mapping/Annotation/ReferenceManyEmbed.php | 10 + .../Gedmo/Mapping/Annotation/ReferenceOne.php | 15 + .../lib/Gedmo/Mapping/Annotation/Slug.php | 38 + .../Gedmo/Mapping/Annotation/SlugHandler.php | 31 + .../Mapping/Annotation/SlugHandlerOption.php | 31 + .../Mapping/Annotation/SoftDeleteable.php | 26 + .../Mapping/Annotation/SortableGroup.php | 18 + .../Mapping/Annotation/SortablePosition.php | 18 + .../Mapping/Annotation/Timestampable.php | 24 + .../Gedmo/Mapping/Annotation/Translatable.php | 20 + .../Mapping/Annotation/TranslationEntity.php | 20 + .../lib/Gedmo/Mapping/Annotation/Tree.php | 29 + .../Gedmo/Mapping/Annotation/TreeClosure.php | 20 + .../lib/Gedmo/Mapping/Annotation/TreeLeft.php | 18 + .../Gedmo/Mapping/Annotation/TreeLevel.php | 18 + .../Gedmo/Mapping/Annotation/TreeLockTime.php | 19 + .../Gedmo/Mapping/Annotation/TreeParent.php | 18 + .../lib/Gedmo/Mapping/Annotation/TreePath.php | 27 + .../Gedmo/Mapping/Annotation/TreePathHash.php | 18 + .../Mapping/Annotation/TreePathSource.php | 19 + .../Gedmo/Mapping/Annotation/TreeRight.php | 18 + .../lib/Gedmo/Mapping/Annotation/TreeRoot.php | 20 + .../Gedmo/Mapping/Annotation/Uploadable.php | 46 + .../Annotation/UploadableFileMimeType.php | 19 + .../Mapping/Annotation/UploadableFileName.php | 18 + .../Mapping/Annotation/UploadableFilePath.php | 19 + .../Mapping/Annotation/UploadableFileSize.php | 19 + .../Gedmo/Mapping/Annotation/Versioned.php | 18 + .../lib/Gedmo/Mapping/Driver.php | 34 + .../Driver/AbstractAnnotationDriver.php | 111 + .../Driver/AnnotationDriverInterface.php | 28 + .../lib/Gedmo/Mapping/Driver/Chain.php | 103 + .../lib/Gedmo/Mapping/Driver/File.php | 132 + .../lib/Gedmo/Mapping/Driver/Xml.php | 106 + .../lib/Gedmo/Mapping/Event/Adapter/ODM.php | 184 ++ .../lib/Gedmo/Mapping/Event/Adapter/ORM.php | 181 ++ .../Gedmo/Mapping/Event/AdapterInterface.php | 152 + .../Mapping/ExtensionMetadataFactory.php | 180 ++ .../Gedmo/Mapping/MappedEventSubscriber.php | 283 ++ .../Mapping/Driver/Annotation.php | 77 + .../Mapping/Driver/Yaml.php | 83 + .../ReferenceIntegrity/Mapping/Validator.php | 38 + .../ReferenceIntegrity/ReferenceIntegrity.php | 47 + .../ReferenceIntegrityListener.php | 181 ++ .../lib/Gedmo/References/LazyCollection.php | 241 ++ .../References/Mapping/Driver/Annotation.php | 99 + .../Gedmo/References/Mapping/Driver/Xml.php | 109 + .../Gedmo/References/Mapping/Driver/Yaml.php | 77 + .../References/Mapping/Event/Adapter/ODM.php | 94 + .../References/Mapping/Event/Adapter/ORM.php | 119 + .../Mapping/Event/ReferencesAdapter.php | 48 + .../Gedmo/References/ReferencesListener.php | 209 ++ .../Handler/InversedRelativeSlugHandler.php | 132 + .../Sluggable/Handler/RelativeSlugHandler.php | 151 + .../Handler/SlugHandlerInterface.php | 78 + ...SlugHandlerWithUniqueCallbackInterface.php | 28 + .../Sluggable/Handler/TreeSlugHandler.php | 201 ++ .../Sluggable/Mapping/Driver/Annotation.php | 173 + .../Gedmo/Sluggable/Mapping/Driver/Xml.php | 147 + .../Gedmo/Sluggable/Mapping/Driver/Yaml.php | 155 + .../Sluggable/Mapping/Event/Adapter/ODM.php | 123 + .../Sluggable/Mapping/Event/Adapter/ORM.php | 117 + .../Mapping/Event/SluggableAdapter.php | 54 + .../lib/Gedmo/Sluggable/Sluggable.php | 41 + .../lib/Gedmo/Sluggable/SluggableListener.php | 569 ++++ .../lib/Gedmo/Sluggable/Util/Urlizer.php | 12 + .../Filter/ODM/SoftDeleteableFilter.php | 97 + .../Filter/SoftDeleteableFilter.php | 127 + .../Mapping/Driver/Annotation.php | 59 + .../SoftDeleteable/Mapping/Driver/Xml.php | 59 + .../SoftDeleteable/Mapping/Driver/Yaml.php | 76 + .../Mapping/Event/Adapter/ORM.php | 17 + .../Mapping/Event/SoftDeleteableAdapter.php | 16 + .../SoftDeleteable/Mapping/Validator.php | 52 + .../Exec/MultiTableDeleteExecutor.php | 48 + .../Query/TreeWalker/SoftDeleteableWalker.php | 141 + .../Gedmo/SoftDeleteable/SoftDeleteable.php | 27 + .../SoftDeleteable/SoftDeleteableListener.php | 119 + .../SoftDeleteable/Traits/SoftDeleteable.php | 51 + .../Traits/SoftDeleteableDocument.php | 54 + .../Traits/SoftDeleteableEntity.php | 54 + .../Entity/Repository/SortableRepository.php | 91 + .../Sortable/Mapping/Driver/Annotation.php | 88 + .../lib/Gedmo/Sortable/Mapping/Driver/Xml.php | 107 + .../Gedmo/Sortable/Mapping/Driver/Yaml.php | 108 + .../Sortable/Mapping/Event/Adapter/ODM.php | 64 + .../Sortable/Mapping/Event/Adapter/ORM.php | 104 + .../Mapping/Event/SortableAdapter.php | 16 + .../lib/Gedmo/Sortable/Sortable.php | 42 + .../lib/Gedmo/Sortable/SortableListener.php | 620 ++++ .../Mapping/Driver/Annotation.php | 87 + .../Timestampable/Mapping/Driver/Xml.php | 106 + .../Timestampable/Mapping/Driver/Yaml.php | 107 + .../Mapping/Event/Adapter/ODM.php | 36 + .../Mapping/Event/Adapter/ORM.php | 36 + .../Mapping/Event/TimestampableAdapter.php | 25 + .../lib/Gedmo/Timestampable/Timestampable.php | 50 + .../Timestampable/TimestampableListener.php | 36 + .../Timestampable/Traits/Timestampable.php | 68 + .../Traits/TimestampableDocument.php | 75 + .../Traits/TimestampableEntity.php | 75 + .../Gedmo/Tool/Logging/DBAL/QueryAnalyzer.php | 248 ++ .../Gedmo/Tool/Wrapper/AbstractWrapper.php | 100 + .../lib/Gedmo/Tool/Wrapper/EntityWrapper.php | 138 + .../Tool/Wrapper/MongoDocumentWrapper.php | 137 + .../lib/Gedmo/Tool/WrapperInterface.php | 87 + .../AbstractPersonalTranslation.php | 153 + .../MappedSuperclass/AbstractTranslation.php | 185 ++ .../Repository/TranslationRepository.php | 254 ++ .../Translatable/Document/Translation.php | 30 + .../AbstractPersonalTranslation.php | 155 + .../MappedSuperclass/AbstractTranslation.php | 187 ++ .../Repository/TranslationRepository.php | 249 ++ .../Gedmo/Translatable/Entity/Translation.php | 30 + .../Hydrator/ORM/ObjectHydrator.php | 78 + .../Hydrator/ORM/SimpleObjectHydrator.php | 78 + .../Mapping/Driver/Annotation.php | 114 + .../Gedmo/Translatable/Mapping/Driver/Xml.php | 105 + .../Translatable/Mapping/Driver/Yaml.php | 88 + .../Mapping/Event/Adapter/ODM.php | 205 ++ .../Mapping/Event/Adapter/ORM.php | 260 ++ .../Mapping/Event/TranslatableAdapter.php | 96 + .../Query/TreeWalker/TranslationWalker.php | 470 +++ .../lib/Gedmo/Translatable/Translatable.php | 34 + .../Translatable/TranslatableListener.php | 796 +++++ .../Gedmo/Translator/Document/Translation.php | 55 + .../Gedmo/Translator/Entity/Translation.php | 60 + .../lib/Gedmo/Translator/Translation.php | 101 + .../Gedmo/Translator/TranslationInterface.php | 70 + .../lib/Gedmo/Translator/TranslationProxy.php | 174 + .../Repository/AbstractTreeRepository.php | 199 ++ .../Repository/MaterializedPathRepository.php | 205 ++ .../MappedSuperclass/AbstractClosure.php | 117 + .../Repository/AbstractTreeRepository.php | 248 ++ .../Repository/ClosureTreeRepository.php | 605 ++++ .../Repository/MaterializedPathRepository.php | 286 ++ .../Repository/NestedTreeRepository.php | 1075 ++++++ .../Tree/Hydrator/ORM/TreeObjectHydrator.php | 265 ++ .../Gedmo/Tree/Mapping/Driver/Annotation.php | 252 ++ .../lib/Gedmo/Tree/Mapping/Driver/Xml.php | 267 ++ .../lib/Gedmo/Tree/Mapping/Driver/Yaml.php | 215 ++ .../Gedmo/Tree/Mapping/Event/Adapter/ODM.php | 18 + .../Gedmo/Tree/Mapping/Event/Adapter/ORM.php | 18 + .../Gedmo/Tree/Mapping/Event/TreeAdapter.php | 16 + .../lib/Gedmo/Tree/Mapping/Validator.php | 240 ++ .../lib/Gedmo/Tree/Node.php | 39 + .../lib/Gedmo/Tree/RepositoryInterface.php | 60 + .../lib/Gedmo/Tree/RepositoryUtils.php | 183 ++ .../Gedmo/Tree/RepositoryUtilsInterface.php | 74 + .../lib/Gedmo/Tree/Strategy.php | 151 + .../Strategy/AbstractMaterializedPath.php | 539 +++ .../Strategy/ODM/MongoDB/MaterializedPath.php | 97 + .../lib/Gedmo/Tree/Strategy/ORM/Closure.php | 469 +++ .../Tree/Strategy/ORM/MaterializedPath.php | 66 + .../lib/Gedmo/Tree/Strategy/ORM/Nested.php | 718 ++++ .../Gedmo/Tree/Traits/MaterializedPath.php | 124 + .../lib/Gedmo/Tree/Traits/NestedSet.php | 34 + .../lib/Gedmo/Tree/Traits/NestedSetEntity.php | 43 + .../Gedmo/Tree/Traits/NestedSetEntityUuid.php | 25 + .../lib/Gedmo/Tree/TreeListener.php | 285 ++ .../Event/UploadableBaseEventArgs.php | 134 + .../UploadablePostFileProcessEventArgs.php | 15 + .../UploadablePreFileProcessEventArgs.php | 15 + .../lib/Gedmo/Uploadable/Events.php | 34 + .../Uploadable/FileInfo/FileInfoArray.php | 62 + .../Uploadable/FileInfo/FileInfoInterface.php | 27 + .../FilenameGeneratorAlphanumeric.php | 25 + .../FilenameGeneratorInterface.php | 25 + .../FilenameGeneratorSha1.php | 22 + .../Uploadable/Mapping/Driver/Annotation.php | 102 + .../Gedmo/Uploadable/Mapping/Driver/Xml.php | 87 + .../Gedmo/Uploadable/Mapping/Driver/Yaml.php | 93 + .../Gedmo/Uploadable/Mapping/Validator.php | 242 ++ .../Uploadable/MimeType/MimeTypeGuesser.php | 41 + .../MimeType/MimeTypeGuesserInterface.php | 15 + .../MimeType/MimeTypesExtensionsMap.php | 722 +++++ .../lib/Gedmo/Uploadable/Uploadable.php | 27 + .../Gedmo/Uploadable/UploadableListener.php | 779 +++++ .../orm/doctrine-extensions-mapping-2-1.xsd | 166 + .../orm/doctrine-extensions-mapping-2-2.xsd | 182 ++ vendor/monolog/monolog/.php_cs | 59 + vendor/monolog/monolog/CHANGELOG.md | 342 ++ vendor/monolog/monolog/LICENSE | 19 + vendor/monolog/monolog/README.md | 95 + vendor/monolog/monolog/composer.json | 66 + vendor/monolog/monolog/doc/01-usage.md | 231 ++ .../doc/02-handlers-formatters-processors.md | 157 + vendor/monolog/monolog/doc/03-utilities.md | 13 + vendor/monolog/monolog/doc/04-extending.md | 76 + vendor/monolog/monolog/doc/sockets.md | 39 + vendor/monolog/monolog/phpunit.xml.dist | 19 + .../monolog/src/Monolog/ErrorHandler.php | 230 ++ .../Monolog/Formatter/ChromePHPFormatter.php | 78 + .../Monolog/Formatter/ElasticaFormatter.php | 89 + .../Monolog/Formatter/FlowdockFormatter.php | 116 + .../Monolog/Formatter/FluentdFormatter.php | 85 + .../Monolog/Formatter/FormatterInterface.php | 36 + .../Formatter/GelfMessageFormatter.php | 138 + .../src/Monolog/Formatter/HtmlFormatter.php | 141 + .../src/Monolog/Formatter/JsonFormatter.php | 208 ++ .../src/Monolog/Formatter/LineFormatter.php | 179 + .../src/Monolog/Formatter/LogglyFormatter.php | 47 + .../Monolog/Formatter/LogstashFormatter.php | 166 + .../Monolog/Formatter/MongoDBFormatter.php | 105 + .../Monolog/Formatter/NormalizerFormatter.php | 297 ++ .../src/Monolog/Formatter/ScalarFormatter.php | 48 + .../Monolog/Formatter/WildfireFormatter.php | 113 + .../src/Monolog/Handler/AbstractHandler.php | 186 ++ .../Handler/AbstractProcessingHandler.php | 66 + .../Monolog/Handler/AbstractSyslogHandler.php | 101 + .../src/Monolog/Handler/AmqpHandler.php | 148 + .../Monolog/Handler/BrowserConsoleHandler.php | 230 ++ .../src/Monolog/Handler/BufferHandler.php | 117 + .../src/Monolog/Handler/ChromePHPHandler.php | 211 ++ .../src/Monolog/Handler/CouchDBHandler.php | 72 + .../src/Monolog/Handler/CubeHandler.php | 151 + .../monolog/src/Monolog/Handler/Curl/Util.php | 57 + .../Monolog/Handler/DeduplicationHandler.php | 169 + .../Handler/DoctrineCouchDBHandler.php | 45 + .../src/Monolog/Handler/DynamoDbHandler.php | 107 + .../Monolog/Handler/ElasticSearchHandler.php | 128 + .../src/Monolog/Handler/ErrorLogHandler.php | 82 + .../src/Monolog/Handler/FilterHandler.php | 140 + .../ActivationStrategyInterface.php | 28 + .../ChannelLevelActivationStrategy.php | 59 + .../ErrorLevelActivationStrategy.php | 34 + .../Monolog/Handler/FingersCrossedHandler.php | 163 + .../src/Monolog/Handler/FirePHPHandler.php | 195 ++ .../src/Monolog/Handler/FleepHookHandler.php | 126 + .../src/Monolog/Handler/FlowdockHandler.php | 127 + .../src/Monolog/Handler/GelfHandler.php | 73 + .../src/Monolog/Handler/GroupHandler.php | 104 + .../src/Monolog/Handler/HandlerInterface.php | 90 + .../src/Monolog/Handler/HandlerWrapper.php | 108 + .../src/Monolog/Handler/HipChatHandler.php | 350 ++ .../src/Monolog/Handler/IFTTTHandler.php | 69 + .../src/Monolog/Handler/LogEntriesHandler.php | 55 + .../src/Monolog/Handler/LogglyHandler.php | 102 + .../src/Monolog/Handler/MailHandler.php | 67 + .../src/Monolog/Handler/MandrillHandler.php | 68 + .../Handler/MissingExtensionException.php | 21 + .../src/Monolog/Handler/MongoDBHandler.php | 59 + .../Monolog/Handler/NativeMailerHandler.php | 185 ++ .../src/Monolog/Handler/NewRelicHandler.php | 202 ++ .../src/Monolog/Handler/NullHandler.php | 45 + .../src/Monolog/Handler/PHPConsoleHandler.php | 242 ++ .../src/Monolog/Handler/PsrHandler.php | 56 + .../src/Monolog/Handler/PushoverHandler.php | 185 ++ .../src/Monolog/Handler/RavenHandler.php | 232 ++ .../src/Monolog/Handler/RedisHandler.php | 97 + .../src/Monolog/Handler/RollbarHandler.php | 132 + .../Monolog/Handler/RotatingFileHandler.php | 178 + .../src/Monolog/Handler/SamplingHandler.php | 82 + .../src/Monolog/Handler/Slack/SlackRecord.php | 294 ++ .../src/Monolog/Handler/SlackHandler.php | 215 ++ .../Monolog/Handler/SlackWebhookHandler.php | 115 + .../src/Monolog/Handler/SlackbotHandler.php | 80 + .../src/Monolog/Handler/SocketHandler.php | 346 ++ .../src/Monolog/Handler/StreamHandler.php | 176 + .../Monolog/Handler/SwiftMailerHandler.php | 99 + .../src/Monolog/Handler/SyslogHandler.php | 67 + .../Monolog/Handler/SyslogUdp/UdpSocket.php | 56 + .../src/Monolog/Handler/SyslogUdpHandler.php | 103 + .../src/Monolog/Handler/TestHandler.php | 154 + .../Handler/WhatFailureGroupHandler.php | 61 + .../Monolog/Handler/ZendMonitorHandler.php | 95 + vendor/monolog/monolog/src/Monolog/Logger.php | 700 ++++ .../src/Monolog/Processor/GitProcessor.php | 64 + .../Processor/IntrospectionProcessor.php | 112 + .../Processor/MemoryPeakUsageProcessor.php | 35 + .../src/Monolog/Processor/MemoryProcessor.php | 63 + .../Processor/MemoryUsageProcessor.php | 35 + .../Monolog/Processor/MercurialProcessor.php | 63 + .../Monolog/Processor/ProcessIdProcessor.php | 31 + .../Processor/PsrLogMessageProcessor.php | 48 + .../src/Monolog/Processor/TagProcessor.php | 44 + .../src/Monolog/Processor/UidProcessor.php | 46 + .../src/Monolog/Processor/WebProcessor.php | 113 + .../monolog/monolog/src/Monolog/Registry.php | 134 + .../tests/Monolog/ErrorHandlerTest.php | 31 + .../Formatter/ChromePHPFormatterTest.php | 158 + .../Formatter/ElasticaFormatterTest.php | 79 + .../Formatter/FlowdockFormatterTest.php | 55 + .../Formatter/FluentdFormatterTest.php | 62 + .../Formatter/GelfMessageFormatterTest.php | 258 ++ .../Monolog/Formatter/JsonFormatterTest.php | 183 ++ .../Monolog/Formatter/LineFormatterTest.php | 222 ++ .../Monolog/Formatter/LogglyFormatterTest.php | 40 + .../Formatter/LogstashFormatterTest.php | 333 ++ .../Formatter/MongoDBFormatterTest.php | 262 ++ .../Formatter/NormalizerFormatterTest.php | 423 +++ .../Monolog/Formatter/ScalarFormatterTest.php | 110 + .../Formatter/WildfireFormatterTest.php | 142 + .../Monolog/Handler/AbstractHandlerTest.php | 115 + .../Handler/AbstractProcessingHandlerTest.php | 80 + .../tests/Monolog/Handler/AmqpHandlerTest.php | 136 + .../Handler/BrowserConsoleHandlerTest.php | 130 + .../Monolog/Handler/BufferHandlerTest.php | 158 + .../Monolog/Handler/ChromePHPHandlerTest.php | 156 + .../Monolog/Handler/CouchDBHandlerTest.php | 31 + .../Handler/DeduplicationHandlerTest.php | 165 + .../Handler/DoctrineCouchDBHandlerTest.php | 52 + .../Monolog/Handler/DynamoDbHandlerTest.php | 82 + .../Handler/ElasticSearchHandlerTest.php | 239 ++ .../Monolog/Handler/ErrorLogHandlerTest.php | 66 + .../Monolog/Handler/FilterHandlerTest.php | 170 + .../Handler/FingersCrossedHandlerTest.php | 279 ++ .../Monolog/Handler/FirePHPHandlerTest.php | 96 + .../tests/Monolog/Handler/Fixtures/.gitkeep | 0 .../Monolog/Handler/FleepHookHandlerTest.php | 85 + .../Monolog/Handler/FlowdockHandlerTest.php | 88 + .../Monolog/Handler/GelfHandlerLegacyTest.php | 95 + .../tests/Monolog/Handler/GelfHandlerTest.php | 117 + .../Handler/GelfMockMessagePublisher.php | 25 + .../Monolog/Handler/GroupHandlerTest.php | 112 + .../Monolog/Handler/HandlerWrapperTest.php | 130 + .../Monolog/Handler/HipChatHandlerTest.php | 279 ++ .../Monolog/Handler/LogEntriesHandlerTest.php | 84 + .../tests/Monolog/Handler/MailHandlerTest.php | 75 + .../tests/Monolog/Handler/MockRavenClient.php | 27 + .../Monolog/Handler/MongoDBHandlerTest.php | 65 + .../Handler/NativeMailerHandlerTest.php | 111 + .../Monolog/Handler/NewRelicHandlerTest.php | 200 ++ .../tests/Monolog/Handler/NullHandlerTest.php | 33 + .../Monolog/Handler/PHPConsoleHandlerTest.php | 273 ++ .../tests/Monolog/Handler/PsrHandlerTest.php | 50 + .../Monolog/Handler/PushoverHandlerTest.php | 141 + .../Monolog/Handler/RavenHandlerTest.php | 255 ++ .../Monolog/Handler/RedisHandlerTest.php | 127 + .../Monolog/Handler/RollbarHandlerTest.php | 84 + .../Handler/RotatingFileHandlerTest.php | 211 ++ .../Monolog/Handler/SamplingHandlerTest.php | 33 + .../Monolog/Handler/Slack/SlackRecordTest.php | 387 +++ .../Monolog/Handler/SlackHandlerTest.php | 155 + .../Handler/SlackWebhookHandlerTest.php | 107 + .../Monolog/Handler/SlackbotHandlerTest.php | 47 + .../Monolog/Handler/SocketHandlerTest.php | 309 ++ .../Monolog/Handler/StreamHandlerTest.php | 184 ++ .../Handler/SwiftMailerHandlerTest.php | 113 + .../Monolog/Handler/SyslogHandlerTest.php | 44 + .../Monolog/Handler/SyslogUdpHandlerTest.php | 76 + .../tests/Monolog/Handler/TestHandlerTest.php | 70 + .../tests/Monolog/Handler/UdpSocketTest.php | 64 + .../Handler/WhatFailureGroupHandlerTest.php | 121 + .../Handler/ZendMonitorHandlerTest.php | 69 + .../monolog/tests/Monolog/LoggerTest.php | 548 ++++ .../Monolog/Processor/GitProcessorTest.php | 29 + .../Processor/IntrospectionProcessorTest.php | 123 + .../MemoryPeakUsageProcessorTest.php | 42 + .../Processor/MemoryUsageProcessorTest.php | 42 + .../Processor/MercurialProcessorTest.php | 41 + .../Processor/ProcessIdProcessorTest.php | 30 + .../Processor/PsrLogMessageProcessorTest.php | 43 + .../Monolog/Processor/TagProcessorTest.php | 49 + .../Monolog/Processor/UidProcessorTest.php | 33 + .../Monolog/Processor/WebProcessorTest.php | 113 + .../tests/Monolog/PsrLogCompatTest.php | 47 + .../monolog/tests/Monolog/RegistryTest.php | 153 + .../monolog/tests/Monolog/TestCase.php | 58 + vendor/pagerfanta/pagerfanta/CHANGELOG.md | 17 + vendor/pagerfanta/pagerfanta/LICENSE | 20 + vendor/pagerfanta/pagerfanta/README.md | 610 ++++ .../pagerfanta/pagerfanta/code_of_conduct.md | 1 + vendor/pagerfanta/pagerfanta/composer.json | 46 + .../Pagerfanta/Adapter/AdapterInterface.php | 37 + .../src/Pagerfanta/Adapter/ArrayAdapter.php | 58 + .../Pagerfanta/Adapter/CallbackAdapter.php | 56 + .../Adapter/ConcatenationAdapter.php | 126 + .../Adapter/DoctrineCollectionAdapter.php | 60 + .../Adapter/DoctrineDbalAdapter.php | 77 + .../DoctrineDbalSingleTableAdapter.php | 70 + .../Adapter/DoctrineODMMongoDBAdapter.php | 64 + .../Adapter/DoctrineODMPhpcrAdapter.php | 65 + .../Pagerfanta/Adapter/DoctrineORMAdapter.php | 81 + .../Adapter/DoctrineSelectableAdapter.php | 80 + .../Pagerfanta/Adapter/ElasticaAdapter.php | 101 + .../src/Pagerfanta/Adapter/FixedAdapter.php | 52 + .../Pagerfanta/Adapter/MandangoAdapter.php | 60 + .../src/Pagerfanta/Adapter/MongoAdapter.php | 61 + .../src/Pagerfanta/Adapter/NullAdapter.php | 79 + .../src/Pagerfanta/Adapter/Propel2Adapter.php | 72 + .../src/Pagerfanta/Adapter/PropelAdapter.php | 68 + .../Pagerfanta/Adapter/SolariumAdapter.php | 172 + .../src/Pagerfanta/Exception/Exception.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../LessThan1CurrentPageException.php | 21 + .../LessThan1MaxPerPageException.php | 21 + .../Pagerfanta/Exception/LogicException.php | 21 + .../Exception/NotBooleanException.php | 19 + .../NotIntegerCurrentPageException.php | 21 + .../Exception/NotIntegerException.php | 21 + .../NotIntegerMaxPerPageException.php | 21 + .../NotValidCurrentPageException.php | 21 + .../Exception/NotValidMaxPerPageException.php | 21 + .../OutOfRangeCurrentPageException.php | 21 + .../pagerfanta/src/Pagerfanta/Pagerfanta.php | 527 +++ .../src/Pagerfanta/PagerfantaInterface.php | 19 + .../src/Pagerfanta/View/DefaultView.php | 301 ++ .../src/Pagerfanta/View/OptionableView.php | 55 + .../src/Pagerfanta/View/SemanticUiView.php | 44 + .../View/Template/DefaultTemplate.php | 102 + .../View/Template/SemanticUiTemplate.php | 142 + .../src/Pagerfanta/View/Template/Template.php | 66 + .../View/Template/TemplateInterface.php | 110 + .../Template/TwitterBootstrap3Template.php | 32 + .../Template/TwitterBootstrap4Template.php | 33 + .../Template/TwitterBootstrapTemplate.php | 146 + .../Pagerfanta/View/TwitterBootstrap3View.php | 38 + .../Pagerfanta/View/TwitterBootstrap4View.php | 38 + .../Pagerfanta/View/TwitterBootstrapView.php | 45 + .../src/Pagerfanta/View/ViewFactory.php | 98 + .../Pagerfanta/View/ViewFactoryInterface.php | 76 + .../src/Pagerfanta/View/ViewInterface.php | 42 + vendor/paragonie/random_compat/LICENSE | 22 + vendor/paragonie/random_compat/build-phar.sh | 5 + vendor/paragonie/random_compat/composer.json | 34 + .../dist/random_compat.phar.pubkey | 5 + .../dist/random_compat.phar.pubkey.asc | 11 + vendor/paragonie/random_compat/lib/random.php | 32 + .../random_compat/other/build_phar.php | 57 + .../random_compat/psalm-autoload.php | 9 + vendor/paragonie/random_compat/psalm.xml | 19 + vendor/psr/cache/CHANGELOG.md | 16 + vendor/psr/cache/LICENSE.txt | 19 + vendor/psr/cache/README.md | 9 + vendor/psr/cache/composer.json | 25 + vendor/psr/cache/src/CacheException.php | 10 + vendor/psr/cache/src/CacheItemInterface.php | 105 + .../psr/cache/src/CacheItemPoolInterface.php | 138 + .../cache/src/InvalidArgumentException.php | 13 + vendor/psr/container/.gitignore | 3 + vendor/psr/container/LICENSE | 21 + vendor/psr/container/README.md | 5 + vendor/psr/container/composer.json | 27 + .../src/ContainerExceptionInterface.php | 13 + .../psr/container/src/ContainerInterface.php | 37 + .../src/NotFoundExceptionInterface.php | 13 + vendor/psr/log/.gitignore | 1 + vendor/psr/log/LICENSE | 19 + vendor/psr/log/Psr/Log/AbstractLogger.php | 128 + .../log/Psr/Log/InvalidArgumentException.php | 7 + vendor/psr/log/Psr/Log/LogLevel.php | 18 + .../psr/log/Psr/Log/LoggerAwareInterface.php | 18 + vendor/psr/log/Psr/Log/LoggerAwareTrait.php | 26 + vendor/psr/log/Psr/Log/LoggerInterface.php | 123 + vendor/psr/log/Psr/Log/LoggerTrait.php | 140 + vendor/psr/log/Psr/Log/NullLogger.php | 28 + .../log/Psr/Log/Test/LoggerInterfaceTest.php | 140 + vendor/psr/log/README.md | 45 + vendor/psr/log/composer.json | 26 + vendor/psr/simple-cache/.editorconfig | 12 + vendor/psr/simple-cache/LICENSE.md | 21 + vendor/psr/simple-cache/README.md | 8 + vendor/psr/simple-cache/composer.json | 25 + .../psr/simple-cache/src/CacheException.php | 10 + .../psr/simple-cache/src/CacheInterface.php | 114 + .../src/InvalidArgumentException.php | 13 + vendor/sylius/fixtures-bundle/.gitignore | 15 + .../Command/FixturesListCommand.php | 87 + .../Command/FixturesLoadCommand.php | 87 + .../Compiler/FixtureRegistryPass.php | 38 + .../Compiler/ListenerRegistryPass.php | 38 + .../DependencyInjection/Configuration.php | 139 + .../SyliusFixturesExtension.php | 70 + .../Fixture/AbstractFixture.php | 41 + .../Fixture/FixtureInterface.php | 29 + .../Fixture/FixtureNotFoundException.php | 26 + .../Fixture/FixtureRegistry.php | 54 + .../Fixture/FixtureRegistryInterface.php | 31 + vendor/sylius/fixtures-bundle/LICENSE | 19 + .../Listener/AbstractListener.php | 41 + .../AfterFixtureListenerInterface.php | 23 + .../Listener/AfterSuiteListenerInterface.php | 23 + .../BeforeFixtureListenerInterface.php | 23 + .../Listener/BeforeSuiteListenerInterface.php | 23 + .../fixtures-bundle/Listener/FixtureEvent.php | 71 + .../Listener/ListenerInterface.php | 24 + .../Listener/ListenerNotFoundException.php | 26 + .../Listener/ListenerRegistry.php | 54 + .../Listener/ListenerRegistryInterface.php | 31 + .../Listener/LoggerListener.php | 56 + .../Listener/MongoDBPurgerListener.php | 70 + .../Listener/ORMPurgerListener.php | 92 + .../Listener/PHPCRPurgerListener.php | 70 + .../fixtures-bundle/Listener/SuiteEvent.php | 40 + .../fixtures-bundle/Loader/FixtureLoader.php | 28 + .../Loader/FixtureLoaderInterface.php | 27 + .../Loader/HookableFixtureLoader.php | 80 + .../Loader/HookableSuiteLoader.php | 79 + .../fixtures-bundle/Loader/SuiteLoader.php | 45 + .../Loader/SuiteLoaderInterface.php | 24 + vendor/sylius/fixtures-bundle/README.md | 47 + .../Resources/config/services.xml | 32 + .../Resources/config/services/fixture.xml | 20 + .../integrations/doctrine/mongodb-odm.xml | 23 + .../services/integrations/doctrine/orm.xml | 23 + .../integrations/doctrine/phpcr-odm.xml | 23 + .../Resources/config/services/listener.xml | 25 + .../Resources/config/services/loader.xml | 32 + .../Resources/config/services/logger.xml | 44 + .../Resources/config/services/suite.xml | 30 + .../Suite/LazySuiteRegistry.php | 78 + vendor/sylius/fixtures-bundle/Suite/Suite.php | 96 + .../fixtures-bundle/Suite/SuiteFactory.php | 106 + .../Suite/SuiteFactoryInterface.php | 25 + .../fixtures-bundle/Suite/SuiteInterface.php | 37 + .../Suite/SuiteNotFoundException.php | 26 + .../Suite/SuiteRegistryInterface.php | 31 + .../fixtures-bundle/SyliusFixturesBundle.php | 33 + .../Compiler/FixtureRegistryPassTest.php | 48 + .../Compiler/ListenerRegistryPassTest.php | 48 + .../DependencyInjection/ConfigurationTest.php | 338 ++ .../SyliusFixturesExtensionTest.php | 52 + .../Listener/MongoDBPurgerListenerTest.php | 49 + .../Tests/Listener/ORMPurgerListenerTest.php | 73 + .../Listener/PHPCRPurgerListenerTest.php | 49 + vendor/sylius/fixtures-bundle/composer.json | 73 + .../sylius/fixtures-bundle/phpspec.yml.dist | 5 + .../sylius/fixtures-bundle/phpunit.xml.dist | 17 + .../Fixture/FixtureNotFoundExceptionSpec.php | 34 + .../spec/Fixture/FixtureRegistrySpec.php | 59 + .../ListenerNotFoundExceptionSpec.php | 34 + .../spec/Listener/ListenerRegistrySpec.php | 59 + .../spec/Listener/LoggerListenerSpec.php | 70 + .../spec/Loader/FixtureLoaderSpec.php | 34 + .../spec/Loader/HookableFixtureLoaderSpec.php | 104 + .../spec/Loader/HookableSuiteLoaderSpec.php | 95 + .../spec/Loader/SuiteLoaderSpec.php | 50 + .../spec/Suite/LazySuiteRegistrySpec.php | 63 + .../spec/Suite/SuiteFactorySpec.php | 223 ++ .../spec/Suite/SuiteNotFoundExceptionSpec.php | 34 + .../fixtures-bundle/spec/Suite/SuiteSpec.php | 88 + .../fixtures-bundle/test/app/AppKernel.php | 60 + .../test/app/config/config.yml | 32 + .../test/app/config/parameters.yml | 6 + .../sylius/fixtures-bundle/test/app/console | 24 + .../sylius/fixtures-bundle/test/composer.json | 3 + .../src/Tests/SyliusFixturesBundleTest.php | 41 + vendor/sylius/resource/.gitignore | 5 + .../Exception/DeleteHandlingException.php | 63 + .../Exception/RaceConditionException.php | 31 + .../Exception/UnexpectedTypeException.php | 30 + .../Exception/UnsupportedMethodException.php | 28 + .../Exception/UpdateHandlingException.php | 63 + vendor/sylius/resource/Factory/Factory.php | 41 + .../resource/Factory/FactoryInterface.php | 22 + .../resource/Factory/TranslatableFactory.php | 60 + .../Factory/TranslatableFactoryInterface.php | 18 + .../Generator/RandomnessGenerator.php | 82 + .../RandomnessGeneratorInterface.php | 39 + vendor/sylius/resource/LICENSE | 19 + vendor/sylius/resource/Metadata/Metadata.php | 207 ++ .../resource/Metadata/MetadataInterface.php | 105 + vendor/sylius/resource/Metadata/Registry.php | 72 + .../resource/Metadata/RegistryInterface.php | 54 + .../resource/Model/AbstractTranslation.php | 72 + .../resource/Model/ArchivableInterface.php | 27 + .../sylius/resource/Model/ArchivableTrait.php | 41 + .../resource/Model/CodeAwareInterface.php | 27 + .../resource/Model/ResourceInterface.php | 22 + .../resource/Model/ResourceLogEntry.php | 20 + .../resource/Model/SlugAwareInterface.php | 27 + .../resource/Model/TimestampableInterface.php | 37 + .../resource/Model/TimestampableTrait.php | 59 + .../resource/Model/ToggleableInterface.php | 33 + .../sylius/resource/Model/ToggleableTrait.php | 48 + .../resource/Model/TranslatableInterface.php | 58 + .../resource/Model/TranslatableTrait.php | 168 + .../resource/Model/TranslationInterface.php | 37 + .../resource/Model/VersionedInterface.php | 27 + vendor/sylius/resource/README.md | 47 + .../Exception/ExistingResourceException.php | 22 + .../Repository/InMemoryRepository.php | 228 ++ .../Repository/RepositoryInterface.php | 41 + vendor/sylius/resource/ResourceActions.php | 28 + .../resource/StateMachine/StateMachine.php | 49 + .../StateMachine/StateMachineInterface.php | 39 + .../resource/Storage/StorageInterface.php | 48 + .../ImmutableTranslationLocaleProvider.php | 53 + .../TranslationLocaleProviderInterface.php | 27 + .../TranslatableEntityLocaleAssigner.php | 44 + ...nslatableEntityLocaleAssignerInterface.php | 24 + vendor/sylius/resource/composer.json | 61 + vendor/sylius/resource/phpspec.yml.dist | 5 + .../Exception/DeleteHandlingExceptionSpec.php | 39 + .../Exception/RaceConditionExceptionSpec.php | 40 + .../Exception/UnexpectedTypeExceptionSpec.php | 34 + .../UnsupportedMethodExceptionSpec.php | 34 + .../Exception/UpdateHandlingExceptionSpec.php | 39 + .../resource/spec/Factory/FactorySpec.php | 35 + .../spec/Factory/TranslatableFactorySpec.php | 59 + .../Fixtures/SampleBookResourceInterface.php | 34 + .../Generator/RandomnessGeneratorSpec.php | 66 + .../resource/spec/Metadata/MetadataSpec.php | 138 + .../resource/spec/Metadata/RegistrySpec.php | 89 + .../spec/Model/AbstractTranslationSpec.php | 61 + .../ExistingResourceExceptionSpec.php | 29 + .../Repository/InMemoryRepositorySpec.php | 210 ++ ...ImmutableTranslationLocaleProviderSpec.php | 40 + .../TranslatableEntityLocaleAssignerSpec.php | 44 + vendor/symfony/cache/.gitignore | 3 + .../symfony/cache/Adapter/AbstractAdapter.php | 299 ++ .../cache/Adapter/AdapterInterface.php | 37 + vendor/symfony/cache/Adapter/ApcuAdapter.php | 27 + vendor/symfony/cache/Adapter/ArrayAdapter.php | 155 + vendor/symfony/cache/Adapter/ChainAdapter.php | 273 ++ .../symfony/cache/Adapter/DoctrineAdapter.php | 27 + .../cache/Adapter/FilesystemAdapter.php | 26 + .../cache/Adapter/MemcachedAdapter.php | 36 + vendor/symfony/cache/Adapter/NullAdapter.php | 121 + vendor/symfony/cache/Adapter/PdoAdapter.php | 50 + .../symfony/cache/Adapter/PhpArrayAdapter.php | 292 ++ .../symfony/cache/Adapter/PhpFilesAdapter.php | 37 + vendor/symfony/cache/Adapter/ProxyAdapter.php | 180 ++ vendor/symfony/cache/Adapter/RedisAdapter.php | 29 + .../cache/Adapter/SimpleCacheAdapter.php | 79 + .../symfony/cache/Adapter/TagAwareAdapter.php | 374 +++ .../Adapter/TagAwareAdapterInterface.php | 33 + .../cache/Adapter/TraceableAdapter.php | 233 ++ .../Adapter/TraceableTagAwareAdapter.php | 36 + vendor/symfony/cache/CHANGELOG.md | 36 + vendor/symfony/cache/CacheItem.php | 187 ++ .../DataCollector/CacheDataCollector.php | 183 ++ vendor/symfony/cache/DoctrineProvider.php | 102 + .../cache/Exception/CacheException.php | 19 + .../Exception/InvalidArgumentException.php | 19 + vendor/symfony/cache/LICENSE | 19 + vendor/symfony/cache/PruneableInterface.php | 23 + vendor/symfony/cache/README.md | 18 + vendor/symfony/cache/ResettableInterface.php | 20 + vendor/symfony/cache/Simple/AbstractCache.php | 181 ++ vendor/symfony/cache/Simple/ApcuCache.php | 24 + vendor/symfony/cache/Simple/ArrayCache.php | 148 + vendor/symfony/cache/Simple/ChainCache.php | 252 ++ vendor/symfony/cache/Simple/DoctrineCache.php | 27 + .../symfony/cache/Simple/FilesystemCache.php | 26 + .../symfony/cache/Simple/MemcachedCache.php | 26 + vendor/symfony/cache/Simple/NullCache.php | 86 + vendor/symfony/cache/Simple/PdoCache.php | 48 + vendor/symfony/cache/Simple/PhpArrayCache.php | 252 ++ vendor/symfony/cache/Simple/PhpFilesCache.php | 37 + vendor/symfony/cache/Simple/Psr6Cache.php | 228 ++ vendor/symfony/cache/Simple/RedisCache.php | 29 + .../symfony/cache/Simple/TraceableCache.php | 241 ++ .../Adapter/AbstractRedisAdapterTest.php | 46 + .../cache/Tests/Adapter/AdapterTestCase.php | 171 + .../cache/Tests/Adapter/ApcuAdapterTest.php | 124 + .../cache/Tests/Adapter/ArrayAdapterTest.php | 56 + .../cache/Tests/Adapter/ChainAdapterTest.php | 118 + .../Tests/Adapter/DoctrineAdapterTest.php | 32 + .../Tests/Adapter/FilesystemAdapterTest.php | 61 + .../Tests/Adapter/MaxIdLengthAdapterTest.php | 85 + .../Tests/Adapter/MemcachedAdapterTest.php | 195 ++ .../Adapter/NamespacedProxyAdapterTest.php | 26 + .../cache/Tests/Adapter/NullAdapterTest.php | 128 + .../cache/Tests/Adapter/PdoAdapterTest.php | 73 + .../Tests/Adapter/PdoDbalAdapterTest.php | 48 + .../Tests/Adapter/PhpArrayAdapterTest.php | 133 + .../PhpArrayAdapterWithFallbackTest.php | 49 + .../Tests/Adapter/PhpFilesAdapterTest.php | 47 + .../cache/Tests/Adapter/PredisAdapterTest.php | 53 + .../Adapter/PredisClusterAdapterTest.php | 26 + .../cache/Tests/Adapter/ProxyAdapterTest.php | 71 + .../cache/Tests/Adapter/RedisAdapterTest.php | 92 + .../Tests/Adapter/RedisArrayAdapterTest.php | 24 + .../Tests/Adapter/RedisClusterAdapterTest.php | 27 + .../Tests/Adapter/SimpleCacheAdapterTest.php | 30 + .../Tests/Adapter/TagAwareAdapterTest.php | 207 ++ .../Tests/Adapter/TraceableAdapterTest.php | 191 ++ .../Adapter/TraceableTagAwareAdapterTest.php | 37 + vendor/symfony/cache/Tests/CacheItemTest.php | 77 + .../cache/Tests/DoctrineProviderTest.php | 45 + .../cache/Tests/Fixtures/ArrayCache.php | 52 + .../cache/Tests/Fixtures/ExternalAdapter.php | 76 + .../Tests/Simple/AbstractRedisCacheTest.php | 46 + .../cache/Tests/Simple/ApcuCacheTest.php | 35 + .../cache/Tests/Simple/ArrayCacheTest.php | 25 + .../cache/Tests/Simple/CacheTestCase.php | 146 + .../cache/Tests/Simple/ChainCacheTest.php | 116 + .../cache/Tests/Simple/DoctrineCacheTest.php | 31 + .../Tests/Simple/FilesystemCacheTest.php | 34 + .../cache/Tests/Simple/MemcachedCacheTest.php | 174 + .../Simple/MemcachedCacheTextModeTest.php | 25 + .../cache/Tests/Simple/NullCacheTest.php | 96 + .../cache/Tests/Simple/PdoCacheTest.php | 47 + .../cache/Tests/Simple/PdoDbalCacheTest.php | 48 + .../cache/Tests/Simple/PhpArrayCacheTest.php | 143 + .../Simple/PhpArrayCacheWithFallbackTest.php | 55 + .../cache/Tests/Simple/PhpFilesCacheTest.php | 42 + .../cache/Tests/Simple/Psr6CacheTest.php | 30 + .../Tests/Simple/RedisArrayCacheTest.php | 24 + .../cache/Tests/Simple/RedisCacheTest.php | 82 + .../Tests/Simple/RedisClusterCacheTest.php | 27 + .../cache/Tests/Simple/TraceableCacheTest.php | 171 + .../cache/Tests/Traits/PdoPruneableTrait.php | 34 + vendor/symfony/cache/Traits/AbstractTrait.php | 268 ++ vendor/symfony/cache/Traits/ApcuTrait.php | 115 + vendor/symfony/cache/Traits/ArrayTrait.php | 108 + vendor/symfony/cache/Traits/DoctrineTrait.php | 98 + .../cache/Traits/FilesystemCommonTrait.php | 128 + .../symfony/cache/Traits/FilesystemTrait.php | 107 + .../symfony/cache/Traits/MemcachedTrait.php | 296 ++ vendor/symfony/cache/Traits/PdoTrait.php | 404 +++ vendor/symfony/cache/Traits/PhpArrayTrait.php | 142 + vendor/symfony/cache/Traits/PhpFilesTrait.php | 158 + vendor/symfony/cache/Traits/ProxyTrait.php | 43 + vendor/symfony/cache/Traits/RedisProxy.php | 65 + vendor/symfony/cache/Traits/RedisTrait.php | 359 ++ vendor/symfony/cache/composer.json | 49 + vendor/symfony/cache/phpunit.xml.dist | 50 + vendor/symfony/class-loader/.gitignore | 3 + .../symfony/class-loader/ApcClassLoader.php | 143 + vendor/symfony/class-loader/CHANGELOG.md | 41 + .../class-loader/ClassCollectionLoader.php | 448 +++ vendor/symfony/class-loader/ClassLoader.php | 207 ++ .../class-loader/ClassMapGenerator.php | 160 + vendor/symfony/class-loader/LICENSE | 19 + .../symfony/class-loader/MapClassLoader.php | 70 + .../symfony/class-loader/Psr4ClassLoader.php | 94 + vendor/symfony/class-loader/README.md | 14 + .../class-loader/Tests/ApcClassLoaderTest.php | 200 ++ .../Tests/ClassCollectionLoaderTest.php | 319 ++ .../class-loader/Tests/ClassLoaderTest.php | 238 ++ .../Tests/ClassMapGeneratorTest.php | 151 + .../Tests/Fixtures/Apc/Namespaced/Bar.php | 17 + .../Tests/Fixtures/Apc/Namespaced/Baz.php | 17 + .../Tests/Fixtures/Apc/Namespaced/Foo.php | 17 + .../Tests/Fixtures/Apc/Namespaced/FooBar.php | 17 + .../Tests/Fixtures/Apc/Pearlike/Bar.php | 6 + .../Tests/Fixtures/Apc/Pearlike/Baz.php | 6 + .../Tests/Fixtures/Apc/Pearlike/Foo.php | 6 + .../alpha/Apc/ApcPrefixCollision/A/Bar.php | 6 + .../alpha/Apc/ApcPrefixCollision/A/Foo.php | 6 + .../alpha/Apc/NamespaceCollision/A/Bar.php | 17 + .../alpha/Apc/NamespaceCollision/A/Foo.php | 17 + .../beta/Apc/ApcPrefixCollision/A/B/Bar.php | 6 + .../beta/Apc/ApcPrefixCollision/A/B/Foo.php | 6 + .../beta/Apc/NamespaceCollision/A/B/Bar.php | 17 + .../beta/Apc/NamespaceCollision/A/B/Foo.php | 17 + .../Apc/fallback/Apc/Pearlike/FooBar.php | 6 + .../Apc/fallback/Namespaced/FooBar.php | 17 + .../Tests/Fixtures/ClassesWithParents/A.php | 7 + .../Fixtures/ClassesWithParents/ATrait.php | 7 + .../Tests/Fixtures/ClassesWithParents/B.php | 7 + .../Fixtures/ClassesWithParents/BTrait.php | 8 + .../ClassesWithParents/CInterface.php | 7 + .../Fixtures/ClassesWithParents/CTrait.php | 7 + .../Tests/Fixtures/ClassesWithParents/D.php | 8 + .../Tests/Fixtures/ClassesWithParents/E.php | 8 + .../Tests/Fixtures/ClassesWithParents/F.php | 8 + .../Tests/Fixtures/ClassesWithParents/G.php | 8 + .../ClassesWithParents/GInterface.php | 7 + .../Tests/Fixtures/DeclaredClass.php | 7 + .../Tests/Fixtures/DeclaredInterface.php | 7 + .../Tests/Fixtures/Namespaced/Bar.php | 17 + .../Tests/Fixtures/Namespaced/Baz.php | 17 + .../Tests/Fixtures/Namespaced/Foo.php | 17 + .../Fixtures/Namespaced/WithComments.php | 37 + .../Fixtures/Namespaced/WithDirMagic.php | 15 + .../Fixtures/Namespaced/WithFileMagic.php | 15 + .../Fixtures/Namespaced/WithHaltCompiler.php | 18 + .../Fixtures/Namespaced/WithStrictTypes.php | 13 + .../Tests/Fixtures/Namespaced2/Bar.php | 8 + .../Tests/Fixtures/Namespaced2/Baz.php | 8 + .../Tests/Fixtures/Namespaced2/Foo.php | 8 + .../Tests/Fixtures/Pearlike/Bar.php | 6 + .../Tests/Fixtures/Pearlike/Baz.php | 6 + .../Tests/Fixtures/Pearlike/Foo.php | 6 + .../Tests/Fixtures/Pearlike/WithComments.php | 16 + .../Tests/Fixtures/Pearlike2/Bar.php | 6 + .../Tests/Fixtures/Pearlike2/Baz.php | 6 + .../Tests/Fixtures/Pearlike2/Foo.php | 6 + .../Tests/Fixtures/WarmedClass.php | 7 + .../Tests/Fixtures/WarmedInterface.php | 7 + .../alpha/NamespaceCollision/A/Bar.php | 17 + .../alpha/NamespaceCollision/A/Foo.php | 17 + .../alpha/NamespaceCollision/C/Bar.php | 8 + .../alpha/NamespaceCollision/C/Foo.php | 8 + .../Fixtures/alpha/PrefixCollision/A/Bar.php | 6 + .../Fixtures/alpha/PrefixCollision/A/Foo.php | 6 + .../Fixtures/alpha/PrefixCollision/C/Bar.php | 6 + .../Fixtures/alpha/PrefixCollision/C/Foo.php | 6 + .../beta/NamespaceCollision/A/B/Bar.php | 17 + .../beta/NamespaceCollision/A/B/Foo.php | 17 + .../beta/NamespaceCollision/C/B/Bar.php | 8 + .../beta/NamespaceCollision/C/B/Foo.php | 8 + .../Fixtures/beta/PrefixCollision/A/B/Bar.php | 6 + .../Fixtures/beta/PrefixCollision/A/B/Foo.php | 6 + .../Fixtures/beta/PrefixCollision/C/B/Bar.php | 6 + .../Fixtures/beta/PrefixCollision/C/B/Foo.php | 6 + .../Tests/Fixtures/classmap/SomeClass.php | 16 + .../Tests/Fixtures/classmap/SomeInterface.php | 16 + .../Tests/Fixtures/classmap/SomeParent.php | 16 + .../Tests/Fixtures/classmap/multipleNs.php | 25 + .../Tests/Fixtures/classmap/notAClass.php | 3 + .../Tests/Fixtures/classmap/notPhpFile.md | 1 + .../classmap/sameNsMultipleClasses.php | 19 + .../Tests/Fixtures/deps/traits.php | 37 + .../Fixtures/fallback/Namespaced/FooBar.php | 17 + .../Fixtures/fallback/Namespaced2/FooBar.php | 8 + .../Fixtures/fallback/Pearlike/FooBar.php | 6 + .../Fixtures/fallback/Pearlike2/FooBar.php | 6 + .../Tests/Fixtures/includepath/Foo.php | 5 + .../Tests/Fixtures/php5.4/traits.php | 32 + .../Tests/Fixtures/php5.5/class_cons.php | 11 + .../Fixtures/psr-4/Class_With_Underscores.php | 7 + .../class-loader/Tests/Fixtures/psr-4/Foo.php | 7 + .../Lets/Go/Deeper/Class_With_Underscores.php | 7 + .../Fixtures/psr-4/Lets/Go/Deeper/Foo.php | 7 + .../Tests/Psr4ClassLoaderTest.php | 75 + .../class-loader/WinCacheClassLoader.php | 142 + .../class-loader/XcacheClassLoader.php | 137 + vendor/symfony/class-loader/composer.json | 40 + vendor/symfony/class-loader/phpunit.xml.dist | 31 + vendor/symfony/config/.gitignore | 3 + vendor/symfony/config/CHANGELOG.md | 78 + vendor/symfony/config/ConfigCache.php | 62 + vendor/symfony/config/ConfigCacheFactory.php | 51 + .../config/ConfigCacheFactoryInterface.php | 32 + .../symfony/config/ConfigCacheInterface.php | 49 + .../symfony/config/Definition/ArrayNode.php | 380 +++ vendor/symfony/config/Definition/BaseNode.php | 362 +++ .../symfony/config/Definition/BooleanNode.php | 47 + .../Builder/ArrayNodeDefinition.php | 522 +++ .../Builder/BooleanNodeDefinition.php | 53 + .../Definition/Builder/EnumNodeDefinition.php | 56 + .../config/Definition/Builder/ExprBuilder.php | 248 ++ .../Builder/FloatNodeDefinition.php | 32 + .../Builder/IntegerNodeDefinition.php | 32 + .../Definition/Builder/MergeBuilder.php | 67 + .../config/Definition/Builder/NodeBuilder.php | 238 ++ .../Definition/Builder/NodeDefinition.php | 353 ++ .../Builder/NodeParentInterface.php | 21 + .../Builder/NormalizationBuilder.php | 60 + .../Builder/NumericNodeDefinition.php | 73 + .../Builder/ParentNodeDefinitionInterface.php | 49 + .../Builder/ScalarNodeDefinition.php | 32 + .../config/Definition/Builder/TreeBuilder.php | 67 + .../Definition/Builder/ValidationBuilder.php | 44 + .../Builder/VariableNodeDefinition.php | 65 + .../Definition/ConfigurationInterface.php | 27 + .../Definition/Dumper/XmlReferenceDumper.php | 311 ++ .../Definition/Dumper/YamlReferenceDumper.php | 256 ++ vendor/symfony/config/Definition/EnumNode.php | 54 + .../Exception/DuplicateKeyException.php | 22 + .../config/Definition/Exception/Exception.php | 21 + .../Exception/ForbiddenOverwriteException.php | 22 + .../InvalidConfigurationException.php | 49 + .../Exception/InvalidDefinitionException.php | 21 + .../Exception/InvalidTypeException.php | 21 + .../Exception/UnsetKeyException.php | 22 + .../symfony/config/Definition/FloatNode.php | 43 + .../symfony/config/Definition/IntegerNode.php | 38 + .../config/Definition/NodeInterface.php | 100 + .../symfony/config/Definition/NumericNode.php | 64 + .../symfony/config/Definition/Processor.php | 97 + .../Definition/PrototypeNodeInterface.php | 27 + .../config/Definition/PrototypedArrayNode.php | 377 +++ .../symfony/config/Definition/ScalarNode.php | 53 + .../config/Definition/VariableNode.php | 128 + .../DependencyInjection/ConfigCachePass.php | 52 + ...LoaderImportCircularReferenceException.php | 27 + .../Exception/FileLoaderLoadException.php | 109 + .../FileLocatorFileNotFoundException.php | 34 + vendor/symfony/config/FileLocator.php | 98 + .../symfony/config/FileLocatorInterface.php | 34 + vendor/symfony/config/LICENSE | 19 + .../config/Loader/DelegatingLoader.php | 50 + vendor/symfony/config/Loader/FileLoader.php | 172 + .../symfony/config/Loader/GlobFileLoader.php | 36 + vendor/symfony/config/Loader/Loader.php | 78 + .../symfony/config/Loader/LoaderInterface.php | 52 + .../symfony/config/Loader/LoaderResolver.php | 68 + .../config/Loader/LoaderResolverInterface.php | 30 + vendor/symfony/config/README.md | 15 + .../Resource/ClassExistenceResource.php | 169 + .../config/Resource/ComposerResource.php | 78 + .../config/Resource/DirectoryResource.php | 116 + .../config/Resource/FileExistenceResource.php | 76 + .../symfony/config/Resource/FileResource.php | 75 + .../symfony/config/Resource/GlobResource.php | 151 + .../Resource/ReflectionClassResource.php | 200 ++ .../config/Resource/ResourceInterface.php | 33 + .../Resource/SelfCheckingResourceChecker.php | 36 + .../SelfCheckingResourceInterface.php | 30 + .../config/ResourceCheckerConfigCache.php | 182 ++ .../ResourceCheckerConfigCacheFactory.php | 48 + .../config/ResourceCheckerInterface.php | 48 + .../config/Tests/ConfigCacheFactoryTest.php | 29 + .../symfony/config/Tests/ConfigCacheTest.php | 99 + .../config/Tests/Definition/ArrayNodeTest.php | 251 ++ .../Tests/Definition/BooleanNodeTest.php | 74 + .../Builder/ArrayNodeDefinitionTest.php | 347 ++ .../Builder/BooleanNodeDefinitionTest.php | 39 + .../Builder/EnumNodeDefinitionTest.php | 77 + .../Definition/Builder/ExprBuilderTest.php | 270 ++ .../Definition/Builder/NodeBuilderTest.php | 95 + .../Builder/NumericNodeDefinitionTest.php | 104 + .../Definition/Builder/TreeBuilderTest.php | 134 + .../Dumper/XmlReferenceDumperTest.php | 114 + .../Dumper/YamlReferenceDumperTest.php | 143 + .../config/Tests/Definition/EnumNodeTest.php | 55 + .../Tests/Definition/FinalizationTest.php | 74 + .../config/Tests/Definition/FloatNodeTest.php | 78 + .../Tests/Definition/IntegerNodeTest.php | 75 + .../config/Tests/Definition/MergeTest.php | 196 ++ .../Tests/Definition/NormalizationTest.php | 230 ++ .../Definition/PrototypedArrayNodeTest.php | 342 ++ .../Tests/Definition/ScalarNodeTest.php | 169 + .../ConfigCachePassTest.php | 59 + .../Exception/FileLoaderLoadExceptionTest.php | 98 + .../symfony/config/Tests/FileLocatorTest.php | 120 + .../config/Tests/Fixtures/Again/foo.xml | 0 .../config/Tests/Fixtures/BadParent.php | 7 + .../symfony/config/Tests/Fixtures/BarNode.php | 18 + .../Fixtures/Builder/BarNodeDefinition.php | 23 + .../Tests/Fixtures/Builder/NodeBuilder.php | 34 + .../Builder/VariableNodeDefinition.php | 18 + .../Configuration/ExampleConfiguration.php | 102 + .../Tests/Fixtures/Resource/.hiddenFile | 0 .../Fixtures/Resource/ConditionalClass.php | 9 + .../Tests/Fixtures/Util/document_type.xml | 3 + .../config/Tests/Fixtures/Util/invalid.xml | 2 + .../Tests/Fixtures/Util/invalid_schema.xml | 2 + .../config/Tests/Fixtures/Util/schema.xsd | 9 + .../config/Tests/Fixtures/Util/valid.xml | 3 + vendor/symfony/config/Tests/Fixtures/foo.xml | 0 .../Tests/Loader/DelegatingLoaderTest.php | 71 + .../config/Tests/Loader/FileLoaderTest.php | 128 + .../Tests/Loader/LoaderResolverTest.php | 47 + .../config/Tests/Loader/LoaderTest.php | 118 + .../Resource/ClassExistenceResourceTest.php | 100 + .../Tests/Resource/ComposerResourceTest.php | 47 + .../Tests/Resource/DirectoryResourceTest.php | 183 ++ .../Resource/FileExistenceResourceTest.php | 71 + .../Tests/Resource/FileResourceTest.php | 85 + .../Tests/Resource/GlobResourceTest.php | 114 + .../Resource/ReflectionClassResourceTest.php | 189 ++ .../config/Tests/Resource/ResourceStub.php | 34 + .../Tests/ResourceCheckerConfigCacheTest.php | 150 + .../config/Tests/Util/XmlUtilsTest.php | 217 ++ .../Util/Exception/InvalidXmlException.php | 21 + .../Util/Exception/XmlParsingException.php | 21 + vendor/symfony/config/Util/XmlUtils.php | 269 ++ vendor/symfony/config/composer.json | 48 + vendor/symfony/config/phpunit.xml.dist | 31 + vendor/symfony/debug/.gitignore | 3 + vendor/symfony/debug/BufferingLogger.php | 37 + vendor/symfony/debug/CHANGELOG.md | 70 + vendor/symfony/debug/Debug.php | 60 + vendor/symfony/debug/DebugClassLoader.php | 429 +++ vendor/symfony/debug/ErrorHandler.php | 682 ++++ .../Exception/ClassNotFoundException.php | 36 + .../debug/Exception/FatalErrorException.php | 77 + .../debug/Exception/FatalThrowableError.php | 51 + .../debug/Exception/FlattenException.php | 276 ++ .../debug/Exception/OutOfMemoryException.php | 21 + .../debug/Exception/SilencedErrorContext.php | 67 + .../Exception/UndefinedFunctionException.php | 36 + .../Exception/UndefinedMethodException.php | 36 + vendor/symfony/debug/ExceptionHandler.php | 440 +++ .../ClassNotFoundFatalErrorHandler.php | 189 ++ .../FatalErrorHandlerInterface.php | 32 + .../UndefinedFunctionFatalErrorHandler.php | 84 + .../UndefinedMethodFatalErrorHandler.php | 66 + vendor/symfony/debug/LICENSE | 19 + vendor/symfony/debug/README.md | 13 + .../debug/Tests/DebugClassLoaderTest.php | 346 ++ .../symfony/debug/Tests/ErrorHandlerTest.php | 504 +++ .../Tests/Exception/FlattenExceptionTest.php | 340 ++ .../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 | 19 + .../debug/Tests/Fixtures/FinalClass.php | 10 + .../debug/Tests/Fixtures/FinalMethod.php | 24 + .../Tests/Fixtures/FinalMethod2Trait.php | 10 + .../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 + .../Fixtures/TraitWithInternalMethod.php | 13 + .../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 | 27 + .../Tests/phpt/decorate_exception_hander.phpt | 47 + .../debug/Tests/phpt/exception_rethrown.phpt | 35 + .../phpt/fatal_with_nested_handlers.phpt | 42 + vendor/symfony/debug/composer.json | 40 + vendor/symfony/debug/phpunit.xml.dist | 33 + .../symfony/dependency-injection/.gitignore | 3 + vendor/symfony/dependency-injection/Alias.php | 90 + .../Argument/ArgumentInterface.php | 27 + .../Argument/BoundArgument.php | 46 + .../Argument/IteratorArgument.php | 55 + .../Argument/RewindableGenerator.php | 47 + .../Argument/ServiceClosureArgument.php | 50 + .../Argument/TaggedIteratorArgument.php | 34 + .../symfony/dependency-injection/CHANGELOG.md | 182 ++ .../dependency-injection/ChildDefinition.php | 124 + .../Compiler/AbstractRecursivePass.php | 172 + .../Compiler/AnalyzeServiceReferencesPass.php | 175 + .../Compiler/AutoAliasServicePass.php | 41 + .../Compiler/AutowirePass.php | 383 +++ .../Compiler/AutowireRequiredMethodsPass.php | 70 + .../Compiler/CheckArgumentsValidityPass.php | 85 + .../Compiler/CheckCircularReferencesPass.php | 78 + .../Compiler/CheckDefinitionValidityPass.php | 106 + ...xceptionOnInvalidReferenceBehaviorPass.php | 36 + .../Compiler/CheckReferenceValidityPass.php | 48 + .../Compiler/Compiler.php | 120 + .../Compiler/CompilerPassInterface.php | 27 + .../Compiler/DecoratorServicePass.php | 67 + .../Compiler/DefinitionErrorExceptionPass.php | 39 + .../Compiler/ExtensionCompilerPass.php | 37 + .../Compiler/InlineServiceDefinitionsPass.php | 129 + .../MergeExtensionConfigurationPass.php | 202 ++ .../Compiler/PassConfig.php | 269 ++ .../Compiler/PriorityTaggedServiceTrait.php | 55 + .../Compiler/RegisterEnvVarProcessorsPass.php | 78 + .../RegisterServiceSubscribersPass.php | 101 + .../RemoveAbstractDefinitionsPass.php | 33 + .../Compiler/RemovePrivateAliasesPass.php | 39 + .../Compiler/RemoveUnusedDefinitionsPass.php | 85 + .../Compiler/RepeatablePassInterface.php | 23 + .../Compiler/RepeatedPass.php | 79 + .../ReplaceAliasByActualDefinitionPass.php | 89 + .../Compiler/ResolveBindingsPass.php | 171 + .../Compiler/ResolveChildDefinitionsPass.php | 175 + .../Compiler/ResolveClassPass.php | 40 + .../Compiler/ResolveEnvPlaceholdersPass.php | 44 + .../Compiler/ResolveFactoryClassPass.php | 38 + .../Compiler/ResolveHotPathPass.php | 71 + .../ResolveInstanceofConditionalsPass.php | 155 + .../Compiler/ResolveInvalidReferencesPass.php | 111 + .../Compiler/ResolveNamedArgumentsPass.php | 101 + .../ResolveParameterPlaceHoldersPass.php | 87 + .../Compiler/ResolvePrivatesPass.php | 40 + .../ResolveReferencesToAliasesPass.php | 69 + .../ResolveServiceSubscribersPass.php | 51 + .../ResolveTaggedIteratorArgumentPass.php | 38 + .../Compiler/ServiceLocatorTagPass.php | 112 + .../Compiler/ServiceReferenceGraph.php | 98 + .../Compiler/ServiceReferenceGraphEdge.php | 87 + .../Compiler/ServiceReferenceGraphNode.php | 118 + .../Config/ContainerParametersResource.php | 64 + .../ContainerParametersResourceChecker.php | 52 + .../dependency-injection/Container.php | 403 +++ .../ContainerAwareInterface.php | 22 + .../ContainerAwareTrait.php | 30 + .../dependency-injection/ContainerBuilder.php | 1584 +++++++++ .../ContainerInterface.php | 100 + .../dependency-injection/Definition.php | 884 +++++ .../dependency-injection/Dumper/Dumper.php | 29 + .../Dumper/DumperInterface.php | 29 + .../Dumper/GraphvizDumper.php | 243 ++ .../dependency-injection/Dumper/PhpDumper.php | 1833 +++++++++++ .../dependency-injection/Dumper/XmlDumper.php | 359 ++ .../Dumper/YamlDumper.php | 319 ++ .../dependency-injection/EnvVarProcessor.php | 163 + .../EnvVarProcessorInterface.php | 40 + .../Exception/AutowiringFailedException.php | 32 + .../Exception/BadMethodCallException.php | 19 + .../Exception/EnvNotFoundException.php | 25 + .../Exception/EnvParameterException.php | 25 + .../Exception/ExceptionInterface.php | 24 + .../Exception/InvalidArgumentException.php | 21 + .../Exception/LogicException.php | 19 + .../Exception/OutOfBoundsException.php | 19 + .../ParameterCircularReferenceException.php | 34 + .../Exception/ParameterNotFoundException.php | 98 + .../Exception/RuntimeException.php | 21 + .../ServiceCircularReferenceException.php | 41 + .../Exception/ServiceNotFoundException.php | 67 + .../ExpressionLanguage.php | 36 + .../ExpressionLanguageProvider.php | 50 + .../ConfigurationExtensionInterface.php | 30 + .../Extension/Extension.php | 124 + .../Extension/ExtensionInterface.php | 52 + .../Extension/PrependExtensionInterface.php | 22 + vendor/symfony/dependency-injection/LICENSE | 19 + .../Instantiator/InstantiatorInterface.php | 36 + .../Instantiator/RealServiceInstantiator.php | 33 + .../LazyProxy/PhpDumper/DumperInterface.php | 47 + .../LazyProxy/PhpDumper/NullDumper.php | 48 + .../LazyProxy/ProxyHelper.php | 57 + .../Loader/ClosureLoader.php | 48 + .../Configurator/AbstractConfigurator.php | 87 + .../AbstractServiceConfigurator.php | 93 + .../Loader/Configurator/AliasConfigurator.php | 30 + .../Configurator/ContainerConfigurator.php | 117 + .../Configurator/DefaultsConfigurator.php | 61 + .../InlineServiceConfigurator.php | 36 + .../Configurator/InstanceofConfigurator.php | 37 + .../Configurator/ParametersConfigurator.php | 51 + .../Configurator/PrototypeConfigurator.php | 82 + .../Configurator/ReferenceConfigurator.php | 66 + .../Configurator/ServiceConfigurator.php | 68 + .../Configurator/ServicesConfigurator.php | 122 + .../Configurator/Traits/AbstractTrait.php | 28 + .../Configurator/Traits/ArgumentTrait.php | 44 + .../Traits/AutoconfigureTrait.php | 35 + .../Configurator/Traits/AutowireTrait.php | 27 + .../Loader/Configurator/Traits/BindTrait.php | 43 + .../Loader/Configurator/Traits/CallTrait.php | 34 + .../Loader/Configurator/Traits/ClassTrait.php | 27 + .../Configurator/Traits/ConfiguratorTrait.php | 29 + .../Configurator/Traits/DecorateTrait.php | 35 + .../Configurator/Traits/DeprecateTrait.php | 33 + .../Configurator/Traits/FactoryTrait.php | 37 + .../Loader/Configurator/Traits/FileTrait.php | 29 + .../Loader/Configurator/Traits/LazyTrait.php | 27 + .../Configurator/Traits/ParentTrait.php | 50 + .../Configurator/Traits/PropertyTrait.php | 27 + .../Configurator/Traits/PublicTrait.php | 35 + .../Loader/Configurator/Traits/ShareTrait.php | 29 + .../Configurator/Traits/SyntheticTrait.php | 28 + .../Loader/Configurator/Traits/TagTrait.php | 42 + .../Loader/DirectoryLoader.php | 54 + .../Loader/FileLoader.php | 180 ++ .../Loader/GlobFileLoader.php | 40 + .../Loader/IniFileLoader.php | 93 + .../Loader/PhpFileLoader.php | 73 + .../Loader/XmlFileLoader.php | 720 +++++ .../Loader/YamlFileLoader.php | 802 +++++ .../schema/dic/services/services-1.0.xsd | 278 ++ .../dependency-injection/Parameter.php | 35 + .../EnvPlaceholderParameterBag.php | 123 + .../ParameterBag/FrozenParameterBag.php | 68 + .../ParameterBag/ParameterBag.php | 289 ++ .../ParameterBag/ParameterBagInterface.php | 115 + vendor/symfony/dependency-injection/README.md | 14 + .../dependency-injection/Reference.php | 47 + .../ResettableContainerInterface.php | 31 + .../dependency-injection/ServiceLocator.php | 145 + .../ServiceSubscriberInterface.php | 50 + .../TaggedContainerInterface.php | 29 + .../Argument/RewindableGeneratorTest.php | 53 + .../Tests/ChildDefinitionTest.php | 151 + .../AnalyzeServiceReferencesPassTest.php | 215 ++ .../Compiler/AutoAliasServicePassTest.php | 112 + .../Tests/Compiler/AutowirePassTest.php | 814 +++++ .../AutowireRequiredMethodsPassTest.php | 80 + .../CheckArgumentsValidityPassTest.php | 78 + .../CheckCircularReferencesPassTest.php | 158 + .../CheckDefinitionValidityPassTest.php | 120 + ...tionOnInvalidReferenceBehaviorPassTest.php | 90 + .../CheckReferenceValidityPassTest.php | 50 + .../Compiler/DecoratorServicePassTest.php | 152 + .../DefinitionErrorExceptionPassTest.php | 53 + .../Compiler/ExtensionCompilerPassTest.php | 81 + .../InlineServiceDefinitionsPassTest.php | 333 ++ .../Tests/Compiler/IntegrationTest.php | 227 ++ .../MergeExtensionConfigurationPassTest.php | 199 ++ .../Tests/Compiler/OptionalServiceClass.php | 18 + .../Tests/Compiler/PassConfigTest.php | 38 + .../PriorityTaggedServiceTraitTest.php | 91 + .../RegisterEnvVarProcessorsPassTest.php | 88 + .../RegisterServiceSubscribersPassTest.php | 139 + .../RemoveUnusedDefinitionsPassTest.php | 137 + ...ReplaceAliasByActualDefinitionPassTest.php | 71 + .../Compiler/ResolveBindingsPassTest.php | 114 + .../ResolveChildDefinitionsPassTest.php | 399 +++ .../Tests/Compiler/ResolveClassPassTest.php | 97 + .../Compiler/ResolveFactoryClassPassTest.php | 88 + .../Tests/Compiler/ResolveHotPathPassTest.php | 57 + .../ResolveInstanceofConditionalsPassTest.php | 268 ++ .../ResolveInvalidReferencesPassTest.php | 135 + .../ResolveNamedArgumentsPassTest.php | 162 + .../ResolveParameterPlaceHoldersPassTest.php | 100 + .../Compiler/ResolvePrivatesPassTest.php | 39 + .../ResolveReferencesToAliasesPassTest.php | 91 + .../ResolveTaggedIteratorArgumentPassTest.php | 40 + ...ContainerParametersResourceCheckerTest.php | 77 + .../ContainerParametersResourceTest.php | 43 + .../Tests/ContainerBuilderTest.php | 1473 +++++++++ .../Tests/ContainerTest.php | 483 +++ .../Tests/CrossCheckTest.php | 89 + .../Tests/DefinitionTest.php | 383 +++ .../Tests/Dumper/GraphvizDumperTest.php | 74 + .../Tests/Dumper/PhpDumperTest.php | 989 ++++++ .../Tests/Dumper/XmlDumperTest.php | 210 ++ .../Tests/Dumper/YamlDumperTest.php | 104 + .../Tests/EnvVarProcessorTest.php | 304 ++ .../Tests/Extension/ExtensionTest.php | 59 + .../Tests/Fixtures/Bar.php | 23 + .../Tests/Fixtures/BarInterface.php | 16 + .../Tests/Fixtures/CaseSensitiveClass.php | 22 + ...tructorWithMandatoryArgumentsContainer.php | 10 + ...structorWithOptionalArgumentsContainer.php | 10 + .../ConstructorWithoutArgumentsContainer.php | 10 + .../Container/NoConstructorContainer.php | 9 + .../Tests/Fixtures/CustomDefinition.php | 18 + .../Tests/Fixtures/DeprecatedClass.php | 18 + .../Tests/Fixtures/FactoryDummy.php | 43 + .../Tests/Fixtures/NamedArgumentsDummy.php | 21 + .../Tests/Fixtures/ParentNotExists.php | 7 + .../Prototype/BadClasses/MissingParent.php | 7 + .../Tests/Fixtures/Prototype/Foo.php | 14 + .../Tests/Fixtures/Prototype/FooInterface.php | 7 + .../OtherDir/AnotherSub/DeeperBaz.php | 7 + .../Tests/Fixtures/Prototype/OtherDir/Baz.php | 7 + .../OtherDir/Component1/Dir1/Service1.php | 7 + .../OtherDir/Component1/Dir2/Service2.php | 8 + .../OtherDir/Component1/Dir3/Service3.php | 8 + .../OtherDir/Component2/Dir1/Service4.php | 7 + .../OtherDir/Component2/Dir2/Service5.php | 8 + .../Tests/Fixtures/Prototype/Sub/Bar.php | 7 + .../Fixtures/Prototype/Sub/BarInterface.php | 7 + .../Prototype/Sub/NoLoadAbstractBar.php | 7 + .../Prototype/Sub/NoLoadBarInterface.php | 7 + .../Fixtures/Prototype/Sub/NoLoadBarTrait.php | 7 + .../Tests/Fixtures/SimilarArgumentsDummy.php | 24 + .../Tests/Fixtures/StubbedTranslator.php | 29 + .../Tests/Fixtures/TestServiceSubscriber.php | 22 + .../Tests/Fixtures/array.json | 1 + .../Tests/Fixtures/config/basic.expected.yml | 10 + .../Tests/Fixtures/config/basic.php | 11 + .../Tests/Fixtures/config/child.expected.yml | 15 + .../Tests/Fixtures/config/child.php | 22 + .../Fixtures/config/defaults.expected.yml | 27 + .../Tests/Fixtures/config/defaults.php | 21 + .../config/factory_short_notation.php | 9 + .../Fixtures/config/instanceof.expected.yml | 21 + .../Tests/Fixtures/config/instanceof.php | 22 + .../Tests/Fixtures/config/php7.expected.yml | 19 + .../Tests/Fixtures/config/php7.php | 19 + .../Fixtures/config/prototype.expected.yml | 25 + .../Tests/Fixtures/config/prototype.php | 22 + .../Tests/Fixtures/config/services9.php | 133 + .../services_autoconfigure_with_parent.php | 9 + .../Fixtures/containers/CustomContainer.php | 17 + .../Tests/Fixtures/containers/container10.php | 15 + .../Tests/Fixtures/containers/container11.php | 13 + .../Tests/Fixtures/containers/container12.php | 13 + .../Tests/Fixtures/containers/container13.php | 18 + .../Tests/Fixtures/containers/container14.php | 17 + .../Tests/Fixtures/containers/container15.php | 12 + .../Tests/Fixtures/containers/container16.php | 12 + .../Tests/Fixtures/containers/container17.php | 11 + .../Tests/Fixtures/containers/container19.php | 24 + .../Tests/Fixtures/containers/container21.php | 21 + .../Tests/Fixtures/containers/container24.php | 13 + .../Tests/Fixtures/containers/container33.php | 12 + .../Tests/Fixtures/containers/container8.php | 14 + .../Tests/Fixtures/containers/container9.php | 183 ++ .../containers/container_abstract.php | 13 + .../containers/container_almost_circular.php | 58 + .../containers/container_env_in_id.php | 22 + .../containers/container_inline_requires.php | 19 + .../container_uninitialized_ref.php | 49 + .../Fixtures/directory/import/import.yml | 2 + .../Fixtures/directory/recurse/simple.ini | 2 + .../Fixtures/directory/recurse/simple.yml | 2 + .../Tests/Fixtures/directory/simple.php | 3 + .../Tests/Fixtures/graphviz/services1.dot | 7 + .../Tests/Fixtures/graphviz/services10-1.dot | 10 + .../Tests/Fixtures/graphviz/services10.dot | 10 + .../Tests/Fixtures/graphviz/services13.dot | 10 + .../Tests/Fixtures/graphviz/services14.dot | 7 + .../Tests/Fixtures/graphviz/services17.dot | 8 + .../Tests/Fixtures/graphviz/services18.dot | 8 + .../Tests/Fixtures/graphviz/services9.dot | 60 + .../Tests/Fixtures/includes/FooVariadic.php | 16 + .../Tests/Fixtures/includes/HotPath/C1.php | 8 + .../Tests/Fixtures/includes/HotPath/C2.php | 7 + .../Tests/Fixtures/includes/HotPath/C3.php | 7 + .../Tests/Fixtures/includes/HotPath/I1.php | 7 + .../Tests/Fixtures/includes/HotPath/P1.php | 7 + .../Tests/Fixtures/includes/HotPath/T1.php | 7 + .../Fixtures/includes/ProjectExtension.php | 47 + .../includes/ProjectWithXsdExtension.php | 19 + .../ProjectWithXsdExtensionInPhar.phar | Bin 0 -> 1161 bytes .../Fixtures/includes/autowiring_classes.php | 354 ++ .../Tests/Fixtures/includes/classes.php | 136 + .../Tests/Fixtures/includes/createphar.php | 47 + .../Tests/Fixtures/includes/foo.php | 43 + .../Fixtures/includes/schema/project-1.0.xsd | 13 + .../Tests/Fixtures/ini/almostvalid.ini | 2 + .../Tests/Fixtures/ini/ini_with_wrong_ext.xml | 2 + .../Tests/Fixtures/ini/nonvalid.ini | 2 + .../Tests/Fixtures/ini/parameters.ini | 3 + .../Tests/Fixtures/ini/parameters1.ini | 3 + .../Tests/Fixtures/ini/parameters2.ini | 2 + .../Tests/Fixtures/ini/types.ini | 27 + ...er_class_constructor_without_arguments.php | 62 + ...s_with_mandatory_constructor_arguments.php | 59 + ...ss_with_optional_constructor_arguments.php | 62 + ...om_container_class_without_constructor.php | 59 + .../Tests/Fixtures/php/php_with_wrong_ext.yml | 3 + .../Tests/Fixtures/php/services1-1.php | 59 + .../Tests/Fixtures/php/services1.php | 57 + .../Tests/Fixtures/php/services10.php | 141 + .../Tests/Fixtures/php/services12.php | 155 + .../Tests/Fixtures/php/services13.php | 74 + .../Tests/Fixtures/php/services19.php | 85 + .../Tests/Fixtures/php/services24.php | 70 + .../Tests/Fixtures/php/services26.php | 175 + .../Tests/Fixtures/php/services33.php | 81 + .../Tests/Fixtures/php/services8.php | 140 + .../Tests/Fixtures/php/services9_as_files.txt | 521 +++ .../Tests/Fixtures/php/services9_compiled.php | 481 +++ .../php/services_almost_circular_private.php | 167 + .../php/services_almost_circular_public.php | 239 ++ .../Fixtures/php/services_array_params.php | 160 + .../Fixtures/php/services_base64_env.php | 135 + .../php/services_dedup_lazy_proxy.php | 92 + .../Tests/Fixtures/php/services_env_in_id.php | 153 + .../Fixtures/php/services_inline_requires.php | 177 + .../Tests/Fixtures/php/services_locator.php | 174 + .../Fixtures/php/services_non_shared_lazy.php | 90 + .../Fixtures/php/services_private_frozen.php | 82 + .../php/services_private_in_expression.php | 72 + .../Tests/Fixtures/php/services_rot13_env.php | 161 + .../Fixtures/php/services_subscriber.php | 92 + .../php/services_uninitialized_ref.php | 124 + .../Tests/Fixtures/php/simple.php | 3 + .../Tests/Fixtures/xml/class_from_id.xml | 6 + .../Fixtures/xml/extension1/services.xml | 14 + .../Fixtures/xml/extension2/services.xml | 14 + .../Fixtures/xml/extensions/services1.xml | 19 + .../Fixtures/xml/extensions/services2.xml | 19 + .../Fixtures/xml/extensions/services3.xml | 19 + .../Fixtures/xml/extensions/services4.xml | 6 + .../Fixtures/xml/extensions/services5.xml | 11 + .../Fixtures/xml/extensions/services6.xml | 11 + .../Fixtures/xml/extensions/services7.xml | 11 + .../Fixtures/xml/invalid_alias_definition.xml | 6 + .../Tests/Fixtures/xml/namespaces.xml | 17 + .../xml/nested_service_without_id.xml | 10 + .../Tests/Fixtures/xml/nonvalid.xml | 3 + .../Tests/Fixtures/xml/services1.xml | 8 + .../Tests/Fixtures/xml/services10.xml | 16 + .../Tests/Fixtures/xml/services13.xml | 9 + .../Tests/Fixtures/xml/services14.xml | 19 + .../Tests/Fixtures/xml/services2.xml | 31 + .../Tests/Fixtures/xml/services21.xml | 24 + .../Tests/Fixtures/xml/services23.xml | 6 + .../Tests/Fixtures/xml/services24.xml | 9 + .../Tests/Fixtures/xml/services28.xml | 13 + .../Tests/Fixtures/xml/services3.xml | 13 + .../Tests/Fixtures/xml/services4.xml | 14 + .../Fixtures/xml/services4_bad_import.xml | 9 + .../Tests/Fixtures/xml/services5.xml | 22 + .../Tests/Fixtures/xml/services6.xml | 65 + .../Tests/Fixtures/xml/services7.xml | 9 + .../Tests/Fixtures/xml/services8.xml | 27 + .../Tests/Fixtures/xml/services9.xml | 152 + .../Tests/Fixtures/xml/services_abstract.xml | 9 + .../Fixtures/xml/services_autoconfigure.xml | 9 + .../services_autoconfigure_with_parent.xml | 7 + .../Tests/Fixtures/xml/services_bindings.xml | 21 + .../Tests/Fixtures/xml/services_case.xml | 16 + .../xml/services_defaults_with_parent.xml | 9 + .../Fixtures/xml/services_deprecated.xml | 11 + .../Tests/Fixtures/xml/services_dump_load.xml | 11 + .../xml/services_inline_not_candidate.xml | 11 + .../Fixtures/xml/services_instanceof.xml | 12 + .../xml/services_instanceof_with_parent.xml | 9 + .../Fixtures/xml/services_named_args.xml | 13 + .../Tests/Fixtures/xml/services_prototype.xml | 6 + .../Fixtures/xml/services_without_id.xml | 12 + .../Fixtures/xml/tag_with_empty_name.xml | 11 + .../Tests/Fixtures/xml/tag_without_name.xml | 11 + .../xml/with_key_outside_collection.xml | 9 + .../Tests/Fixtures/xml/withdoctype.xml | 3 + .../Tests/Fixtures/xml/xml_with_wrong_ext.php | 9 + .../Fixtures/yaml/anonymous_services.yml | 14 + .../yaml/anonymous_services_alias.yml | 7 + .../yaml/anonymous_services_in_instanceof.yml | 15 + .../yaml/anonymous_services_in_parameters.yml | 2 + .../Tests/Fixtures/yaml/bad_alias.yml | 11 + .../Tests/Fixtures/yaml/bad_calls.yml | 4 + .../Tests/Fixtures/yaml/bad_decorates.yml | 7 + .../Fixtures/yaml/bad_empty_defaults.yml | 2 + .../Fixtures/yaml/bad_empty_instanceof.yml | 2 + .../Tests/Fixtures/yaml/bad_format.yml | 2 + .../Tests/Fixtures/yaml/bad_import.yml | 2 + .../Tests/Fixtures/yaml/bad_imports.yml | 2 + .../Tests/Fixtures/yaml/bad_keyword.yml | 10 + .../Tests/Fixtures/yaml/bad_parameters.yml | 2 + .../Tests/Fixtures/yaml/bad_service.yml | 2 + .../Tests/Fixtures/yaml/bad_services.yml | 1 + .../Tests/Fixtures/yaml/badtag1.yml | 5 + .../Tests/Fixtures/yaml/badtag2.yml | 6 + .../Tests/Fixtures/yaml/badtag3.yml | 6 + .../Tests/Fixtures/yaml/bar/services.yml | 4 + .../Tests/Fixtures/yaml/class_from_id.yml | 4 + .../Tests/Fixtures/yaml/foo/services.yml | 4 + .../_child.yml | 4 + .../expected.yml | 11 + .../autoconfigure_child_not_applied/main.yml | 8 + .../autoconfigure_parent_child/_child.yml | 3 + .../autoconfigure_parent_child/expected.yml | 6 + .../autoconfigure_parent_child/main.yml | 8 + .../_child.yml | 3 + .../expected.yml | 7 + .../autoconfigure_parent_child_tags/main.yml | 8 + .../integration/child_parent/expected.yml | 9 + .../yaml/integration/child_parent/main.yml | 13 + .../defaults_child_tags/expected.yml | 9 + .../integration/defaults_child_tags/main.yml | 19 + .../expected.yml | 26 + .../defaults_instanceof_importance/main.yml | 30 + .../defaults_parent_child/_child.yml | 4 + .../defaults_parent_child/expected.yml | 7 + .../defaults_parent_child/main.yml | 10 + .../instanceof_parent_child/_child.yml | 4 + .../instanceof_parent_child/expected.yml | 8 + .../instanceof_parent_child/main.yml | 12 + .../yaml/legacy_invalid_alias_definition.yml | 5 + .../Tests/Fixtures/yaml/nonvalid1.yml | 2 + .../Tests/Fixtures/yaml/nonvalid2.yml | 1 + .../Tests/Fixtures/yaml/null_config.yml | 1 + .../Tests/Fixtures/yaml/services1.yml | 11 + .../Tests/Fixtures/yaml/services10.yml | 10 + .../Tests/Fixtures/yaml/services11.yml | 1 + .../Tests/Fixtures/yaml/services13.yml | 3 + .../Tests/Fixtures/yaml/services14.yml | 3 + .../Tests/Fixtures/yaml/services2.yml | 13 + .../Tests/Fixtures/yaml/services21.yml | 15 + .../Tests/Fixtures/yaml/services23.yml | 4 + .../Tests/Fixtures/yaml/services24.yml | 16 + .../Tests/Fixtures/yaml/services26.yml | 22 + .../Tests/Fixtures/yaml/services28.yml | 34 + .../Tests/Fixtures/yaml/services3.yml | 5 + .../Fixtures/yaml/services31_invalid_tags.yml | 6 + .../Tests/Fixtures/yaml/services4.yml | 8 + .../Fixtures/yaml/services4_bad_import.yml | 2 + .../Tests/Fixtures/yaml/services6.yml | 43 + .../Tests/Fixtures/yaml/services7.yml | 2 + .../Tests/Fixtures/yaml/services8.yml | 18 + .../Tests/Fixtures/yaml/services9.yml | 173 + .../Fixtures/yaml/services_autoconfigure.yml | 9 + .../services_autoconfigure_with_parent.yml | 9 + .../Tests/Fixtures/yaml/services_bindings.yml | 17 + .../Tests/Fixtures/yaml/services_case.yml | 11 + .../services_configurator_short_syntax.yml | 8 + .../yaml/services_defaults_with_parent.yml | 11 + .../Fixtures/yaml/services_dump_load.yml | 16 + .../Tests/Fixtures/yaml/services_inline.yml | 16 + .../Fixtures/yaml/services_instanceof.yml | 11 + .../yaml/services_instanceof_with_parent.yml | 12 + .../Fixtures/yaml/services_named_args.yml | 12 + .../Fixtures/yaml/services_prototype.yml | 4 + .../yaml/services_prototype_namespace.yml | 10 + ...s_prototype_namespace_without_resource.yml | 4 + .../Fixtures/yaml/services_underscore.yml | 3 + .../Fixtures/yaml/tag_name_empty_string.yml | 6 + .../Fixtures/yaml/tag_name_no_string.yml | 6 + .../Tests/Fixtures/yaml/tag_name_only.yml | 5 + .../Fixtures/yaml/yaml_with_wrong_ext.ini | 2 + .../RealServiceInstantiatorTest.php | 36 + .../LazyProxy/PhpDumper/NullDumperTest.php | 34 + .../Tests/Loader/ClosureLoaderTest.php | 38 + .../Tests/Loader/DirectoryLoaderTest.php | 80 + .../Tests/Loader/FileLoaderTest.php | 241 ++ .../Tests/Loader/GlobFileLoaderTest.php | 44 + .../Tests/Loader/IniFileLoaderTest.php | 128 + .../Tests/Loader/LoaderResolverTest.php | 62 + .../Tests/Loader/PhpFileLoaderTest.php | 102 + .../Tests/Loader/XmlFileLoaderTest.php | 798 +++++ .../Tests/Loader/YamlFileLoaderTest.php | 741 +++++ .../EnvPlaceholderParameterBagTest.php | 165 + .../ParameterBag/FrozenParameterBagTest.php | 64 + .../Tests/ParameterBag/ParameterBagTest.php | 260 ++ .../Tests/ParameterTest.php | 24 + .../Tests/ReferenceTest.php | 24 + .../Tests/ServiceLocatorTest.php | 144 + .../dependency-injection/TypedReference.php | 51 + .../symfony/dependency-injection/Variable.php | 40 + .../dependency-injection/composer.json | 55 + .../dependency-injection/phpunit.xml.dist | 31 + vendor/symfony/event-dispatcher/.gitignore | 3 + vendor/symfony/event-dispatcher/CHANGELOG.md | 55 + .../Debug/TraceableEventDispatcher.php | 336 ++ .../TraceableEventDispatcherInterface.php | 41 + .../Debug/WrappedListener.php | 114 + .../RegisterListenersPass.php | 136 + 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 +++ .../Debug/TraceableEventDispatcherTest.php | 293 ++ .../RegisterListenersPassTest.php | 206 ++ .../Tests/EventDispatcherTest.php | 22 + .../event-dispatcher/Tests/EventTest.php | 55 + .../Tests/GenericEventTest.php | 140 + .../Tests/ImmutableEventDispatcherTest.php | 106 + vendor/symfony/event-dispatcher/composer.json | 47 + .../symfony/event-dispatcher/phpunit.xml.dist | 31 + vendor/symfony/expression-language/.gitignore | 3 + .../symfony/expression-language/CHANGELOG.md | 20 + .../symfony/expression-language/Compiler.php | 146 + .../expression-language/Expression.php | 37 + .../ExpressionFunction.php | 100 + .../ExpressionFunctionProviderInterface.php | 23 + .../ExpressionLanguage.php | 167 + vendor/symfony/expression-language/LICENSE | 19 + vendor/symfony/expression-language/Lexer.php | 103 + .../Node/ArgumentsNode.php | 40 + .../expression-language/Node/ArrayNode.php | 118 + .../expression-language/Node/BinaryNode.php | 162 + .../Node/ConditionalNode.php | 56 + .../expression-language/Node/ConstantNode.php | 81 + .../expression-language/Node/FunctionNode.php | 67 + .../expression-language/Node/GetAttrNode.php | 114 + .../expression-language/Node/NameNode.php | 45 + .../symfony/expression-language/Node/Node.php | 110 + .../expression-language/Node/UnaryNode.php | 66 + .../expression-language/ParsedExpression.php | 40 + vendor/symfony/expression-language/Parser.php | 380 +++ vendor/symfony/expression-language/README.md | 15 + .../Resources/bin/generate_operator_regex.php | 23 + .../SerializedParsedExpression.php | 37 + .../expression-language/SyntaxError.php | 41 + .../Tests/ExpressionFunctionTest.php | 45 + .../Tests/ExpressionLanguageTest.php | 263 ++ .../Tests/ExpressionTest.php | 28 + .../Tests/Fixtures/TestProvider.php | 41 + .../expression-language/Tests/LexerTest.php | 121 + .../Tests/Node/AbstractNodeTest.php | 50 + .../Tests/Node/ArgumentsNodeTest.php | 36 + .../Tests/Node/ArrayNodeTest.php | 73 + .../Tests/Node/BinaryNodeTest.php | 166 + .../Tests/Node/ConditionalNodeTest.php | 42 + .../Tests/Node/ConstantNodeTest.php | 60 + .../Tests/Node/FunctionNodeTest.php | 52 + .../Tests/Node/GetAttrNodeTest.php | 78 + .../Tests/Node/NameNodeTest.php | 38 + .../Tests/Node/NodeTest.php | 41 + .../Tests/Node/UnaryNodeTest.php | 48 + .../Tests/ParsedExpressionTest.php | 29 + .../expression-language/Tests/ParserTest.php | 210 ++ vendor/symfony/expression-language/Token.php | 66 + .../expression-language/TokenStream.php | 93 + .../symfony/expression-language/composer.json | 34 + .../expression-language/phpunit.xml.dist | 30 + vendor/symfony/filesystem/.gitignore | 3 + vendor/symfony/filesystem/CHANGELOG.md | 59 + .../Exception/ExceptionInterface.php | 21 + .../Exception/FileNotFoundException.php | 34 + .../filesystem/Exception/IOException.php | 39 + .../Exception/IOExceptionInterface.php | 27 + .../Exception/InvalidArgumentException.php | 19 + vendor/symfony/filesystem/Filesystem.php | 766 +++++ vendor/symfony/filesystem/LICENSE | 19 + vendor/symfony/filesystem/README.md | 13 + .../filesystem/Tests/ExceptionTest.php | 47 + .../filesystem/Tests/FilesystemTest.php | 1646 ++++++++++ .../filesystem/Tests/FilesystemTestCase.php | 166 + .../Tests/Fixtures/MockStream/MockStream.php | 46 + vendor/symfony/filesystem/composer.json | 34 + vendor/symfony/filesystem/phpunit.xml.dist | 30 + vendor/symfony/finder/.gitignore | 3 + vendor/symfony/finder/CHANGELOG.md | 61 + .../symfony/finder/Comparator/Comparator.php | 98 + .../finder/Comparator/DateComparator.php | 51 + .../finder/Comparator/NumberComparator.php | 79 + .../Exception/AccessDeniedException.php | 19 + vendor/symfony/finder/Finder.php | 746 +++++ vendor/symfony/finder/Glob.php | 116 + .../finder/Iterator/CustomFilterIterator.php | 61 + .../Iterator/DateRangeFilterIterator.php | 58 + .../Iterator/DepthRangeFilterIterator.php | 45 + .../ExcludeDirectoryFilterIterator.php | 84 + .../Iterator/FileTypeFilterIterator.php | 53 + .../Iterator/FilecontentFilterIterator.php | 58 + .../Iterator/FilenameFilterIterator.php | 47 + .../Iterator/MultiplePcreFilterIterator.php | 112 + .../finder/Iterator/PathFilterIterator.php | 56 + .../Iterator/RecursiveDirectoryIterator.php | 140 + .../Iterator/SizeRangeFilterIterator.php | 57 + .../finder/Iterator/SortableIterator.php | 80 + vendor/symfony/finder/LICENSE | 19 + vendor/symfony/finder/README.md | 14 + vendor/symfony/finder/SplFileInfo.php | 78 + .../Tests/Comparator/ComparatorTest.php | 65 + .../Tests/Comparator/DateComparatorTest.php | 64 + .../Tests/Comparator/NumberComparatorTest.php | 108 + vendor/symfony/finder/Tests/FinderTest.php | 737 +++++ vendor/symfony/finder/Tests/Fixtures/.dot/a | 0 .../finder/Tests/Fixtures/.dot/b/c.neon | 0 .../finder/Tests/Fixtures/.dot/b/d.neon | 0 .../finder/Tests/Fixtures/A/B/C/abc.dat | 0 .../symfony/finder/Tests/Fixtures/A/B/ab.dat | 0 vendor/symfony/finder/Tests/Fixtures/A/a.dat | 0 .../Tests/Fixtures/copy/A/B/C/abc.dat.copy | 0 .../Tests/Fixtures/copy/A/B/ab.dat.copy | 0 .../finder/Tests/Fixtures/copy/A/a.dat.copy | 0 .../symfony/finder/Tests/Fixtures/dolor.txt | 2 + .../symfony/finder/Tests/Fixtures/ipsum.txt | 2 + .../symfony/finder/Tests/Fixtures/lorem.txt | 2 + vendor/symfony/finder/Tests/Fixtures/one/.dot | 1 + vendor/symfony/finder/Tests/Fixtures/one/a | 0 .../finder/Tests/Fixtures/one/b/c.neon | 0 .../finder/Tests/Fixtures/one/b/d.neon | 0 .../Fixtures/r+e.gex[c]a(r)s/dir/bar.dat | 0 .../finder/Tests/Fixtures/with space/foo.txt | 0 vendor/symfony/finder/Tests/GlobTest.php | 95 + .../Iterator/CustomFilterIteratorTest.php | 46 + .../Iterator/DateRangeFilterIteratorTest.php | 74 + .../Iterator/DepthRangeFilterIteratorTest.php | 83 + .../ExcludeDirectoryFilterIteratorTest.php | 80 + .../Iterator/FileTypeFilterIteratorTest.php | 73 + .../FilecontentFilterIteratorTest.php | 86 + .../Iterator/FilenameFilterIteratorTest.php | 54 + .../finder/Tests/Iterator/Iterator.php | 55 + .../Tests/Iterator/IteratorTestCase.php | 100 + .../Tests/Iterator/MockFileListIterator.php | 21 + .../finder/Tests/Iterator/MockSplFileInfo.php | 132 + .../MultiplePcreFilterIteratorTest.php | 71 + .../Tests/Iterator/PathFilterIteratorTest.php | 82 + .../Tests/Iterator/RealIteratorTestCase.php | 119 + .../RecursiveDirectoryIteratorTest.php | 59 + .../Iterator/SizeRangeFilterIteratorTest.php | 69 + .../Tests/Iterator/SortableIteratorTest.php | 183 ++ vendor/symfony/finder/composer.json | 33 + vendor/symfony/finder/phpunit.xml.dist | 30 + vendor/symfony/framework-bundle/.gitignore | 3 + vendor/symfony/framework-bundle/CHANGELOG.md | 271 ++ .../AbstractPhpFileCacheWarmer.php | 92 + .../CacheWarmer/AnnotationsCacheWarmer.php | 97 + .../CacheWarmer/ClassCacheCacheWarmer.php | 67 + .../CacheWarmer/RouterCacheWarmer.php | 78 + .../CacheWarmer/SerializerCacheWarmer.php | 89 + .../CacheWarmer/TemplateFinder.php | 111 + .../CacheWarmer/TemplateFinderInterface.php | 27 + .../CacheWarmer/TemplatePathsCacheWarmer.php | 62 + .../CacheWarmer/TranslationsCacheWarmer.php | 79 + .../CacheWarmer/ValidatorCacheWarmer.php | 100 + vendor/symfony/framework-bundle/Client.php | 206 ++ .../framework-bundle/Command/AboutCommand.php | 142 + .../Command/AbstractConfigCommand.php | 134 + .../Command/AssetsInstallCommand.php | 291 ++ .../Command/CacheClearCommand.php | 362 +++ .../Command/CachePoolClearCommand.php | 119 + .../Command/CachePoolPruneCommand.php | 71 + .../Command/CacheWarmupCommand.php | 100 + .../Command/ConfigDebugCommand.php | 151 + .../Command/ConfigDumpReferenceCommand.php | 133 + .../Command/ContainerAwareCommand.php | 56 + .../Command/ContainerDebugCommand.php | 258 ++ .../Command/DebugAutowiringCommand.php | 97 + .../Command/EventDispatcherDebugCommand.php | 120 + .../Command/RouterDebugCommand.php | 182 ++ .../Command/RouterMatchCommand.php | 153 + .../Command/TranslationDebugCommand.php | 388 +++ .../Command/TranslationUpdateCommand.php | 328 ++ .../Command/WorkflowDumpCommand.php | 78 + .../Command/XliffLintCommand.php | 70 + .../Command/YamlLintCommand.php | 69 + .../framework-bundle/Console/Application.php | 198 ++ .../Console/Descriptor/Descriptor.php | 282 ++ .../Console/Descriptor/JsonDescriptor.php | 416 +++ .../Console/Descriptor/MarkdownDescriptor.php | 387 +++ .../Console/Descriptor/TextDescriptor.php | 498 +++ .../Console/Descriptor/XmlDescriptor.php | 595 ++++ .../Console/Helper/DescriptorHelper.php | 36 + .../Controller/AbstractController.php | 72 + .../Controller/Controller.php | 42 + .../Controller/ControllerNameParser.php | 152 + .../Controller/ControllerResolver.php | 78 + .../Controller/ControllerTrait.php | 478 +++ .../Controller/RedirectController.php | 177 + .../Controller/TemplateController.php | 94 + .../DataCollector/RequestDataCollector.php | 77 + .../DataCollector/RouterDataCollector.php | 37 + .../AddAnnotationsCachedReaderPass.php | 42 + .../Compiler/AddCacheClearerPass.php | 45 + .../Compiler/AddCacheWarmerPass.php | 48 + .../Compiler/AddConsoleCommandPass.php | 27 + .../Compiler/AddConstraintValidatorsPass.php | 23 + .../Compiler/AddDebugLogProcessorPass.php | 35 + .../AddExpressionLanguageProvidersPass.php | 46 + .../Compiler/AddValidatorInitializersPass.php | 23 + .../Compiler/CacheCollectorPass.php | 66 + .../Compiler/CachePoolClearerPass.php | 41 + .../Compiler/CachePoolPass.php | 148 + .../Compiler/CachePoolPrunerPass.php | 60 + .../Compiler/CompilerDebugDumpPass.php | 46 + .../Compiler/ConfigCachePass.php | 28 + .../ContainerBuilderDebugDumpPass.php | 35 + .../ControllerArgumentValueResolverPass.php | 27 + .../Compiler/DataCollectorTranslatorPass.php | 35 + .../DependencyInjection/Compiler/FormPass.php | 88 + .../Compiler/LoggingTranslatorPass.php | 54 + .../Compiler/ProfilerPass.php | 58 + .../Compiler/PropertyInfoPass.php | 27 + .../Compiler/RoutingResolverPass.php | 27 + .../Compiler/SerializerPass.php | 28 + .../Compiler/TemplatingPass.php | 54 + .../Compiler/TranslationDumperPass.php | 25 + .../Compiler/TranslationExtractorPass.php | 25 + .../Compiler/TranslatorPass.php | 25 + .../Compiler/UnusedTagsPass.php | 91 + .../Compiler/ValidateWorkflowsPass.php | 25 + .../Compiler/WorkflowGuardListenerPass.php | 48 + .../DependencyInjection/Configuration.php | 960 ++++++ .../FrameworkExtension.php | 1731 ++++++++++ .../ResolveControllerNameSubscriber.php | 48 + .../EventListener/SessionListener.php | 43 + .../EventListener/TestSessionListener.php | 43 + .../framework-bundle/FrameworkBundle.php | 150 + .../framework-bundle/HttpCache/HttpCache.php | 79 + .../Kernel/MicroKernelTrait.php | 96 + vendor/symfony/framework-bundle/LICENSE | 19 + vendor/symfony/framework-bundle/README.md | 10 + .../Resources/config/annotations.xml | 57 + .../Resources/config/assets.xml | 54 + .../Resources/config/cache.xml | 114 + .../Resources/config/cache_debug.xml | 15 + .../Resources/config/collectors.xml | 55 + .../Resources/config/console.xml | 117 + .../Resources/config/debug.xml | 28 + .../Resources/config/debug_prod.xml | 31 + .../framework-bundle/Resources/config/esi.xml | 17 + .../Resources/config/form.xml | 199 ++ .../Resources/config/form_csrf.xml | 25 + .../Resources/config/form_debug.xml | 31 + .../Resources/config/fragment_listener.xml | 16 + .../Resources/config/fragment_renderer.xml | 51 + .../Resources/config/identity_translator.xml | 16 + .../Resources/config/lock.xml | 38 + .../Resources/config/profiling.xml | 29 + .../Resources/config/property_access.xml | 17 + .../Resources/config/property_info.xml | 25 + .../Resources/config/request.xml | 15 + .../Resources/config/routing.xml | 126 + .../Resources/config/schema/symfony-1.0.xsd | 326 ++ .../Resources/config/security_csrf.xml | 25 + .../Resources/config/serializer.xml | 129 + .../Resources/config/services.xml | 82 + .../Resources/config/session.xml | 82 + .../framework-bundle/Resources/config/ssi.xml | 17 + .../Resources/config/templating.xml | 52 + .../Resources/config/templating_debug.xml | 19 + .../Resources/config/templating_php.xml | 88 + .../Resources/config/test.xml | 37 + .../Resources/config/translation.xml | 149 + .../Resources/config/translation_debug.xml | 21 + .../Resources/config/validator.xml | 76 + .../Resources/config/validator_debug.xml | 21 + .../framework-bundle/Resources/config/web.xml | 79 + .../Resources/config/web_link.xml | 14 + .../Resources/config/workflow.xml | 31 + .../Resources/views/Form/attributes.html.php | 9 + .../views/Form/button_attributes.html.php | 2 + .../views/Form/button_label.html.php | 0 .../Resources/views/Form/button_row.html.php | 3 + .../views/Form/button_widget.html.php | 4 + .../views/Form/checkbox_widget.html.php | 5 + .../views/Form/choice_attributes.html.php | 8 + .../views/Form/choice_options.html.php | 1 + .../views/Form/choice_widget.html.php | 5 + .../Form/choice_widget_collapsed.html.php | 18 + .../Form/choice_widget_expanded.html.php | 6 + .../views/Form/choice_widget_options.html.php | 13 + .../views/Form/collection_widget.html.php | 4 + .../views/Form/color_widget.html.php | 1 + .../views/Form/container_attributes.html.php | 1 + .../Resources/views/Form/date_widget.html.php | 11 + .../views/Form/datetime_widget.html.php | 7 + .../views/Form/email_widget.html.php | 1 + .../Resources/views/Form/form.html.php | 3 + .../views/Form/form_enctype.html.php | 1 + .../Resources/views/Form/form_end.html.php | 4 + .../Resources/views/Form/form_errors.html.php | 7 + .../Resources/views/Form/form_label.html.php | 8 + .../Resources/views/Form/form_rest.html.php | 5 + .../Resources/views/Form/form_row.html.php | 5 + .../Resources/views/Form/form_rows.html.php | 3 + .../Resources/views/Form/form_start.html.php | 6 + .../Resources/views/Form/form_widget.html.php | 5 + .../views/Form/form_widget_compound.html.php | 7 + .../views/Form/form_widget_simple.html.php | 1 + .../Resources/views/Form/hidden_row.html.php | 1 + .../views/Form/hidden_widget.html.php | 1 + .../views/Form/integer_widget.html.php | 1 + .../views/Form/money_widget.html.php | 1 + .../views/Form/number_widget.html.php | 1 + .../views/Form/password_widget.html.php | 1 + .../views/Form/percent_widget.html.php | 1 + .../views/Form/radio_widget.html.php | 5 + .../views/Form/range_widget.html.php | 1 + .../views/Form/repeated_row.html.php | 1 + .../views/Form/reset_widget.html.php | 1 + .../views/Form/search_widget.html.php | 1 + .../views/Form/submit_widget.html.php | 1 + .../Resources/views/Form/tel_widget.html.php | 1 + .../views/Form/textarea_widget.html.php | 1 + .../Resources/views/Form/time_widget.html.php | 22 + .../Resources/views/Form/url_widget.html.php | 1 + .../views/Form/widget_attributes.html.php | 3 + .../Form/widget_container_attributes.html.php | 2 + .../views/FormTable/button_row.html.php | 6 + .../views/FormTable/form_row.html.php | 9 + .../FormTable/form_widget_compound.html.php | 11 + .../views/FormTable/hidden_row.html.php | 5 + .../AnnotatedRouteControllerLoader.php | 56 + .../Routing/DelegatingLoader.php | 91 + .../Routing/RedirectableUrlMatcher.php | 42 + .../framework-bundle/Routing/Router.php | 177 + .../Templating/DelegatingEngine.php | 74 + .../Templating/EngineInterface.php | 36 + .../Templating/GlobalVariables.php | 94 + .../Templating/Helper/ActionsHelper.php | 62 + .../Templating/Helper/AssetsHelper.php | 67 + .../Templating/Helper/CodeHelper.php | 225 ++ .../Templating/Helper/FormHelper.php | 255 ++ .../Templating/Helper/RequestHelper.php | 72 + .../Templating/Helper/RouterHelper.php | 70 + .../Templating/Helper/SessionHelper.php | 80 + .../Templating/Helper/StopwatchHelper.php | 46 + .../Templating/Helper/TranslatorHelper.php | 52 + .../Templating/Loader/FilesystemLoader.php | 62 + .../Templating/Loader/TemplateLocator.php | 85 + .../framework-bundle/Templating/PhpEngine.php | 78 + .../Templating/TemplateFilenameParser.php | 45 + .../Templating/TemplateNameParser.php | 80 + .../Templating/TemplateReference.php | 57 + .../Templating/TimedPhpEngine.php | 48 + .../framework-bundle/Test/KernelTestCase.php | 231 ++ .../framework-bundle/Test/WebTestCase.php | 40 + .../AnnotationsCacheWarmerTest.php | 103 + .../CacheWarmer/ClassCacheCacheWarmerTest.php | 51 + .../CacheWarmer/SerializerCacheWarmerTest.php | 80 + .../Tests/CacheWarmer/TemplateFinderTest.php | 57 + .../TemplatePathsCacheWarmerTest.php | 102 + .../CacheWarmer/ValidatorCacheWarmerTest.php | 105 + .../framework-bundle/Tests/ClientTest.php | 65 + .../CacheClearCommandTest.php | 92 + .../Fixture/TestAppKernel.php | 43 + .../CacheClearCommand/Fixture/config.yml | 2 + .../Tests/Command/CachePruneCommandTest.php | 109 + .../Tests/Command/RouterDebugCommandTest.php | 125 + .../Tests/Command/RouterMatchCommandTest.php | 127 + .../Command/TranslationDebugCommandTest.php | 258 ++ .../Command/TranslationUpdateCommandTest.php | 248 ++ .../Tests/Command/YamlLintCommandTest.php | 201 ++ .../Tests/Console/ApplicationTest.php | 261 ++ .../Descriptor/AbstractDescriptorTest.php | 251 ++ .../Console/Descriptor/JsonDescriptorTest.php | 27 + .../Descriptor/MarkdownDescriptorTest.php | 27 + .../Console/Descriptor/ObjectsProvider.php | 198 ++ .../Console/Descriptor/TextDescriptorTest.php | 37 + .../Console/Descriptor/XmlDescriptorTest.php | 27 + .../Controller/AbstractControllerTest.php | 63 + .../Controller/ControllerNameParserTest.php | 192 ++ .../Controller/ControllerResolverTest.php | 223 ++ .../Tests/Controller/ControllerTest.php | 27 + .../Tests/Controller/ControllerTraitTest.php | 556 ++++ .../Controller/RedirectControllerTest.php | 321 ++ .../Controller/TemplateControllerTest.php | 90 + .../Compiler/AddCacheWarmerPassTest.php | 66 + .../Compiler/AddConsoleCommandPassTest.php | 129 + .../AddConstraintValidatorsPassTest.php | 75 + ...AddExpressionLanguageProvidersPassTest.php | 97 + .../Compiler/CacheCollectorPassTest.php | 49 + .../Compiler/CachePoolClearerPassTest.php | 61 + .../Compiler/CachePoolPassTest.php | 117 + .../Compiler/CachePoolPrunerPassTest.php | 72 + .../Compiler/ConfigCachePassTest.php | 59 + ...ontrollerArgumentValueResolverPassTest.php | 70 + .../DataCollectorTranslatorPassTest.php | 126 + .../Compiler/FormPassTest.php | 224 ++ .../Compiler/LoggingTranslatorPassTest.php | 81 + .../Compiler/ProfilerPassTest.php | 58 + .../Compiler/PropertyInfoPassTest.php | 72 + .../Compiler/SerializerPassTest.php | 77 + .../Compiler/TranslatorPassTest.php | 53 + .../Compiler/UnusedTagsPassTest.php | 34 + .../WorkflowGuardListenerPassTest.php | 110 + .../DependencyInjection/ConfigurationTest.php | 408 +++ .../Resources/config/validation.xml | 0 .../Resources/config/validation.yml | 0 .../CustomPathBundle/src/CustomPathBundle.php | 22 + .../Resources/config/serialization.xml | 0 .../Resources/config/serialization.yml | 0 .../config/serializer_mapping/files/foo.xml | 0 .../config/serializer_mapping/files/foo.yml | 0 .../serializer_mapping/serialization.yaml | 0 .../serializer_mapping/serialization.yml | 0 .../Resources/config/validation.xml | 0 .../Resources/config/validation.yml | 0 .../config/validation_mapping/files/foo.xml | 0 .../config/validation_mapping/files/foo.yml | 0 .../config/validation_mapping/validation.yaml | 0 .../config/validation_mapping/validation.yml | 0 .../Fixtures/TestBundle/TestBundle.php | 18 + .../Fixtures/php/assets.php | 32 + .../Fixtures/php/assets_disabled.php | 7 + .../assets_version_strategy_as_service.php | 8 + .../Fixtures/php/cache.php | 29 + .../Fixtures/php/cache_env_var.php | 9 + .../DependencyInjection/Fixtures/php/csrf.php | 9 + .../Fixtures/php/csrf_needs_session.php | 7 + .../Fixtures/php/default_config.php | 3 + .../php/esi_and_ssi_without_fragments.php | 13 + .../Fixtures/php/esi_disabled.php | 7 + .../Fixtures/php/form_no_csrf.php | 9 + .../DependencyInjection/Fixtures/php/full.php | 84 + .../Fixtures/php/php_errors_disabled.php | 8 + .../Fixtures/php/php_errors_enabled.php | 8 + .../Fixtures/php/profiler.php | 7 + .../Fixtures/php/property_accessor.php | 8 + .../Fixtures/php/property_info.php | 7 + .../Fixtures/php/request.php | 7 + .../Fixtures/php/serializer_disabled.php | 7 + .../Fixtures/php/serializer_enabled.php | 7 + .../Fixtures/php/serializer_legacy_cache.php | 8 + .../Fixtures/php/serializer_mapping.php | 15 + .../Fixtures/php/session.php | 7 + .../Fixtures/php/ssi_disabled.php | 7 + .../Fixtures/php/templating_disabled.php | 5 + .../Fixtures/php/templating_no_assets.php | 7 + .../php/templating_php_assets_disabled.php | 8 + .../templating_php_translator_disabled.php | 8 + .../php/templating_php_translator_enabled.php | 8 + .../Fixtures/php/translator_fallbacks.php | 7 + .../Fixtures/php/validation_annotations.php | 9 + .../Fixtures/php/validation_mapping.php | 13 + .../validation_multiple_static_methods.php | 9 + .../php/validation_no_static_method.php | 9 + .../Fixtures/php/validation_strict_email.php | 7 + .../php/validation_translation_domain.php | 7 + .../Fixtures/php/web_link.php | 5 + .../workflow_with_arguments_and_service.php | 31 + ...th_multiple_transitions_with_same_name.php | 49 + ...flow_with_support_and_support_strategy.php | 31 + .../php/workflow_with_type_and_service.php | 31 + ...w_without_support_and_support_strategy.php | 27 + .../Fixtures/php/workflows.php | 111 + .../Fixtures/php/workflows_enabled.php | 5 + .../Fixtures/php/workflows_without_type.php | 26 + .../Fixtures/translations/test_paths.en.yml | 2 + .../Fixtures/xml/assets.xml | 27 + .../Fixtures/xml/assets_disabled.xml | 12 + .../assets_version_strategy_as_service.xml | 14 + .../Fixtures/xml/cache.xml | 17 + .../Fixtures/xml/cache_env_var.xml | 17 + .../DependencyInjection/Fixtures/xml/csrf.xml | 14 + .../Fixtures/xml/csrf_disabled.xml | 12 + .../Fixtures/xml/csrf_needs_session.xml | 12 + .../Fixtures/xml/default_config.xml | 9 + .../xml/esi_and_ssi_without_fragments.xml | 13 + .../Fixtures/xml/esi_disabled.xml | 11 + .../xml/form_csrf_sets_field_name.xml | 14 + .../form_csrf_under_form_sets_field_name.xml | 14 + .../Fixtures/xml/form_no_csrf.xml | 14 + .../DependencyInjection/Fixtures/xml/full.xml | 47 + .../DependencyInjection/Fixtures/xml/lock.xml | 11 + .../Fixtures/xml/lock_named.xml | 22 + .../Fixtures/xml/php_errors_disabled.xml | 11 + .../Fixtures/xml/php_errors_enabled.xml | 11 + .../Fixtures/xml/profiler.xml | 12 + .../Fixtures/xml/property_accessor.xml | 12 + .../Fixtures/xml/property_info.xml | 11 + .../Fixtures/xml/request.xml | 11 + .../Fixtures/xml/serializer_disabled.xml | 11 + .../Fixtures/xml/serializer_enabled.xml | 11 + .../Fixtures/xml/serializer_legacy_cache.xml | 11 + .../Fixtures/xml/serializer_mapping.xml | 17 + .../Fixtures/xml/session.xml | 12 + .../Fixtures/xml/ssi_disabled.xml | 11 + .../Fixtures/xml/templating_disabled.xml | 11 + .../Fixtures/xml/templating_no_assets.xml | 14 + .../templating_php_translator_disabled.xml | 14 + .../xml/templating_php_translator_enabled.xml | 14 + .../Fixtures/xml/translator_fallbacks.xml | 15 + .../Fixtures/xml/validation_annotations.xml | 12 + .../Fixtures/xml/validation_mapping.xml | 16 + .../validation_multiple_static_methods.xml | 15 + .../xml/validation_no_static_method.xml | 12 + .../Fixtures/xml/validation_strict_email.xml | 11 + .../xml/validation_translation_domain.xml | 11 + .../Fixtures/xml/web_link.xml | 12 + .../workflow_with_arguments_and_service.xml | 24 + ...th_multiple_transitions_with_same_name.xml | 46 + ...flow_with_support_and_support_strategy.xml | 21 + .../xml/workflow_with_type_and_service.xml | 21 + ...w_without_support_and_support_strategy.xml | 20 + .../Fixtures/xml/workflows.xml | 94 + .../Fixtures/xml/workflows_enabled.xml | 11 + .../Fixtures/xml/workflows_without_type.xml | 21 + .../Fixtures/yml/assets.yml | 21 + .../Fixtures/yml/assets_disabled.yml | 3 + .../assets_version_strategy_as_service.yml | 4 + .../Fixtures/yml/cache.yml | 19 + .../Fixtures/yml/cache_env_var.yml | 6 + .../DependencyInjection/Fixtures/yml/csrf.yml | 5 + .../Fixtures/yml/csrf_needs_session.yml | 2 + .../Fixtures/yml/default_config.yml | 1 + .../yml/esi_and_ssi_without_fragments.yml | 7 + .../Fixtures/yml/esi_disabled.yml | 3 + .../Fixtures/yml/form_no_csrf.yml | 4 + .../DependencyInjection/Fixtures/yml/full.yml | 63 + .../DependencyInjection/Fixtures/yml/lock.yml | 2 + .../Fixtures/yml/lock_named.yml | 9 + .../Fixtures/yml/php_errors_disabled.yml | 3 + .../Fixtures/yml/php_errors_enabled.yml | 4 + .../Fixtures/yml/profiler.yml | 3 + .../Fixtures/yml/property_accessor.yml | 4 + .../Fixtures/yml/property_info.yml | 3 + .../Fixtures/yml/request.yml | 3 + .../Fixtures/yml/serializer_disabled.yml | 3 + .../Fixtures/yml/serializer_enabled.yml | 3 + .../Fixtures/yml/serializer_legacy_cache.yml | 4 + .../Fixtures/yml/serializer_mapping.yml | 10 + .../Fixtures/yml/session.yml | 3 + .../Fixtures/yml/ssi_disabled.yml | 3 + .../Fixtures/yml/templating_disabled.yml | 2 + .../Fixtures/yml/templating_no_assets.yml | 3 + .../yml/templating_php_assets_disabled.yml | 4 + .../templating_php_translator_disabled.yml | 4 + .../yml/templating_php_translator_enabled.yml | 4 + .../Fixtures/yml/translator_fallbacks.yml | 3 + .../Fixtures/yml/validation_annotations.yml | 5 + .../Fixtures/yml/validation_mapping.yml | 7 + .../validation_multiple_static_methods.yml | 5 + .../yml/validation_no_static_method.yml | 5 + .../Fixtures/yml/validation_strict_email.yml | 3 + .../yml/validation_translation_domain.yml | 3 + .../Fixtures/yml/web_link.yml | 3 + .../workflow_with_arguments_and_service.yml | 19 + ...th_multiple_transitions_with_same_name.yml | 33 + ...flow_with_support_and_support_strategy.yml | 17 + .../yml/workflow_with_type_and_service.yml | 17 + ...w_without_support_and_support_strategy.yml | 14 + .../Fixtures/yml/workflows.yml | 80 + .../Fixtures/yml/workflows_enabled.yml | 2 + .../Fixtures/yml/workflows_without_type.yml | 7 + .../FrameworkExtensionTest.php | 1198 +++++++ .../PhpFrameworkExtensionTest.php | 59 + .../XmlFrameworkExtensionTest.php | 30 + .../YamlFrameworkExtensionTest.php | 25 + .../config/serializer/foo.yml | 0 .../config/validator/foo.xml | 0 .../translations/test_default.en.xlf | 0 .../ResolveControllerNameSubscriberTest.php | 63 + .../Tests/Fixtures/BaseBundle/BaseBundle.php | 18 + .../Resources/views/base.format.engine | 0 .../views/controller/base.format.engine | 0 .../views/this.is.a.template.format.engine | 0 .../Tests/Fixtures/DeclaredClass.php | 7 + .../Tests/Fixtures/Descriptor/alias_1.json | 4 + .../Tests/Fixtures/Descriptor/alias_1.md | 2 + .../Tests/Fixtures/Descriptor/alias_1.txt | 3 + .../Tests/Fixtures/Descriptor/alias_1.xml | 2 + .../Tests/Fixtures/Descriptor/alias_2.json | 4 + .../Tests/Fixtures/Descriptor/alias_2.md | 2 + .../Tests/Fixtures/Descriptor/alias_2.txt | 3 + .../Tests/Fixtures/Descriptor/alias_2.xml | 2 + .../Descriptor/alias_with_definition_1.json | 20 + .../Descriptor/alias_with_definition_1.md | 17 + .../Descriptor/alias_with_definition_1.txt | 21 + .../Descriptor/alias_with_definition_1.xml | 5 + .../Descriptor/alias_with_definition_2.json | 41 + .../Descriptor/alias_with_definition_2.md | 25 + .../Descriptor/alias_with_definition_2.txt | 25 + .../Descriptor/alias_with_definition_2.xml | 18 + .../Fixtures/Descriptor/array_parameter.json | 3 + .../Fixtures/Descriptor/array_parameter.md | 4 + .../Fixtures/Descriptor/array_parameter.txt | 5 + .../Fixtures/Descriptor/array_parameter.xml | 2 + .../Descriptor/builder_1_arguments.json | 80 + .../Descriptor/builder_1_arguments.md | 34 + .../Descriptor/builder_1_arguments.txt | 12 + .../Descriptor/builder_1_arguments.xml | 27 + .../Fixtures/Descriptor/builder_1_public.json | 27 + .../Fixtures/Descriptor/builder_1_public.md | 33 + .../Fixtures/Descriptor/builder_1_public.txt | 12 + .../Fixtures/Descriptor/builder_1_public.xml | 8 + .../Descriptor/builder_1_services.json | 66 + .../Fixtures/Descriptor/builder_1_services.md | 59 + .../Descriptor/builder_1_services.txt | 14 + .../Descriptor/builder_1_services.xml | 25 + .../Fixtures/Descriptor/builder_1_tag1.json | 41 + .../Fixtures/Descriptor/builder_1_tag1.md | 26 + .../Fixtures/Descriptor/builder_1_tag1.txt | 11 + .../Fixtures/Descriptor/builder_1_tag1.xml | 19 + .../Fixtures/Descriptor/builder_1_tags.json | 38 + .../Fixtures/Descriptor/builder_1_tags.md | 39 + .../Fixtures/Descriptor/builder_1_tags.txt | 14 + .../Fixtures/Descriptor/builder_1_tags.xml | 19 + .../Tests/Fixtures/Descriptor/callable_1.json | 4 + .../Tests/Fixtures/Descriptor/callable_1.md | 3 + .../Tests/Fixtures/Descriptor/callable_1.txt | 1 + .../Tests/Fixtures/Descriptor/callable_1.xml | 2 + .../Tests/Fixtures/Descriptor/callable_2.json | 6 + .../Tests/Fixtures/Descriptor/callable_2.md | 5 + .../Tests/Fixtures/Descriptor/callable_2.txt | 1 + .../Tests/Fixtures/Descriptor/callable_2.xml | 2 + .../Tests/Fixtures/Descriptor/callable_3.json | 5 + .../Tests/Fixtures/Descriptor/callable_3.md | 4 + .../Tests/Fixtures/Descriptor/callable_3.txt | 1 + .../Tests/Fixtures/Descriptor/callable_3.xml | 2 + .../Tests/Fixtures/Descriptor/callable_4.json | 6 + .../Tests/Fixtures/Descriptor/callable_4.md | 5 + .../Tests/Fixtures/Descriptor/callable_4.txt | 1 + .../Tests/Fixtures/Descriptor/callable_4.xml | 2 + .../Tests/Fixtures/Descriptor/callable_5.json | 7 + .../Tests/Fixtures/Descriptor/callable_5.md | 6 + .../Tests/Fixtures/Descriptor/callable_5.txt | 1 + .../Tests/Fixtures/Descriptor/callable_5.xml | 2 + .../Tests/Fixtures/Descriptor/callable_6.json | 3 + .../Tests/Fixtures/Descriptor/callable_6.md | 2 + .../Tests/Fixtures/Descriptor/callable_6.txt | 1 + .../Tests/Fixtures/Descriptor/callable_6.xml | 2 + .../Tests/Fixtures/Descriptor/callable_7.json | 4 + .../Tests/Fixtures/Descriptor/callable_7.md | 3 + .../Tests/Fixtures/Descriptor/callable_7.txt | 1 + .../Tests/Fixtures/Descriptor/callable_7.xml | 2 + .../Fixtures/Descriptor/definition_1.json | 14 + .../Tests/Fixtures/Descriptor/definition_1.md | 10 + .../Fixtures/Descriptor/definition_1.txt | 17 + .../Fixtures/Descriptor/definition_1.xml | 4 + .../Fixtures/Descriptor/definition_2.json | 35 + .../Tests/Fixtures/Descriptor/definition_2.md | 18 + .../Fixtures/Descriptor/definition_2.txt | 21 + .../Fixtures/Descriptor/definition_2.xml | 17 + .../Descriptor/definition_arguments_1.json | 67 + .../Descriptor/definition_arguments_1.md | 11 + .../Descriptor/definition_arguments_1.txt | 22 + .../Descriptor/definition_arguments_1.xml | 23 + .../Descriptor/definition_arguments_2.json | 36 + .../Descriptor/definition_arguments_2.md | 19 + .../Descriptor/definition_arguments_2.txt | 21 + .../Descriptor/definition_arguments_2.xml | 17 + .../Descriptor/event_dispatcher_1_event1.json | 11 + .../Descriptor/event_dispatcher_1_event1.md | 12 + .../Descriptor/event_dispatcher_1_event1.txt | 11 + .../Descriptor/event_dispatcher_1_event1.xml | 5 + .../Descriptor/event_dispatcher_1_events.json | 20 + .../Descriptor/event_dispatcher_1_events.md | 22 + .../Descriptor/event_dispatcher_1_events.txt | 23 + .../Descriptor/event_dispatcher_1_events.xml | 10 + .../Tests/Fixtures/Descriptor/parameter.json | 3 + .../Tests/Fixtures/Descriptor/parameter.md | 4 + .../Tests/Fixtures/Descriptor/parameter.txt | 6 + .../Tests/Fixtures/Descriptor/parameter.xml | 2 + .../Fixtures/Descriptor/parameters_1.json | 10 + .../Tests/Fixtures/Descriptor/parameters_1.md | 7 + .../Fixtures/Descriptor/parameters_1.txt | 13 + .../Fixtures/Descriptor/parameters_1.xml | 7 + .../Tests/Fixtures/Descriptor/route_1.json | 20 + .../Tests/Fixtures/Descriptor/route_1.md | 15 + .../Tests/Fixtures/Descriptor/route_1.txt | 17 + .../Tests/Fixtures/Descriptor/route_1.xml | 20 + .../Tests/Fixtures/Descriptor/route_2.json | 16 + .../Tests/Fixtures/Descriptor/route_2.md | 13 + .../Tests/Fixtures/Descriptor/route_2.txt | 17 + .../Tests/Fixtures/Descriptor/route_2.xml | 14 + .../Descriptor/route_collection_1.json | 38 + .../Fixtures/Descriptor/route_collection_1.md | 37 + .../Descriptor/route_collection_1.txt | 7 + .../Descriptor/route_collection_1.xml | 35 + .../BaseBundle/views/base.format.engine | 0 .../views/controller/custom.format.engine | 0 .../Resources/translations/messages.fr.yml | 1 + .../Resources/views/resource.format.engine | 0 .../views/this.is.a.template.format.engine | 0 .../Resources/views/translation.html.php | 49 + .../Tests/Fixtures/Serialization/Author.php | 8 + .../Tests/Fixtures/Serialization/Person.php | 8 + .../Serialization/Resources/author.yml | 4 + .../Serialization/Resources/person.xml | 13 + .../TemplatePathsCache/templates-empty.php | 2 + .../Fixtures/TemplatePathsCache/templates.php | 3 + .../Controller/DefaultController.php | 21 + .../Fabpot/FooBundle/FabpotFooBundle.php | 30 + .../Controller/DefaultController.php | 21 + .../Controller/Sub/DefaultController.php | 21 + .../Controller/Test/DefaultController.php | 21 + .../TestBundle/FooBundle/FooBundle.php | 23 + .../Controller/DefaultController.php | 21 + .../Cms/FooBundle/SensioCmsFooBundle.php | 23 + .../Controller/DefaultController.php | 21 + .../Sensio/FooBundle/SensioFooBundle.php | 23 + .../Tests/Fixtures/Validation/Article.php | 8 + .../Tests/Fixtures/Validation/Author.php | 8 + .../Tests/Fixtures/Validation/Category.php | 17 + .../Tests/Fixtures/Validation/Person.php | 8 + .../Fixtures/Validation/Resources/author.yml | 4 + .../Validation/Resources/categories.yml | 9 + .../Fixtures/Validation/Resources/person.xml | 29 + .../Tests/Fixtures/Validation/SubCategory.php | 13 + .../Tests/Fixtures/WarmedClass.php | 7 + .../Tests/Fixtures/templates.php | 5 + .../Functional/AnnotatedControllerTest.php | 39 + .../Tests/Functional/AutowiringTypesTest.php | 86 + .../AutowiringTypes/AutowiredServices.php | 61 + .../Controller/AnnotatedController.php | 51 + .../Controller/FragmentController.php | 57 + .../Controller/ProfilerController.php | 26 + .../Controller/SessionController.php | 81 + .../Controller/SubRequestController.php | 66 + .../SubRequestServiceResolutionController.php | 38 + .../AnnotationReaderPass.php | 24 + .../Config/CustomConfig.php | 31 + .../DependencyInjection/Configuration.php | 37 + .../DependencyInjection/TestExtension.php | 54 + .../TestBundle/Resources/config/routing.yml | 54 + .../Bundle/TestBundle/TestBundle.php | 33 + .../Functional/CachePoolClearCommandTest.php | 102 + .../Tests/Functional/CachePoolsTest.php | 103 + .../Functional/ConfigDebugCommandTest.php | 78 + .../ConfigDumpReferenceCommandTest.php | 85 + .../Functional/ContainerDebugCommandTest.php | 66 + .../Tests/Functional/ContainerDumpTest.php | 32 + .../Functional/DebugAutowiringCommandTest.php | 63 + .../Tests/Functional/FragmentTest.php | 38 + .../Tests/Functional/ProfilerTest.php | 46 + .../Tests/Functional/PropertyInfoTest.php | 35 + .../Tests/Functional/SerializerTest.php | 52 + .../Tests/Functional/SessionTest.php | 153 + .../Tests/Functional/SubRequestsTest.php | 31 + .../Tests/Functional/WebTestCase.php | 73 + .../app/AnnotatedController/bundles.php | 18 + .../app/AnnotatedController/config.yml | 2 + .../app/AnnotatedController/routing.yml | 4 + .../Tests/Functional/app/AppKernel.php | 100 + .../app/AutowiringTypes/bundles.php | 18 + .../Functional/app/AutowiringTypes/config.yml | 11 + .../AutowiringTypes/no_annotations_cache.yml | 6 + .../Functional/app/CachePoolClear/bundles.php | 18 + .../Functional/app/CachePoolClear/config.yml | 22 + .../Functional/app/CachePools/bundles.php | 18 + .../Functional/app/CachePools/config.yml | 14 + .../app/CachePools/redis_config.yml | 17 + .../app/CachePools/redis_custom_config.yml | 28 + .../Functional/app/ConfigDump/bundles.php | 18 + .../Functional/app/ConfigDump/config.yml | 10 + .../Functional/app/ContainerDebug/bundles.php | 18 + .../Functional/app/ContainerDebug/config.yml | 10 + .../Functional/app/ContainerDump/bundles.php | 18 + .../Functional/app/ContainerDump/config.yml | 21 + .../ControllerServiceResolution/bundles.php | 18 + .../ControllerServiceResolution/config.yml | 10 + .../ControllerServiceResolution/routing.yml | 4 + .../Tests/Functional/app/Fragment/bundles.php | 18 + .../Tests/Functional/app/Fragment/config.yml | 7 + .../Tests/Functional/app/Fragment/routing.yml | 2 + .../Tests/Functional/app/Profiler/bundles.php | 18 + .../Tests/Functional/app/Profiler/config.yml | 7 + .../Tests/Functional/app/Profiler/routing.yml | 2 + .../app/Resources/views/fragment.html.php | 14 + .../Functional/app/Serializer/bundles.php | 16 + .../Functional/app/Serializer/config.yml | 10 + .../Tests/Functional/app/Session/bundles.php | 18 + .../Tests/Functional/app/Session/config.yml | 7 + .../Tests/Functional/app/Session/routing.yml | 2 + .../Tests/Functional/app/config/default.yml | 2 + .../Tests/Functional/app/config/framework.yml | 13 + .../Tests/Kernel/ConcreteMicroKernel.php | 103 + .../Tests/Kernel/MicroKernelTraitTest.php | 42 + .../Tests/Routing/DelegatingLoaderTest.php | 20 + .../Routing/RedirectableUrlMatcherTest.php | 61 + .../Tests/Routing/RouterTest.php | 264 ++ .../Tests/Templating/DelegatingEngineTest.php | 125 + .../Tests/Templating/GlobalVariablesTest.php | 106 + .../Templating/Helper/AssetsHelperTest.php | 45 + .../Fixtures/StubTemplateNameParser.php | 43 + .../Helper/Fixtures/StubTranslator.php | 35 + .../Helper/FormHelperDivLayoutTest.php | 154 + .../Helper/FormHelperTableLayoutTest.php | 129 + .../Templating/Helper/RequestHelperTest.php | 54 + .../Resources/Child/form_label.html.php | 1 + .../Custom/_name_c_entry_label.html.php | 2 + .../Custom/_names_entry_label.html.php | 4 + .../Resources/Custom/_text_id_widget.html.php | 3 + .../Resources/Parent/form_label.html.php | 1 + .../Parent/form_widget_simple.html.php | 2 + .../Templating/Helper/SessionHelperTest.php | 75 + .../Templating/Helper/StopwatchHelperTest.php | 39 + .../Templating/Loader/TemplateLocatorTest.php | 98 + .../Tests/Templating/PhpEngineTest.php | 76 + .../Templating/TemplateFilenameParserTest.php | 56 + .../Templating/TemplateNameParserTest.php | 109 + .../Templating/TemplateReferenceTest.php | 28 + .../Tests/Templating/TemplateTest.php | 36 + .../Tests/Templating/TimedPhpEngineTest.php | 116 + .../framework-bundle/Tests/TestCase.php | 18 + .../Tests/Translation/PhpExtractorTest.php | 98 + .../Tests/Translation/TranslatorTest.php | 496 +++ .../ConstraintValidatorFactoryTest.php | 99 + .../Translation/PhpExtractor.php | 23 + .../Translation/PhpStringTokenParser.php | 21 + .../Translation/TranslationLoader.php | 37 + .../Translation/Translator.php | 156 + .../Validator/ConstraintValidatorFactory.php | 84 + vendor/symfony/framework-bundle/composer.json | 98 + .../symfony/framework-bundle/phpunit.xml.dist | 33 + vendor/symfony/http-foundation/.gitignore | 3 + .../symfony/http-foundation/AcceptHeader.php | 173 + .../http-foundation/AcceptHeaderItem.php | 191 ++ .../symfony/http-foundation/ApacheRequest.php | 43 + .../http-foundation/BinaryFileResponse.php | 354 ++ vendor/symfony/http-foundation/CHANGELOG.md | 203 ++ vendor/symfony/http-foundation/Cookie.php | 277 ++ .../Exception/ConflictingHeadersException.php | 21 + .../Exception/RequestExceptionInterface.php | 21 + .../SuspiciousOperationException.php | 20 + .../ExpressionRequestMatcher.php | 47 + .../File/Exception/AccessDeniedException.php | 28 + .../Exception/CannotWriteFileException.php | 21 + .../File/Exception/ExtensionFileException.php | 21 + .../File/Exception/FileException.php | 21 + .../File/Exception/FileNotFoundException.php | 28 + .../File/Exception/FormSizeFileException.php | 21 + .../File/Exception/IniSizeFileException.php | 21 + .../File/Exception/NoFileException.php | 21 + .../File/Exception/NoTmpDirFileException.php | 21 + .../File/Exception/PartialFileException.php | 21 + .../Exception/UnexpectedTypeException.php | 20 + .../File/Exception/UploadException.php | 21 + vendor/symfony/http-foundation/File/File.php | 138 + .../File/MimeType/ExtensionGuesser.php | 94 + .../MimeType/ExtensionGuesserInterface.php | 27 + .../MimeType/FileBinaryMimeTypeGuesser.php | 99 + .../File/MimeType/FileinfoMimeTypeGuesser.php | 69 + .../MimeType/MimeTypeExtensionGuesser.php | 808 +++++ .../File/MimeType/MimeTypeGuesser.php | 133 + .../MimeType/MimeTypeGuesserInterface.php | 35 + .../symfony/http-foundation/File/Stream.php | 28 + .../http-foundation/File/UploadedFile.php | 300 ++ vendor/symfony/http-foundation/FileBag.php | 144 + vendor/symfony/http-foundation/HeaderBag.php | 315 ++ .../symfony/http-foundation/HeaderUtils.php | 174 + vendor/symfony/http-foundation/IpUtils.php | 156 + .../symfony/http-foundation/JsonResponse.php | 204 ++ vendor/symfony/http-foundation/LICENSE | 19 + .../symfony/http-foundation/ParameterBag.php | 234 ++ vendor/symfony/http-foundation/README.md | 14 + .../http-foundation/RedirectResponse.php | 109 + vendor/symfony/http-foundation/Request.php | 2019 ++++++++++++ .../http-foundation/RequestMatcher.php | 178 + .../RequestMatcherInterface.php | 27 + .../symfony/http-foundation/RequestStack.php | 103 + vendor/symfony/http-foundation/Response.php | 1234 +++++++ .../http-foundation/ResponseHeaderBag.php | 339 ++ vendor/symfony/http-foundation/ServerBag.php | 102 + .../Session/Attribute/AttributeBag.php | 148 + .../Attribute/AttributeBagInterface.php | 72 + .../Attribute/NamespacedAttributeBag.php | 159 + .../Session/Flash/AutoExpireFlashBag.php | 161 + .../Session/Flash/FlashBag.php | 152 + .../Session/Flash/FlashBagInterface.php | 93 + .../http-foundation/Session/Session.php | 280 ++ .../Session/SessionBagInterface.php | 46 + .../Session/SessionBagProxy.php | 89 + .../Session/SessionInterface.php | 180 ++ .../Handler/AbstractSessionHandler.php | 154 + .../Handler/MemcachedSessionHandler.php | 122 + .../Handler/MigratingSessionHandler.php | 124 + .../Storage/Handler/MongoDbSessionHandler.php | 193 ++ .../Handler/NativeFileSessionHandler.php | 55 + .../Storage/Handler/NullSessionHandler.php | 76 + .../Storage/Handler/PdoSessionHandler.php | 904 ++++++ .../Storage/Handler/RedisSessionHandler.php | 112 + .../Storage/Handler/StrictSessionHandler.php | 103 + .../Session/Storage/MetadataBag.php | 168 + .../Storage/MockArraySessionStorage.php | 252 ++ .../Storage/MockFileSessionStorage.php | 152 + .../Session/Storage/NativeSessionStorage.php | 432 +++ .../Storage/PhpBridgeSessionStorage.php | 59 + .../Session/Storage/Proxy/AbstractProxy.php | 122 + .../Storage/Proxy/SessionHandlerProxy.php | 101 + .../Storage/SessionStorageInterface.php | 137 + .../http-foundation/StreamedResponse.php | 146 + .../Tests/AcceptHeaderItemTest.php | 113 + .../Tests/AcceptHeaderTest.php | 130 + .../Tests/ApacheRequestTest.php | 93 + .../Tests/BinaryFileResponseTest.php | 366 +++ .../http-foundation/Tests/CookieTest.php | 235 ++ .../Tests/ExpressionRequestMatcherTest.php | 69 + .../http-foundation/Tests/File/FakeFile.php | 45 + .../http-foundation/Tests/File/FileTest.php | 180 ++ .../Tests/File/Fixtures/.unknownextension | 1 + .../Tests/File/Fixtures/directory/.empty | 0 .../Tests/File/Fixtures/other-file.example | 0 .../http-foundation/Tests/File/Fixtures/test | Bin 0 -> 35 bytes .../Tests/File/Fixtures/test.gif | Bin 0 -> 35 bytes .../Tests/File/MimeType/MimeTypeTest.php | 90 + .../Tests/File/UploadedFileTest.php | 346 ++ .../http-foundation/Tests/FileBagTest.php | 178 + .../Fixtures/response-functional/common.inc | 43 + .../cookie_max_age.expected | 11 + .../response-functional/cookie_max_age.php | 10 + .../cookie_raw_urlencode.expected | 10 + .../cookie_raw_urlencode.php | 12 + .../cookie_samesite_lax.expected | 9 + .../cookie_samesite_lax.php | 8 + .../cookie_samesite_strict.expected | 9 + .../cookie_samesite_strict.php | 8 + .../cookie_urlencode.expected | 10 + .../response-functional/cookie_urlencode.php | 12 + .../invalid_cookie_name.expected | 6 + .../invalid_cookie_name.php | 11 + .../http-foundation/Tests/HeaderBagTest.php | 205 ++ .../http-foundation/Tests/HeaderUtilsTest.php | 85 + .../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 | 2221 +++++++++++++ .../Tests/ResponseFunctionalTest.php | 58 + .../Tests/ResponseHeaderBagTest.php | 363 +++ .../http-foundation/Tests/ResponseTest.php | 1045 ++++++ .../Tests/ResponseTestCase.php | 89 + .../http-foundation/Tests/ServerBagTest.php | 170 + .../Session/Attribute/AttributeBagTest.php | 186 ++ .../Attribute/NamespacedAttributeBagTest.php | 204 ++ .../Session/Flash/AutoExpireFlashBagTest.php | 161 + .../Tests/Session/Flash/FlashBagTest.php | 157 + .../Tests/Session/SessionTest.php | 263 ++ .../AbstractRedisSessionHandlerTestCase.php | 145 + .../Handler/AbstractSessionHandlerTest.php | 58 + .../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/MemcachedSessionHandlerTest.php | 135 + .../Handler/MigratingSessionHandlerTest.php | 186 ++ .../Handler/MongoDbSessionHandlerTest.php | 209 ++ .../Handler/NativeFileSessionHandlerTest.php | 76 + .../Handler/NullSessionHandlerTest.php | 59 + .../Storage/Handler/PdoSessionHandlerTest.php | 404 +++ .../PredisClusterSessionHandlerTest.php | 22 + .../Handler/PredisSessionHandlerTest.php | 22 + .../Handler/RedisArraySessionHandlerTest.php | 20 + .../RedisClusterSessionHandlerTest.php | 31 + .../Handler/RedisSessionHandlerTest.php | 23 + .../Handler/StrictSessionHandlerTest.php | 189 ++ .../Tests/Session/Storage/MetadataBagTest.php | 139 + .../Storage/MockArraySessionStorageTest.php | 131 + .../Storage/MockFileSessionStorageTest.php | 127 + .../Storage/NativeSessionStorageTest.php | 294 ++ .../Storage/PhpBridgeSessionStorageTest.php | 95 + .../Storage/Proxy/AbstractProxyTest.php | 113 + .../Storage/Proxy/SessionHandlerProxyTest.php | 157 + .../Tests/StreamedResponseTest.php | 144 + .../Tests/schema/http-status-codes.rng | 31 + .../Tests/schema/iana-registry.rng | 198 ++ vendor/symfony/http-foundation/composer.json | 38 + .../symfony/http-foundation/phpunit.xml.dist | 31 + vendor/symfony/http-kernel/.gitignore | 5 + vendor/symfony/http-kernel/Bundle/Bundle.php | 179 + .../http-kernel/Bundle/BundleInterface.php | 71 + vendor/symfony/http-kernel/CHANGELOG.md | 175 + .../CacheClearer/CacheClearerInterface.php | 27 + .../CacheClearer/ChainCacheClearer.php | 39 + .../CacheClearer/Psr6CacheClearer.php | 49 + .../http-kernel/CacheWarmer/CacheWarmer.php | 32 + .../CacheWarmer/CacheWarmerAggregate.php | 61 + .../CacheWarmer/CacheWarmerInterface.php | 32 + .../CacheWarmer/WarmableInterface.php | 27 + vendor/symfony/http-kernel/Client.php | 205 ++ .../http-kernel/Config/FileLocator.php | 54 + .../Controller/ArgumentResolver.php | 94 + .../ArgumentResolver/DefaultValueResolver.php | 40 + .../RequestAttributeValueResolver.php | 40 + .../ArgumentResolver/RequestValueResolver.php | 40 + .../ArgumentResolver/ServiceValueResolver.php | 68 + .../ArgumentResolver/SessionValueResolver.php | 46 + .../VariadicValueResolver.php | 48 + .../Controller/ArgumentResolverInterface.php | 35 + .../ArgumentValueResolverInterface.php | 43 + .../ContainerControllerResolver.php | 119 + .../Controller/ControllerReference.php | 44 + .../Controller/ControllerResolver.php | 174 + .../ControllerResolverInterface.php | 43 + .../Controller/TraceableArgumentResolver.php | 44 + .../TraceableControllerResolver.php | 44 + .../ControllerMetadata/ArgumentMetadata.php | 107 + .../ArgumentMetadataFactory.php | 71 + .../ArgumentMetadataFactoryInterface.php | 27 + .../DataCollector/AjaxDataCollector.php | 38 + .../DataCollector/ConfigDataCollector.php | 332 ++ .../DataCollector/DataCollector.php | 93 + .../DataCollector/DataCollectorInterface.php | 40 + .../DataCollector/DumpDataCollector.php | 315 ++ .../DataCollector/EventDataCollector.php | 117 + .../DataCollector/ExceptionDataCollector.php | 112 + .../LateDataCollectorInterface.php | 25 + .../DataCollector/LoggerDataCollector.php | 274 ++ .../DataCollector/MemoryDataCollector.php | 120 + .../DataCollector/RequestDataCollector.php | 404 +++ .../DataCollector/RouterDataCollector.php | 108 + .../DataCollector/TimeDataCollector.php | 149 + .../http-kernel/Debug/FileLinkFormatter.php | 105 + .../Debug/TraceableEventDispatcher.php | 82 + .../AddAnnotatedClassesToCachePass.php | 145 + .../ConfigurableExtension.php | 42 + .../ControllerArgumentValueResolverPass.php | 48 + .../DependencyInjection/Extension.php | 44 + .../FragmentRendererPass.php | 63 + .../LazyLoadingFragmentHandler.php | 47 + .../DependencyInjection/LoggerPass.php | 41 + .../MergeExtensionConfigurationPass.php | 41 + ...RegisterControllerArgumentLocatorsPass.php | 179 + ...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 | 91 + .../AbstractTestSessionListener.php | 100 + .../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 | 178 + .../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 | 30 + .../Exception/BadRequestHttpException.php | 29 + .../Exception/ConflictHttpException.php | 29 + .../Exception/GoneHttpException.php | 29 + .../http-kernel/Exception/HttpException.php | 51 + .../Exception/HttpExceptionInterface.php | 34 + .../Exception/LengthRequiredHttpException.php | 29 + .../MethodNotAllowedHttpException.php | 32 + .../Exception/NotAcceptableHttpException.php | 29 + .../Exception/NotFoundHttpException.php | 29 + .../PreconditionFailedHttpException.php | 29 + .../PreconditionRequiredHttpException.php | 31 + .../ServiceUnavailableHttpException.php | 34 + .../TooManyRequestsHttpException.php | 36 + .../Exception/UnauthorizedHttpException.php | 32 + .../UnprocessableEntityHttpException.php | 29 + .../UnsupportedMediaTypeHttpException.php | 29 + .../AbstractSurrogateFragmentRenderer.php | 110 + .../Fragment/EsiFragmentRenderer.php | 28 + .../http-kernel/Fragment/FragmentHandler.php | 112 + .../Fragment/FragmentRendererInterface.php | 42 + .../Fragment/HIncludeFragmentRenderer.php | 160 + .../Fragment/InlineFragmentRenderer.php | 135 + .../Fragment/RoutableFragmentRenderer.php | 90 + .../Fragment/SsiFragmentRenderer.php | 28 + .../HttpCache/AbstractSurrogate.php | 134 + vendor/symfony/http-kernel/HttpCache/Esi.php | 115 + .../http-kernel/HttpCache/HttpCache.php | 684 ++++ .../HttpCache/ResponseCacheStrategy.php | 96 + .../ResponseCacheStrategyInterface.php | 37 + vendor/symfony/http-kernel/HttpCache/Ssi.php | 98 + .../symfony/http-kernel/HttpCache/Store.php | 489 +++ .../http-kernel/HttpCache/StoreInterface.php | 83 + .../HttpCache/SubRequestHandler.php | 87 + .../HttpCache/SurrogateInterface.php | 92 + vendor/symfony/http-kernel/HttpKernel.php | 284 ++ .../http-kernel/HttpKernelInterface.php | 43 + vendor/symfony/http-kernel/Kernel.php | 805 +++++ vendor/symfony/http-kernel/KernelEvents.php | 103 + .../symfony/http-kernel/KernelInterface.php | 161 + vendor/symfony/http-kernel/LICENSE | 19 + .../http-kernel/Log/DebugLoggerInterface.php | 43 + vendor/symfony/http-kernel/Log/Logger.php | 104 + .../Profiler/FileProfilerStorage.php | 290 ++ .../symfony/http-kernel/Profiler/Profile.php | 284 ++ .../symfony/http-kernel/Profiler/Profiler.php | 254 ++ .../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 | 69 + .../CacheClearer/ChainCacheClearerTest.php | 46 + .../CacheClearer/Psr6CacheClearerTest.php | 48 + .../CacheWarmer/CacheWarmerAggregateTest.php | 79 + .../Tests/CacheWarmer/CacheWarmerTest.php | 68 + .../symfony/http-kernel/Tests/ClientTest.php | 179 + .../Tests/Config/FileLocatorTest.php | 48 + .../ServiceValueResolverTest.php | 112 + .../Tests/Controller/ArgumentResolverTest.php | 338 ++ .../ContainerControllerResolverTest.php | 297 ++ .../Controller/ControllerResolverTest.php | 184 ++ .../ArgumentMetadataFactoryTest.php | 139 + .../ArgumentMetadataTest.php | 46 + .../Tests/DataCollector/Compiler.log | 4 + .../DataCollector/ConfigDataCollectorTest.php | 66 + .../Tests/DataCollector/DataCollectorTest.php | 38 + .../DataCollector/DumpDataCollectorTest.php | 138 + .../ExceptionDataCollectorTest.php | 59 + .../DataCollector/LoggerDataCollectorTest.php | 144 + .../DataCollector/MemoryDataCollectorTest.php | 59 + .../RequestDataCollectorTest.php | 334 ++ .../DataCollector/TimeDataCollectorTest.php | 55 + .../Tests/Debug/FileLinkFormatterTest.php | 67 + .../Debug/TraceableEventDispatcherTest.php | 121 + .../AddAnnotatedClassesToCachePassTest.php | 99 + ...ontrollerArgumentValueResolverPassTest.php | 67 + .../FragmentRendererPassTest.php | 71 + .../LazyLoadingFragmentHandlerTest.php | 41 + .../DependencyInjection/LoggerPassTest.php | 56 + .../MergeExtensionConfigurationPassTest.php | 50 + ...sterControllerArgumentLocatorsPassTest.php | 413 +++ ...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 | 221 ++ .../EventListener/SaveSessionListenerTest.php | 49 + .../EventListener/SessionListenerTest.php | 113 + .../EventListener/SurrogateListenerTest.php | 67 + .../EventListener/TestSessionListenerTest.php | 221 ++ .../EventListener/TranslatorListenerTest.php | 118 + .../ValidateRequestListenerTest.php | 48 + .../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 | 37 + .../NotAcceptableHttpExceptionTest.php | 13 + .../Exception/NotFoundHttpExceptionTest.php | 13 + .../PreconditionFailedHttpExceptionTest.php | 13 + .../PreconditionRequiredHttpExceptionTest.php | 13 + .../ServiceUnavailableHttpExceptionTest.php | 42 + .../TooManyRequestsHttpExceptionTest.php | 42 + .../UnauthorizedHttpExceptionTest.php | 37 + .../UnprocessableEntityHttpExceptionTest.php | 13 + .../UnsupportedMediaTypeHttpExceptionTest.php | 13 + .../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 | 106 + .../Tests/Fragment/FragmentHandlerTest.php | 99 + .../Fragment/HIncludeFragmentRendererTest.php | 102 + .../Fragment/InlineFragmentRendererTest.php | 245 ++ .../Fragment/RoutableFragmentRendererTest.php | 94 + .../Fragment/SsiFragmentRendererTest.php | 97 + .../http-kernel/Tests/HttpCache/EsiTest.php | 248 ++ .../Tests/HttpCache/HttpCacheTest.php | 1525 +++++++++ .../Tests/HttpCache/HttpCacheTestCase.php | 185 ++ .../HttpCache/ResponseCacheStrategyTest.php | 240 ++ .../http-kernel/Tests/HttpCache/SsiTest.php | 215 ++ .../http-kernel/Tests/HttpCache/StoreTest.php | 301 ++ .../Tests/HttpCache/SubRequestHandlerTest.php | 153 + .../Tests/HttpCache/TestHttpKernel.php | 102 + .../HttpCache/TestMultipleHttpKernel.php | 81 + .../http-kernel/Tests/HttpKernelTest.php | 387 +++ .../symfony/http-kernel/Tests/KernelTest.php | 763 +++++ .../http-kernel/Tests/Log/LoggerTest.php | 212 ++ vendor/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 | 71 + vendor/symfony/http-kernel/phpunit.xml.dist | 40 + vendor/symfony/inflector/Inflector.php | 232 ++ vendor/symfony/inflector/LICENSE | 19 + vendor/symfony/inflector/README.md | 19 + .../symfony/inflector/Tests/InflectorTest.php | 172 + vendor/symfony/inflector/composer.json | 41 + vendor/symfony/inflector/phpunit.xml.dist | 31 + vendor/symfony/monolog-bridge/.gitignore | 3 + vendor/symfony/monolog-bridge/CHANGELOG.md | 37 + .../Formatter/ConsoleFormatter.php | 205 ++ .../Formatter/VarDumperFormatter.php | 45 + .../Handler/ChromePhpHandler.php | 78 + .../monolog-bridge/Handler/ConsoleHandler.php | 187 ++ .../HttpCodeActivationStrategy.php | 74 + .../NotFoundActivationStrategy.php | 53 + .../monolog-bridge/Handler/FirePHPHandler.php | 80 + .../Handler/ServerLogHandler.php | 120 + .../Handler/SwiftMailerHandler.php | 83 + vendor/symfony/monolog-bridge/LICENSE | 19 + vendor/symfony/monolog-bridge/Logger.php | 78 + .../Processor/DebugProcessor.php | 94 + .../Processor/TokenProcessor.php | 43 + .../monolog-bridge/Processor/WebProcessor.php | 46 + vendor/symfony/monolog-bridge/README.md | 12 + .../Tests/Handler/ConsoleHandlerTest.php | 208 ++ .../HttpCodeActivationStrategyTest.php | 81 + .../NotFoundActivationStrategyTest.php | 55 + .../monolog-bridge/Tests/LoggerTest.php | 110 + .../Tests/Processor/DebugProcessorTest.php | 75 + .../Tests/Processor/TokenProcessorTest.php | 42 + .../Tests/Processor/WebProcessorTest.php | 128 + vendor/symfony/monolog-bridge/composer.json | 51 + .../symfony/monolog-bridge/phpunit.xml.dist | 31 + vendor/symfony/options-resolver/.gitignore | 3 + vendor/symfony/options-resolver/CHANGELOG.md | 52 + .../Debug/OptionsResolverIntrospector.php | 102 + .../Exception/AccessException.php | 22 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../Exception/InvalidOptionsException.php | 23 + .../Exception/MissingOptionsException.php | 23 + .../Exception/NoConfigurationException.php | 26 + .../Exception/NoSuchOptionException.php | 26 + .../Exception/OptionDefinitionException.php | 21 + .../Exception/UndefinedOptionsException.php | 24 + vendor/symfony/options-resolver/LICENSE | 19 + vendor/symfony/options-resolver/Options.php | 22 + .../options-resolver/OptionsResolver.php | 1065 ++++++ vendor/symfony/options-resolver/README.md | 15 + .../Debug/OptionsResolverIntrospectorTest.php | 203 ++ .../Tests/OptionsResolverTest.php | 1736 ++++++++++ vendor/symfony/options-resolver/composer.json | 33 + .../symfony/options-resolver/phpunit.xml.dist | 31 + vendor/symfony/polyfill-ctype/Ctype.php | 227 ++ vendor/symfony/polyfill-ctype/LICENSE | 19 + vendor/symfony/polyfill-ctype/README.md | 12 + vendor/symfony/polyfill-ctype/bootstrap.php | 26 + vendor/symfony/polyfill-ctype/composer.json | 34 + vendor/symfony/polyfill-mbstring/LICENSE | 19 + vendor/symfony/polyfill-mbstring/Mbstring.php | 789 +++++ vendor/symfony/polyfill-mbstring/README.md | 13 + .../Resources/unidata/lowerCase.php | 1101 +++++++ .../Resources/unidata/titleCaseRegexp.php | 5 + .../Resources/unidata/upperCase.php | 1109 +++++++ .../symfony/polyfill-mbstring/bootstrap.php | 58 + .../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 + vendor/symfony/polyfill-php70/bootstrap.php | 27 + vendor/symfony/polyfill-php70/composer.json | 33 + vendor/symfony/property-access/.gitignore | 3 + vendor/symfony/property-access/CHANGELOG.md | 35 + .../Exception/AccessException.php | 21 + .../Exception/ExceptionInterface.php | 21 + .../Exception/InvalidArgumentException.php | 21 + .../InvalidPropertyPathException.php | 21 + .../Exception/NoSuchIndexException.php | 21 + .../Exception/NoSuchPropertyException.php | 21 + .../Exception/OutOfBoundsException.php | 21 + .../Exception/RuntimeException.php | 21 + .../Exception/UnexpectedTypeException.php | 40 + vendor/symfony/property-access/LICENSE | 19 + .../property-access/PropertyAccess.php | 47 + .../property-access/PropertyAccessor.php | 921 ++++++ .../PropertyAccessorBuilder.php | 133 + .../PropertyAccessorInterface.php | 114 + .../symfony/property-access/PropertyPath.php | 205 ++ .../property-access/PropertyPathBuilder.php | 299 ++ .../property-access/PropertyPathInterface.php | 86 + .../property-access/PropertyPathIterator.php | 49 + .../PropertyPathIteratorInterface.php | 34 + vendor/symfony/property-access/README.md | 14 + vendor/symfony/property-access/StringUtil.php | 51 + .../Fixtures/NonTraversableArrayObject.php | 65 + .../Tests/Fixtures/ReturnTyped.php | 36 + .../Tests/Fixtures/TestClass.php | 187 ++ .../Tests/Fixtures/TestClassIsWritable.php | 27 + .../Tests/Fixtures/TestClassMagicCall.php | 37 + .../Tests/Fixtures/TestClassMagicGet.php | 42 + .../Tests/Fixtures/TestClassSetValue.php | 32 + .../Fixtures/TestClassTypeErrorInsideCall.php | 28 + .../Tests/Fixtures/Ticket5775Object.php | 31 + .../Tests/Fixtures/TraversableArrayObject.php | 70 + .../Tests/Fixtures/TypeHinted.php | 51 + .../Tests/PropertyAccessorArrayAccessTest.php | 87 + .../Tests/PropertyAccessorArrayObjectTest.php | 20 + .../Tests/PropertyAccessorArrayTest.php | 20 + .../Tests/PropertyAccessorBuilderTest.php | 66 + .../Tests/PropertyAccessorCollectionTest.php | 200 ++ ...yAccessorNonTraversableArrayObjectTest.php | 22 + .../Tests/PropertyAccessorTest.php | 689 ++++ ...ertyAccessorTraversableArrayObjectTest.php | 22 + .../Tests/PropertyPathBuilderTest.php | 288 ++ .../Tests/PropertyPathTest.php | 206 ++ .../property-access/Tests/StringUtilTest.php | 45 + vendor/symfony/property-access/composer.json | 41 + .../symfony/property-access/phpunit.xml.dist | 31 + vendor/symfony/routing/.gitignore | 3 + vendor/symfony/routing/Annotation/Route.php | 164 + vendor/symfony/routing/CHANGELOG.md | 234 ++ vendor/symfony/routing/CompiledRoute.php | 165 + .../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 | 129 + .../routing/Generator/UrlGenerator.php | 330 ++ .../Generator/UrlGeneratorInterface.php | 86 + vendor/symfony/routing/LICENSE | 19 + .../routing/Loader/AnnotationClassLoader.php | 319 ++ .../Loader/AnnotationDirectoryLoader.php | 93 + .../routing/Loader/AnnotationFileLoader.php | 141 + .../symfony/routing/Loader/ClosureLoader.php | 46 + .../Configurator/CollectionConfigurator.php | 95 + .../Configurator/ImportConfigurator.php | 93 + .../Loader/Configurator/RouteConfigurator.php | 34 + .../Configurator/RoutingConfigurator.php | 62 + .../Loader/Configurator/Traits/AddTrait.php | 90 + .../Loader/Configurator/Traits/RouteTrait.php | 127 + .../ServiceRouterLoader.php | 40 + .../routing/Loader/DirectoryLoader.php | 58 + .../symfony/routing/Loader/GlobFileLoader.php | 47 + .../routing/Loader/ObjectRouteLoader.php | 100 + .../symfony/routing/Loader/PhpFileLoader.php | 75 + .../symfony/routing/Loader/XmlFileLoader.php | 425 +++ .../symfony/routing/Loader/YamlFileLoader.php | 262 ++ .../Loader/schema/routing/routing-1.0.xsd | 162 + .../routing/Matcher/Dumper/MatcherDumper.php | 37 + .../Matcher/Dumper/MatcherDumperInterface.php | 39 + .../Matcher/Dumper/PhpMatcherDumper.php | 778 +++++ .../Matcher/Dumper/StaticPrefixCollection.php | 202 ++ .../Matcher/RedirectableUrlMatcher.php | 64 + .../RedirectableUrlMatcherInterface.php | 31 + .../Matcher/RequestMatcherInterface.php | 39 + .../routing/Matcher/TraceableUrlMatcher.php | 141 + vendor/symfony/routing/Matcher/UrlMatcher.php | 271 ++ .../routing/Matcher/UrlMatcherInterface.php | 41 + vendor/symfony/routing/README.md | 13 + vendor/symfony/routing/RequestContext.php | 326 ++ .../routing/RequestContextAwareInterface.php | 27 + vendor/symfony/routing/Route.php | 571 ++++ vendor/symfony/routing/RouteCollection.php | 297 ++ .../routing/RouteCollectionBuilder.php | 376 +++ vendor/symfony/routing/RouteCompiler.php | 329 ++ .../routing/RouteCompilerInterface.php | 30 + vendor/symfony/routing/Router.php | 388 +++ vendor/symfony/routing/RouterInterface.php | 32 + .../routing/Tests/Annotation/RouteTest.php | 59 + .../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 + .../AbstractClassController.php | 7 + .../ActionPathController.php | 15 + .../DefaultValueController.php | 15 + .../ExplicitLocalizedActionPathController.php | 15 + .../InvokableController.php | 15 + .../InvokableLocalizedController.php | 15 + .../LocalizedActionPathController.php | 15 + .../LocalizedMethodActionControllers.php | 25 + ...calizedPrefixLocalizedActionController.php | 18 + ...zedPrefixMissingLocaleActionController.php | 18 + ...efixMissingRouteLocaleActionController.php | 18 + .../LocalizedPrefixWithRouteWithoutLocale.php | 18 + .../MethodActionControllers.php | 25 + .../MissingRouteNameController.php | 15 + .../NothingButNameController.php | 15 + ...PrefixedActionLocalizedRouteController.php | 18 + .../PrefixedActionPathController.php | 18 + .../RouteWithPrefixController.php | 18 + .../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 + vendor/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 | 35 + .../Tests/Fixtures/dumper/url_matcher1.php | 247 ++ .../Tests/Fixtures/dumper/url_matcher10.php | 2830 ++++++++++++++++ .../Tests/Fixtures/dumper/url_matcher11.php | 151 + .../Tests/Fixtures/dumper/url_matcher12.php | 100 + .../Tests/Fixtures/dumper/url_matcher13.php | 69 + .../Tests/Fixtures/dumper/url_matcher2.php | 284 ++ .../Tests/Fixtures/dumper/url_matcher3.php | 112 + .../Tests/Fixtures/dumper/url_matcher4.php | 84 + .../Tests/Fixtures/dumper/url_matcher5.php | 154 + .../Tests/Fixtures/dumper/url_matcher6.php | 131 + .../Tests/Fixtures/dumper/url_matcher7.php | 166 + .../Tests/Fixtures/dumper/url_matcher8.php | 88 + .../Tests/Fixtures/dumper/url_matcher9.php | 53 + .../symfony/routing/Tests/Fixtures/empty.yml | 0 .../routing/Tests/Fixtures/file_resource.yml | 0 vendor/symfony/routing/Tests/Fixtures/foo.xml | 0 .../symfony/routing/Tests/Fixtures/foo1.xml | 0 .../routing/Tests/Fixtures/glob/bar.xml | 8 + .../routing/Tests/Fixtures/glob/bar.yml | 4 + .../routing/Tests/Fixtures/glob/baz.xml | 8 + .../routing/Tests/Fixtures/glob/baz.yml | 4 + .../Tests/Fixtures/glob/import_multiple.xml | 8 + .../Tests/Fixtures/glob/import_multiple.yml | 2 + .../Tests/Fixtures/glob/import_single.xml | 8 + .../Tests/Fixtures/glob/import_single.yml | 2 + .../routing/Tests/Fixtures/glob/php_dsl.php | 7 + .../Tests/Fixtures/glob/php_dsl_bar.php | 12 + .../Tests/Fixtures/glob/php_dsl_baz.php | 12 + .../import_with_name_prefix/routing.xml | 10 + .../import_with_name_prefix/routing.yml | 7 + .../import_with_no_trailing_slash/routing.xml | 10 + .../import_with_no_trailing_slash/routing.yml | 10 + .../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/localized.xml | 13 + ...imported-with-locale-but-not-localized.xml | 9 + ...imported-with-locale-but-not-localized.yml | 4 + .../localized/imported-with-locale.xml | 11 + .../localized/imported-with-locale.yml | 6 + .../importer-with-controller-default.yml | 5 + ...ith-locale-imports-non-localized-route.xml | 10 + ...ith-locale-imports-non-localized-route.yml | 6 + .../localized/importer-with-locale.xml | 10 + .../localized/importer-with-locale.yml | 6 + .../localized/importing-localized-route.yml | 3 + .../Fixtures/localized/localized-route.yml | 9 + .../localized/missing-locale-in-importer.yml | 5 + .../Fixtures/localized/not-localized.yml | 4 + .../officially_formatted_locales.yml | 7 + .../route-without-path-or-locales.yml | 3 + .../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 | 29 + .../routing/Tests/Fixtures/php_dsl_i18n.php | 17 + .../routing/Tests/Fixtures/php_dsl_sub.php | 15 + .../Tests/Fixtures/php_dsl_sub_i18n.php | 11 + .../Tests/Fixtures/php_dsl_sub_root.php | 10 + .../routing/Tests/Fixtures/php_object_dsl.php | 32 + .../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 | 204 ++ .../Tests/Generator/UrlGeneratorTest.php | 724 +++++ .../Loader/AbstractAnnotationLoaderTest.php | 33 + .../Loader/AnnotationClassLoaderTest.php | 270 ++ .../Loader/AnnotationDirectoryLoaderTest.php | 109 + .../Tests/Loader/AnnotationFileLoaderTest.php | 85 + .../Tests/Loader/ClosureLoaderTest.php | 49 + .../Tests/Loader/DirectoryLoaderTest.php | 74 + .../routing/Tests/Loader/FileLocatorStub.php | 17 + .../Tests/Loader/GlobFileLoaderTest.php | 45 + .../Tests/Loader/ObjectRouteLoaderTest.php | 148 + .../Tests/Loader/PhpFileLoaderTest.php | 169 + .../Tests/Loader/XmlFileLoaderTest.php | 444 +++ .../Tests/Loader/YamlFileLoaderTest.php | 307 ++ .../DumpedRedirectableUrlMatcherTest.php | 43 + .../Tests/Matcher/DumpedUrlMatcherTest.php | 30 + .../Matcher/Dumper/PhpMatcherDumperTest.php | 513 +++ .../Dumper/StaticPrefixCollectionTest.php | 177 + .../Matcher/RedirectableUrlMatcherTest.php | 158 + .../Tests/Matcher/TraceableUrlMatcherTest.php | 122 + .../routing/Tests/Matcher/UrlMatcherTest.php | 687 ++++ .../routing/Tests/RequestContextTest.php | 160 + .../Tests/RouteCollectionBuilderTest.php | 364 +++ .../routing/Tests/RouteCollectionTest.php | 333 ++ .../routing/Tests/RouteCompilerTest.php | 408 +++ vendor/symfony/routing/Tests/RouteTest.php | 274 ++ vendor/symfony/routing/Tests/RouterTest.php | 163 + vendor/symfony/routing/composer.json | 55 + vendor/symfony/routing/phpunit.xml.dist | 30 + vendor/webmozart/assert/.composer-auth.json | 7 + vendor/webmozart/assert/.styleci.yml | 11 + vendor/webmozart/assert/CHANGELOG.md | 53 + vendor/webmozart/assert/LICENSE | 20 + vendor/webmozart/assert/README.md | 252 ++ vendor/webmozart/assert/composer.json | 34 + vendor/webmozart/assert/src/Assert.php | 1087 +++++++ vendor/winzou/state-machine/.gitignore | 3 + vendor/winzou/state-machine/.travis.yml | 35 + vendor/winzou/state-machine/LICENSE | 19 + vendor/winzou/state-machine/README.md | 107 + vendor/winzou/state-machine/composer.json | 37 + .../state-machine/examples/DomainObject.php | 41 + .../winzou/state-machine/examples/simple.php | 109 + .../spec/SM/Callback/CallbackSpec.php | 163 + .../CascadeTransitionCallbackSpec.php | 72 + .../state-machine/spec/SM/DummyObject.php | 16 + .../SM/Extension/Twig/SMExtensionSpec.php | 57 + .../spec/SM/Factory/FactorySpec.php | 44 + .../spec/SM/StateMachine/StateMachineSpec.php | 207 ++ .../src/SM/Callback/Callback.php | 144 + .../src/SM/Callback/CallbackFactory.php | 50 + .../SM/Callback/CallbackFactoryInterface.php | 24 + .../src/SM/Callback/CallbackInterface.php | 35 + .../SM/Callback/CascadeTransitionCallback.php | 64 + .../state-machine/src/SM/Event/SMEvents.php | 19 + .../src/SM/Event/TransitionEvent.php | 105 + .../src/SM/Extension/Twig/SMExtension.php | 84 + .../src/SM/Factory/AbstractFactory.php | 104 + .../SM/Factory/ClearableFactoryInterface.php | 20 + .../state-machine/src/SM/Factory/Factory.php | 59 + .../src/SM/Factory/FactoryInterface.php | 27 + .../state-machine/src/SM/SMException.php | 16 + .../src/SM/StateMachine/StateMachine.php | 235 ++ .../SM/StateMachine/StateMachineInterface.php | 68 + vendor/zendframework/zend-stdlib/CHANGELOG.md | 385 +++ vendor/zendframework/zend-stdlib/LICENSE.md | 27 + vendor/zendframework/zend-stdlib/README.md | 29 + .../zendframework/zend-stdlib/composer.json | 56 + .../zend-stdlib/src/AbstractOptions.php | 178 + .../zend-stdlib/src/ArrayObject.php | 434 +++ .../src/ArraySerializableInterface.php | 28 + .../zend-stdlib/src/ArrayStack.php | 33 + .../zend-stdlib/src/ArrayUtils.php | 314 ++ .../src/ArrayUtils/MergeRemoveKey.php | 14 + .../src/ArrayUtils/MergeReplaceKey.php | 34 + .../ArrayUtils/MergeReplaceKeyInterface.php | 21 + .../zend-stdlib/src/ConsoleHelper.php | 158 + .../zend-stdlib/src/DispatchableInterface.php | 22 + .../zend-stdlib/src/ErrorHandler.php | 115 + .../src/Exception/BadMethodCallException.php | 17 + .../src/Exception/DomainException.php | 17 + .../src/Exception/ExceptionInterface.php | 17 + .../Exception/ExtensionNotLoadedException.php | 17 + .../Exception/InvalidArgumentException.php | 17 + .../src/Exception/LogicException.php | 17 + .../src/Exception/RuntimeException.php | 17 + .../zend-stdlib/src/FastPriorityQueue.php | 371 +++ vendor/zendframework/zend-stdlib/src/Glob.php | 202 ++ .../zend-stdlib/src/Guard/AllGuardsTrait.php | 20 + .../Guard/ArrayOrTraversableGuardTrait.php | 41 + .../zend-stdlib/src/Guard/EmptyGuardTrait.php | 35 + .../zend-stdlib/src/Guard/NullGuardTrait.php | 35 + .../src/InitializableInterface.php | 23 + .../zend-stdlib/src/JsonSerializable.php | 17 + .../zendframework/zend-stdlib/src/Message.php | 118 + .../zend-stdlib/src/MessageInterface.php | 44 + .../src/ParameterObjectInterface.php | 38 + .../zend-stdlib/src/Parameters.php | 115 + .../zend-stdlib/src/ParametersInterface.php | 86 + .../zend-stdlib/src/PriorityList.php | 274 ++ .../zend-stdlib/src/PriorityQueue.php | 301 ++ .../zendframework/zend-stdlib/src/Request.php | 15 + .../zend-stdlib/src/RequestInterface.php | 14 + .../zend-stdlib/src/Response.php | 15 + .../zend-stdlib/src/ResponseInterface.php | 14 + .../zend-stdlib/src/SplPriorityQueue.php | 93 + .../zend-stdlib/src/SplQueue.php | 55 + .../zend-stdlib/src/SplStack.php | 55 + .../zend-stdlib/src/StringUtils.php | 187 ++ .../StringWrapper/AbstractStringWrapper.php | 269 ++ .../zend-stdlib/src/StringWrapper/Iconv.php | 289 ++ .../zend-stdlib/src/StringWrapper/Intl.php | 88 + .../src/StringWrapper/MbString.php | 121 + .../zend-stdlib/src/StringWrapper/Native.php | 134 + .../StringWrapper/StringWrapperInterface.php | 111 + 3713 files changed, 319385 insertions(+) create mode 100644 .gitattributes create mode 100644 AbstractExampleFactory.php create mode 100644 AbstractResourceFixture.php create mode 100644 ExampleFactoryInterface.php create mode 100644 LICENSE create mode 100644 LocaleAwareFactoryTrait.php create mode 100644 OptionsResolver/LazyOption.php create mode 100644 README.md create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 vendor/autoload.php create mode 100644 vendor/behat/transliterator/CHANGELOG.md create mode 100644 vendor/behat/transliterator/CONTRIBUTING.md create mode 100644 vendor/behat/transliterator/LICENSE create mode 100644 vendor/behat/transliterator/README.md create mode 100644 vendor/behat/transliterator/composer.json create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x02.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x04.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x05.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x06.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x07.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x09.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x0a.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x0b.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x0c.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x0d.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x0e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x0f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x10.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x11.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x12.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x13.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x14.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x15.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x16.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x17.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x18.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x1e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x1f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x20.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x21.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x24.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x25.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x26.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x27.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x28.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x30.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x31.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x32.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x33.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x4e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x4f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x50.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x51.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x52.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x53.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x54.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x55.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x56.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x57.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x58.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x59.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x5a.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x5b.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x5c.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x5d.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x5e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x5f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x60.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x61.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x62.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x63.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x64.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x65.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x66.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x67.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x68.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x69.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x6a.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x6b.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x6c.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x6d.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x6e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x6f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x70.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x71.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x72.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x73.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x74.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x75.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x76.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x77.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x78.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x79.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x7a.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x7b.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x7c.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x7d.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x7e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x7f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x80.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x81.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x82.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x83.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x84.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x85.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x86.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x87.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x88.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x89.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x8a.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x8b.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x8c.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x8d.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x8e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x8f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x90.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x91.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x92.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x93.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x94.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x95.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x96.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x97.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x98.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x99.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x9a.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x9b.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x9c.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x9d.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x9e.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/x9f.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xa0.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xa1.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xa2.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xa3.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xa4.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xac.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xad.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xae.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xaf.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb0.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb1.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb2.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb3.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb4.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb5.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb6.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb7.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb8.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xb9.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xba.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xbb.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xbc.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xbd.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xbe.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xbf.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc0.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc1.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc2.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc3.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc4.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc5.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc6.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc7.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc8.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xc9.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xca.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xcb.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xcc.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xcd.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xce.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xcf.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd0.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd1.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd2.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd3.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd4.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd5.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd6.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xd7.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xf9.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xfa.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xfb.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xfc.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xfd.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xfe.php create mode 100644 vendor/behat/transliterator/src/Behat/Transliterator/data/xff.php create mode 100644 vendor/composer/ClassLoader.php create mode 100644 vendor/composer/LICENSE create mode 100644 vendor/composer/autoload_classmap.php create mode 100644 vendor/composer/autoload_files.php create mode 100644 vendor/composer/autoload_namespaces.php create mode 100644 vendor/composer/autoload_psr4.php create mode 100644 vendor/composer/autoload_real.php create mode 100644 vendor/composer/autoload_static.php create mode 100644 vendor/composer/installed.json create mode 100644 vendor/doctrine/annotations/CHANGELOG.md create mode 100644 vendor/doctrine/annotations/LICENSE create mode 100644 vendor/doctrine/annotations/README.md create mode 100644 vendor/doctrine/annotations/composer.json create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php create mode 100644 vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php create mode 100644 vendor/doctrine/annotations/phpstan.neon create mode 100644 vendor/doctrine/cache/LICENSE create mode 100644 vendor/doctrine/cache/README.md create mode 100644 vendor/doctrine/cache/UPGRADE.md create mode 100644 vendor/doctrine/cache/composer.json create mode 100644 vendor/doctrine/cache/docs/en/index.rst create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseBucketCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/LegacyMongoDBCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiGetCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiOperationCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiPutCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/VoidCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/WinCacheCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php create mode 100644 vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php create mode 100644 vendor/doctrine/collections/CONTRIBUTING.md create mode 100644 vendor/doctrine/collections/LICENSE create mode 100644 vendor/doctrine/collections/README.md create mode 100644 vendor/doctrine/collections/composer.json create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php create mode 100644 vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php create mode 100644 vendor/doctrine/common/LICENSE create mode 100644 vendor/doctrine/common/README.md create mode 100644 vendor/doctrine/common/UPGRADE_TO_2_1 create mode 100644 vendor/doctrine/common/UPGRADE_TO_2_2 create mode 100644 vendor/doctrine/common/composer.json create mode 100644 vendor/doctrine/common/docs/en/index.rst create mode 100644 vendor/doctrine/common/docs/en/reference/class-loading.rst create mode 100644 vendor/doctrine/common/humbug.json.dist create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/ClassLoader.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/CommonException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Comparable.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Lexer.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php create mode 100644 vendor/doctrine/common/lib/Doctrine/Common/Version.php create mode 100644 vendor/doctrine/common/phpstan.neon create mode 100644 vendor/doctrine/data-fixtures/.gitignore create mode 100644 vendor/doctrine/data-fixtures/.travis.yml create mode 100644 vendor/doctrine/data-fixtures/CHANGELOG.md create mode 100644 vendor/doctrine/data-fixtures/LICENSE create mode 100644 vendor/doctrine/data-fixtures/README.md create mode 100644 vendor/doctrine/data-fixtures/UPGRADE create mode 100644 vendor/doctrine/data-fixtures/composer.json create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/AbstractFixture.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/DependentFixtureInterface.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/MongoDBReferenceListener.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/ORMReferenceListener.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Exception/CircularReferenceException.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/AbstractExecutor.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/MongoDBExecutor.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/FixtureInterface.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Loader.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/OrderedFixtureInterface.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/MongoDBPurger.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/ORMPurger.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PurgerInterface.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ReferenceRepository.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/SharedFixtureInterface.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/TopologicalSorter.php create mode 100644 vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/Vertex.php create mode 100644 vendor/doctrine/data-fixtures/phpunit.xml.dist create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/BaseTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/DependentFixtureTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorSharedFixtureTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/PHPCRExecutorTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/FixtureTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/LoaderTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/OrderedFixtureTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ProxyReferenceRepositoryTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/MongoDBPurgerTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerExcludeTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ReferenceRepositoryTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/TopologicalSorterTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/VertexTest.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestDocument/Role.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/Quoted.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/Role.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/User.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/UserWithSchema.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/MyFixture1.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/MyFixture2.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/NotAFixture.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/RoleFixture.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/UserFixture.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/ExcludedEntity.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/IncludedEntity.php create mode 100644 vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Mock/Node.php create mode 100644 vendor/doctrine/event-manager/.scrutinizer.yml create mode 100644 vendor/doctrine/event-manager/LICENSE create mode 100644 vendor/doctrine/event-manager/README.md create mode 100644 vendor/doctrine/event-manager/composer.json create mode 100644 vendor/doctrine/event-manager/docs/en/index.rst create mode 100644 vendor/doctrine/event-manager/docs/en/reference/index.rst create mode 100644 vendor/doctrine/event-manager/docs/en/sidebar.rst create mode 100644 vendor/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php create mode 100644 vendor/doctrine/event-manager/lib/Doctrine/Common/EventManager.php create mode 100644 vendor/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php create mode 100644 vendor/doctrine/inflector/LICENSE create mode 100644 vendor/doctrine/inflector/README.md create mode 100644 vendor/doctrine/inflector/composer.json create mode 100644 vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php create mode 100644 vendor/doctrine/lexer/LICENSE create mode 100644 vendor/doctrine/lexer/README.md create mode 100644 vendor/doctrine/lexer/composer.json create mode 100644 vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php create mode 100644 vendor/doctrine/persistence/.scrutinizer.yml create mode 100644 vendor/doctrine/persistence/LICENSE create mode 100644 vendor/doctrine/persistence/README.md create mode 100644 vendor/doctrine/persistence/composer.json create mode 100644 vendor/doctrine/persistence/docs/en/index.rst create mode 100644 vendor/doctrine/persistence/docs/en/reference/index.rst create mode 100644 vendor/doctrine/persistence/docs/en/sidebar.rst create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ConnectionRegistry.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/LifecycleEventArgs.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ManagerRegistry.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/AbstractClassMetadataFactory.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/ClassMetadataFactory.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/AnnotationDriver.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriver.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/MappingDriverChain.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/MappingException.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/ReflectionService.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectManager.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectManagerAware.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectManagerDecorator.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectRepository.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/PersistentObject.php create mode 100644 vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Proxy.php create mode 100644 vendor/doctrine/persistence/phpstan.neon create mode 100644 vendor/doctrine/reflection/.scrutinizer.yml create mode 100644 vendor/doctrine/reflection/LICENSE create mode 100644 vendor/doctrine/reflection/README.md create mode 100644 vendor/doctrine/reflection/composer.json create mode 100644 vendor/doctrine/reflection/docs/en/index.rst create mode 100644 vendor/doctrine/reflection/docs/en/reference/index.rst create mode 100644 vendor/doctrine/reflection/docs/en/sidebar.rst create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ClassFinderInterface.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/Psr0FindFile.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/RuntimePublicReflectionProperty.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionClass.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionParser.php create mode 100644 vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php create mode 100644 vendor/doctrine/reflection/phpstan.neon create mode 100644 vendor/gedmo/doctrine-extensions/.gitattributes create mode 100644 vendor/gedmo/doctrine-extensions/.gitignore create mode 100644 vendor/gedmo/doctrine-extensions/.travis.yml create mode 100644 vendor/gedmo/doctrine-extensions/LICENSE create mode 100644 vendor/gedmo/doctrine-extensions/README.md create mode 100644 vendor/gedmo/doctrine-extensions/composer.json create mode 100644 vendor/gedmo/doctrine-extensions/composer7.json create mode 100644 vendor/gedmo/doctrine-extensions/doc/annotations.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/blameable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/ip_traceable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/loggable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/mapping.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/reference_integrity.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/references.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/sluggable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/softdeleteable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/sortable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/symfony2.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/timestampable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/transaction-safety.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/translatable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/tree.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/uploadable.md create mode 100644 vendor/gedmo/doctrine-extensions/doc/zendframework2.md create mode 100644 vendor/gedmo/doctrine-extensions/example/app/Entity/Category.php create mode 100644 vendor/gedmo/doctrine-extensions/example/app/Entity/CategoryTranslation.php create mode 100644 vendor/gedmo/doctrine-extensions/example/app/Entity/Repository/CategoryRepository.php create mode 100755 vendor/gedmo/doctrine-extensions/example/bin/console create mode 100644 vendor/gedmo/doctrine-extensions/example/bin/console.php create mode 100644 vendor/gedmo/doctrine-extensions/example/em.php create mode 100644 vendor/gedmo/doctrine-extensions/example/run.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/AbstractTrackingListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Blameable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/BlameableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/Blameable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableDocument.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableEntity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/DoctrineExtensions.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/BadMethodCallException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/FeatureNotImplementedException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidArgumentException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidMappingException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/ReferenceIntegrityStrictException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/RuntimeException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/TreeLockingException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnexpectedValueException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnsupportedObjectManagerException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCantWriteException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCouldntGuessMimeTypeException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableDirectoryNotFoundException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableExtensionException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileNotReadableException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFormSizeException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableIniSizeException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidFileException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidMimeTypeException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidPathException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableMaxSizeException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoFileException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoPathDefinedException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoTmpDirException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadablePartialException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableUploadException.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/IpTraceableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableDocument.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableEntity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/LogEntry.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/MappedSuperclass/AbstractLogEntry.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/LogEntry.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass/AbstractLogEntry.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Loggable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/LoggableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/LoggableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/All.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Blameable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/IpTraceable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Language.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Locale.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Loggable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Reference.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceIntegrity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceMany.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceManyEmbed.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceOne.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Slug.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandler.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandlerOption.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SoftDeleteable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortableGroup.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortablePosition.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Timestampable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Translatable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TranslationEntity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Tree.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeClosure.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLeft.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLevel.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLockTime.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeParent.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePath.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathHash.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathSource.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRight.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRoot.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Uploadable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileMimeType.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileName.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFilePath.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileSize.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Versioned.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AbstractAnnotationDriver.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AnnotationDriverInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Chain.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/File.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/AdapterInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/ExtensionMetadataFactory.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/MappedEventSubscriber.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Validator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrityListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/LazyCollection.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/ReferencesAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/References/ReferencesListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/InversedRelativeSlugHandler.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/RelativeSlugHandler.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerWithUniqueCallbackInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/TreeSlugHandler.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/SluggableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Sluggable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/SluggableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Util/Urlizer.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Filter/ODM/SoftDeleteableFilter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Filter/SoftDeleteableFilter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/SoftDeleteableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Validator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/Exec/MultiTableDeleteExecutor.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/SoftDeleteableWalker.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableDocument.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableEntity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Entity/Repository/SortableRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/SortableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Sortable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/TimestampableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Timestampable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/TimestampableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/Timestampable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableDocument.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableEntity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Logging/DBAL/QueryAnalyzer.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/AbstractWrapper.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/EntityWrapper.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/MongoDocumentWrapper.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/WrapperInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractPersonalTranslation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractTranslation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Repository/TranslationRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Translation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass/AbstractPersonalTranslation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass/AbstractTranslation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Translation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/ObjectHydrator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/TranslatableAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Translatable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Document/Translation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity/Translation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Translation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationProxy.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/AbstractTreeRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/MaterializedPathRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass/AbstractClosure.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/AbstractTreeRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/ClosureTreeRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/MaterializedPathRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Hydrator/ORM/TreeObjectHydrator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ODM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ORM.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/TreeAdapter.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Validator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Node.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtils.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtilsInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/AbstractMaterializedPath.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ODM/MongoDB/MaterializedPath.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Closure.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/MaterializedPath.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Nested.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/MaterializedPath.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSet.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntity.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntityUuid.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/TreeListener.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadableBaseEventArgs.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePostFileProcessEventArgs.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePreFileProcessEventArgs.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Events.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoArray.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumeric.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorSha1.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Annotation.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Xml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Yaml.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Validator.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesser.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesserInterface.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypesExtensionsMap.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Uploadable.php create mode 100644 vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/UploadableListener.php create mode 100644 vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-1.xsd create mode 100644 vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-2.xsd create mode 100644 vendor/monolog/monolog/.php_cs create mode 100644 vendor/monolog/monolog/CHANGELOG.md create mode 100644 vendor/monolog/monolog/LICENSE create mode 100644 vendor/monolog/monolog/README.md create mode 100644 vendor/monolog/monolog/composer.json create mode 100644 vendor/monolog/monolog/doc/01-usage.md create mode 100644 vendor/monolog/monolog/doc/02-handlers-formatters-processors.md create mode 100644 vendor/monolog/monolog/doc/03-utilities.md create mode 100644 vendor/monolog/monolog/doc/04-extending.md create mode 100644 vendor/monolog/monolog/doc/sockets.md create mode 100644 vendor/monolog/monolog/phpunit.xml.dist create mode 100644 vendor/monolog/monolog/src/Monolog/ErrorHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SlackbotHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php create mode 100644 vendor/monolog/monolog/src/Monolog/Logger.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php create mode 100644 vendor/monolog/monolog/src/Monolog/Registry.php create mode 100644 vendor/monolog/monolog/tests/Monolog/ErrorHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/ChromePHPFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/ElasticaFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/FlowdockFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/LogglyFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/MongoDBFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/ScalarFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AbstractProcessingHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/CouchDBHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/DeduplicationHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/DoctrineCouchDBHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/DynamoDbHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/ElasticSearchHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/ErrorLogHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FilterHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/Fixtures/.gitkeep create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FleepHookHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/FlowdockHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerLegacyTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GelfMockMessagePublisher.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/HandlerWrapperTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/HipChatHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/LogEntriesHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/MailHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/MockRavenClient.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/MongoDBHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/NewRelicHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/NullHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/PHPConsoleHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/PsrHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RollbarHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SamplingHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SlackHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SlackWebhookHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SlackbotHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SwiftMailerHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SyslogHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/SyslogUdpHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/UdpSocketTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Handler/ZendMonitorHandlerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/LoggerTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/GitProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/IntrospectionProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/MemoryPeakUsageProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/MemoryUsageProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/MercurialProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/ProcessIdProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/PsrLogMessageProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/TagProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/UidProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/Processor/WebProcessorTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/PsrLogCompatTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/RegistryTest.php create mode 100644 vendor/monolog/monolog/tests/Monolog/TestCase.php create mode 100644 vendor/pagerfanta/pagerfanta/CHANGELOG.md create mode 100644 vendor/pagerfanta/pagerfanta/LICENSE create mode 100644 vendor/pagerfanta/pagerfanta/README.md create mode 100644 vendor/pagerfanta/pagerfanta/code_of_conduct.md create mode 100644 vendor/pagerfanta/pagerfanta/composer.json create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/AdapterInterface.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ArrayAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/CallbackAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ConcatenationAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineCollectionAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalSingleTableAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMPhpcrAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineORMAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineSelectableAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ElasticaAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/FixedAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MandangoAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MongoAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/NullAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/Propel2Adapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/PropelAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/SolariumAdapter.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/Exception.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/InvalidArgumentException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1CurrentPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1MaxPerPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LogicException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotBooleanException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerCurrentPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerMaxPerPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidCurrentPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidMaxPerPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/OutOfRangeCurrentPageException.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/Pagerfanta.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/PagerfantaInterface.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/DefaultView.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/OptionableView.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/SemanticUiView.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/DefaultTemplate.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/SemanticUiTemplate.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/Template.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TemplateInterface.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap3Template.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap4Template.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrapTemplate.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap3View.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap4View.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrapView.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactory.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactoryInterface.php create mode 100644 vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewInterface.php create mode 100644 vendor/paragonie/random_compat/LICENSE create mode 100755 vendor/paragonie/random_compat/build-phar.sh create mode 100644 vendor/paragonie/random_compat/composer.json create mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey create mode 100644 vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc create mode 100644 vendor/paragonie/random_compat/lib/random.php create mode 100644 vendor/paragonie/random_compat/other/build_phar.php create mode 100644 vendor/paragonie/random_compat/psalm-autoload.php create mode 100644 vendor/paragonie/random_compat/psalm.xml create mode 100644 vendor/psr/cache/CHANGELOG.md create mode 100644 vendor/psr/cache/LICENSE.txt create mode 100644 vendor/psr/cache/README.md create mode 100644 vendor/psr/cache/composer.json create mode 100644 vendor/psr/cache/src/CacheException.php create mode 100644 vendor/psr/cache/src/CacheItemInterface.php create mode 100644 vendor/psr/cache/src/CacheItemPoolInterface.php create mode 100644 vendor/psr/cache/src/InvalidArgumentException.php create mode 100644 vendor/psr/container/.gitignore create mode 100644 vendor/psr/container/LICENSE create mode 100644 vendor/psr/container/README.md create mode 100644 vendor/psr/container/composer.json create mode 100644 vendor/psr/container/src/ContainerExceptionInterface.php create mode 100644 vendor/psr/container/src/ContainerInterface.php create mode 100644 vendor/psr/container/src/NotFoundExceptionInterface.php create mode 100644 vendor/psr/log/.gitignore create mode 100644 vendor/psr/log/LICENSE create mode 100644 vendor/psr/log/Psr/Log/AbstractLogger.php create mode 100644 vendor/psr/log/Psr/Log/InvalidArgumentException.php create mode 100644 vendor/psr/log/Psr/Log/LogLevel.php create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareInterface.php create mode 100644 vendor/psr/log/Psr/Log/LoggerAwareTrait.php create mode 100644 vendor/psr/log/Psr/Log/LoggerInterface.php create mode 100644 vendor/psr/log/Psr/Log/LoggerTrait.php create mode 100644 vendor/psr/log/Psr/Log/NullLogger.php create mode 100644 vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php create mode 100644 vendor/psr/log/README.md create mode 100644 vendor/psr/log/composer.json create mode 100644 vendor/psr/simple-cache/.editorconfig create mode 100644 vendor/psr/simple-cache/LICENSE.md create mode 100644 vendor/psr/simple-cache/README.md create mode 100644 vendor/psr/simple-cache/composer.json create mode 100644 vendor/psr/simple-cache/src/CacheException.php create mode 100644 vendor/psr/simple-cache/src/CacheInterface.php create mode 100644 vendor/psr/simple-cache/src/InvalidArgumentException.php create mode 100644 vendor/sylius/fixtures-bundle/.gitignore create mode 100644 vendor/sylius/fixtures-bundle/Command/FixturesListCommand.php create mode 100644 vendor/sylius/fixtures-bundle/Command/FixturesLoadCommand.php create mode 100644 vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/FixtureRegistryPass.php create mode 100644 vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/ListenerRegistryPass.php create mode 100644 vendor/sylius/fixtures-bundle/DependencyInjection/Configuration.php create mode 100644 vendor/sylius/fixtures-bundle/DependencyInjection/SyliusFixturesExtension.php create mode 100644 vendor/sylius/fixtures-bundle/Fixture/AbstractFixture.php create mode 100644 vendor/sylius/fixtures-bundle/Fixture/FixtureInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Fixture/FixtureNotFoundException.php create mode 100644 vendor/sylius/fixtures-bundle/Fixture/FixtureRegistry.php create mode 100644 vendor/sylius/fixtures-bundle/Fixture/FixtureRegistryInterface.php create mode 100644 vendor/sylius/fixtures-bundle/LICENSE create mode 100644 vendor/sylius/fixtures-bundle/Listener/AbstractListener.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/AfterFixtureListenerInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/AfterSuiteListenerInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/BeforeFixtureListenerInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/BeforeSuiteListenerInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/FixtureEvent.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/ListenerInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/ListenerNotFoundException.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/ListenerRegistry.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/ListenerRegistryInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/LoggerListener.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/MongoDBPurgerListener.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/ORMPurgerListener.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/PHPCRPurgerListener.php create mode 100644 vendor/sylius/fixtures-bundle/Listener/SuiteEvent.php create mode 100644 vendor/sylius/fixtures-bundle/Loader/FixtureLoader.php create mode 100644 vendor/sylius/fixtures-bundle/Loader/FixtureLoaderInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Loader/HookableFixtureLoader.php create mode 100644 vendor/sylius/fixtures-bundle/Loader/HookableSuiteLoader.php create mode 100644 vendor/sylius/fixtures-bundle/Loader/SuiteLoader.php create mode 100644 vendor/sylius/fixtures-bundle/Loader/SuiteLoaderInterface.php create mode 100644 vendor/sylius/fixtures-bundle/README.md create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/fixture.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/mongodb-odm.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/orm.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/phpcr-odm.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/listener.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/loader.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/logger.xml create mode 100644 vendor/sylius/fixtures-bundle/Resources/config/services/suite.xml create mode 100644 vendor/sylius/fixtures-bundle/Suite/LazySuiteRegistry.php create mode 100644 vendor/sylius/fixtures-bundle/Suite/Suite.php create mode 100644 vendor/sylius/fixtures-bundle/Suite/SuiteFactory.php create mode 100644 vendor/sylius/fixtures-bundle/Suite/SuiteFactoryInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Suite/SuiteInterface.php create mode 100644 vendor/sylius/fixtures-bundle/Suite/SuiteNotFoundException.php create mode 100644 vendor/sylius/fixtures-bundle/Suite/SuiteRegistryInterface.php create mode 100644 vendor/sylius/fixtures-bundle/SyliusFixturesBundle.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/FixtureRegistryPassTest.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/ListenerRegistryPassTest.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/DependencyInjection/ConfigurationTest.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/DependencyInjection/SyliusFixturesExtensionTest.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/Listener/MongoDBPurgerListenerTest.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/Listener/ORMPurgerListenerTest.php create mode 100644 vendor/sylius/fixtures-bundle/Tests/Listener/PHPCRPurgerListenerTest.php create mode 100644 vendor/sylius/fixtures-bundle/composer.json create mode 100644 vendor/sylius/fixtures-bundle/phpspec.yml.dist create mode 100644 vendor/sylius/fixtures-bundle/phpunit.xml.dist create mode 100644 vendor/sylius/fixtures-bundle/spec/Fixture/FixtureNotFoundExceptionSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Fixture/FixtureRegistrySpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Listener/ListenerNotFoundExceptionSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Listener/ListenerRegistrySpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Listener/LoggerListenerSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Loader/FixtureLoaderSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Loader/HookableFixtureLoaderSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Loader/HookableSuiteLoaderSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Loader/SuiteLoaderSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Suite/LazySuiteRegistrySpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Suite/SuiteFactorySpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Suite/SuiteNotFoundExceptionSpec.php create mode 100644 vendor/sylius/fixtures-bundle/spec/Suite/SuiteSpec.php create mode 100644 vendor/sylius/fixtures-bundle/test/app/AppKernel.php create mode 100644 vendor/sylius/fixtures-bundle/test/app/config/config.yml create mode 100644 vendor/sylius/fixtures-bundle/test/app/config/parameters.yml create mode 100755 vendor/sylius/fixtures-bundle/test/app/console create mode 100644 vendor/sylius/fixtures-bundle/test/composer.json create mode 100644 vendor/sylius/fixtures-bundle/test/src/Tests/SyliusFixturesBundleTest.php create mode 100644 vendor/sylius/resource/.gitignore create mode 100644 vendor/sylius/resource/Exception/DeleteHandlingException.php create mode 100644 vendor/sylius/resource/Exception/RaceConditionException.php create mode 100644 vendor/sylius/resource/Exception/UnexpectedTypeException.php create mode 100644 vendor/sylius/resource/Exception/UnsupportedMethodException.php create mode 100644 vendor/sylius/resource/Exception/UpdateHandlingException.php create mode 100644 vendor/sylius/resource/Factory/Factory.php create mode 100644 vendor/sylius/resource/Factory/FactoryInterface.php create mode 100644 vendor/sylius/resource/Factory/TranslatableFactory.php create mode 100644 vendor/sylius/resource/Factory/TranslatableFactoryInterface.php create mode 100644 vendor/sylius/resource/Generator/RandomnessGenerator.php create mode 100644 vendor/sylius/resource/Generator/RandomnessGeneratorInterface.php create mode 100644 vendor/sylius/resource/LICENSE create mode 100644 vendor/sylius/resource/Metadata/Metadata.php create mode 100644 vendor/sylius/resource/Metadata/MetadataInterface.php create mode 100644 vendor/sylius/resource/Metadata/Registry.php create mode 100644 vendor/sylius/resource/Metadata/RegistryInterface.php create mode 100644 vendor/sylius/resource/Model/AbstractTranslation.php create mode 100644 vendor/sylius/resource/Model/ArchivableInterface.php create mode 100644 vendor/sylius/resource/Model/ArchivableTrait.php create mode 100644 vendor/sylius/resource/Model/CodeAwareInterface.php create mode 100644 vendor/sylius/resource/Model/ResourceInterface.php create mode 100644 vendor/sylius/resource/Model/ResourceLogEntry.php create mode 100644 vendor/sylius/resource/Model/SlugAwareInterface.php create mode 100644 vendor/sylius/resource/Model/TimestampableInterface.php create mode 100644 vendor/sylius/resource/Model/TimestampableTrait.php create mode 100644 vendor/sylius/resource/Model/ToggleableInterface.php create mode 100644 vendor/sylius/resource/Model/ToggleableTrait.php create mode 100644 vendor/sylius/resource/Model/TranslatableInterface.php create mode 100644 vendor/sylius/resource/Model/TranslatableTrait.php create mode 100644 vendor/sylius/resource/Model/TranslationInterface.php create mode 100644 vendor/sylius/resource/Model/VersionedInterface.php create mode 100644 vendor/sylius/resource/README.md create mode 100644 vendor/sylius/resource/Repository/Exception/ExistingResourceException.php create mode 100644 vendor/sylius/resource/Repository/InMemoryRepository.php create mode 100644 vendor/sylius/resource/Repository/RepositoryInterface.php create mode 100644 vendor/sylius/resource/ResourceActions.php create mode 100644 vendor/sylius/resource/StateMachine/StateMachine.php create mode 100644 vendor/sylius/resource/StateMachine/StateMachineInterface.php create mode 100644 vendor/sylius/resource/Storage/StorageInterface.php create mode 100644 vendor/sylius/resource/Translation/Provider/ImmutableTranslationLocaleProvider.php create mode 100644 vendor/sylius/resource/Translation/Provider/TranslationLocaleProviderInterface.php create mode 100644 vendor/sylius/resource/Translation/TranslatableEntityLocaleAssigner.php create mode 100644 vendor/sylius/resource/Translation/TranslatableEntityLocaleAssignerInterface.php create mode 100644 vendor/sylius/resource/composer.json create mode 100644 vendor/sylius/resource/phpspec.yml.dist create mode 100644 vendor/sylius/resource/spec/Exception/DeleteHandlingExceptionSpec.php create mode 100644 vendor/sylius/resource/spec/Exception/RaceConditionExceptionSpec.php create mode 100644 vendor/sylius/resource/spec/Exception/UnexpectedTypeExceptionSpec.php create mode 100644 vendor/sylius/resource/spec/Exception/UnsupportedMethodExceptionSpec.php create mode 100644 vendor/sylius/resource/spec/Exception/UpdateHandlingExceptionSpec.php create mode 100644 vendor/sylius/resource/spec/Factory/FactorySpec.php create mode 100644 vendor/sylius/resource/spec/Factory/TranslatableFactorySpec.php create mode 100644 vendor/sylius/resource/spec/Fixtures/SampleBookResourceInterface.php create mode 100644 vendor/sylius/resource/spec/Generator/RandomnessGeneratorSpec.php create mode 100644 vendor/sylius/resource/spec/Metadata/MetadataSpec.php create mode 100644 vendor/sylius/resource/spec/Metadata/RegistrySpec.php create mode 100644 vendor/sylius/resource/spec/Model/AbstractTranslationSpec.php create mode 100644 vendor/sylius/resource/spec/Repository/Exception/ExistingResourceExceptionSpec.php create mode 100644 vendor/sylius/resource/spec/Repository/InMemoryRepositorySpec.php create mode 100644 vendor/sylius/resource/spec/Translation/Provider/ImmutableTranslationLocaleProviderSpec.php create mode 100644 vendor/sylius/resource/spec/Translation/TranslatableEntityLocaleAssignerSpec.php create mode 100644 vendor/symfony/cache/.gitignore create mode 100644 vendor/symfony/cache/Adapter/AbstractAdapter.php create mode 100644 vendor/symfony/cache/Adapter/AdapterInterface.php create mode 100644 vendor/symfony/cache/Adapter/ApcuAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ArrayAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ChainAdapter.php create mode 100644 vendor/symfony/cache/Adapter/DoctrineAdapter.php create mode 100644 vendor/symfony/cache/Adapter/FilesystemAdapter.php create mode 100644 vendor/symfony/cache/Adapter/MemcachedAdapter.php create mode 100644 vendor/symfony/cache/Adapter/NullAdapter.php create mode 100644 vendor/symfony/cache/Adapter/PdoAdapter.php create mode 100644 vendor/symfony/cache/Adapter/PhpArrayAdapter.php create mode 100644 vendor/symfony/cache/Adapter/PhpFilesAdapter.php create mode 100644 vendor/symfony/cache/Adapter/ProxyAdapter.php create mode 100644 vendor/symfony/cache/Adapter/RedisAdapter.php create mode 100644 vendor/symfony/cache/Adapter/SimpleCacheAdapter.php create mode 100644 vendor/symfony/cache/Adapter/TagAwareAdapter.php create mode 100644 vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php create mode 100644 vendor/symfony/cache/Adapter/TraceableAdapter.php create mode 100644 vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php create mode 100644 vendor/symfony/cache/CHANGELOG.md create mode 100644 vendor/symfony/cache/CacheItem.php create mode 100644 vendor/symfony/cache/DataCollector/CacheDataCollector.php create mode 100644 vendor/symfony/cache/DoctrineProvider.php create mode 100644 vendor/symfony/cache/Exception/CacheException.php create mode 100644 vendor/symfony/cache/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/cache/LICENSE create mode 100644 vendor/symfony/cache/PruneableInterface.php create mode 100644 vendor/symfony/cache/README.md create mode 100644 vendor/symfony/cache/ResettableInterface.php create mode 100644 vendor/symfony/cache/Simple/AbstractCache.php create mode 100644 vendor/symfony/cache/Simple/ApcuCache.php create mode 100644 vendor/symfony/cache/Simple/ArrayCache.php create mode 100644 vendor/symfony/cache/Simple/ChainCache.php create mode 100644 vendor/symfony/cache/Simple/DoctrineCache.php create mode 100644 vendor/symfony/cache/Simple/FilesystemCache.php create mode 100644 vendor/symfony/cache/Simple/MemcachedCache.php create mode 100644 vendor/symfony/cache/Simple/NullCache.php create mode 100644 vendor/symfony/cache/Simple/PdoCache.php create mode 100644 vendor/symfony/cache/Simple/PhpArrayCache.php create mode 100644 vendor/symfony/cache/Simple/PhpFilesCache.php create mode 100644 vendor/symfony/cache/Simple/Psr6Cache.php create mode 100644 vendor/symfony/cache/Simple/RedisCache.php create mode 100644 vendor/symfony/cache/Simple/TraceableCache.php create mode 100644 vendor/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/AdapterTestCase.php create mode 100644 vendor/symfony/cache/Tests/Adapter/ApcuAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/ArrayAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/ChainAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/DoctrineAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/NullAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PdoAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PredisAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/ProxyAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/RedisAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/TraceableAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.php create mode 100644 vendor/symfony/cache/Tests/CacheItemTest.php create mode 100644 vendor/symfony/cache/Tests/DoctrineProviderTest.php create mode 100644 vendor/symfony/cache/Tests/Fixtures/ArrayCache.php create mode 100644 vendor/symfony/cache/Tests/Fixtures/ExternalAdapter.php create mode 100644 vendor/symfony/cache/Tests/Simple/AbstractRedisCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/ApcuCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/ArrayCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/CacheTestCase.php create mode 100644 vendor/symfony/cache/Tests/Simple/ChainCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/DoctrineCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/FilesystemCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/MemcachedCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/NullCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/PdoCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/PdoDbalCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/PhpArrayCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/PhpFilesCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/Psr6CacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/RedisArrayCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/RedisCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/RedisClusterCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Simple/TraceableCacheTest.php create mode 100644 vendor/symfony/cache/Tests/Traits/PdoPruneableTrait.php create mode 100644 vendor/symfony/cache/Traits/AbstractTrait.php create mode 100644 vendor/symfony/cache/Traits/ApcuTrait.php create mode 100644 vendor/symfony/cache/Traits/ArrayTrait.php create mode 100644 vendor/symfony/cache/Traits/DoctrineTrait.php create mode 100644 vendor/symfony/cache/Traits/FilesystemCommonTrait.php create mode 100644 vendor/symfony/cache/Traits/FilesystemTrait.php create mode 100644 vendor/symfony/cache/Traits/MemcachedTrait.php create mode 100644 vendor/symfony/cache/Traits/PdoTrait.php create mode 100644 vendor/symfony/cache/Traits/PhpArrayTrait.php create mode 100644 vendor/symfony/cache/Traits/PhpFilesTrait.php create mode 100644 vendor/symfony/cache/Traits/ProxyTrait.php create mode 100644 vendor/symfony/cache/Traits/RedisProxy.php create mode 100644 vendor/symfony/cache/Traits/RedisTrait.php create mode 100644 vendor/symfony/cache/composer.json create mode 100644 vendor/symfony/cache/phpunit.xml.dist create mode 100644 vendor/symfony/class-loader/.gitignore create mode 100644 vendor/symfony/class-loader/ApcClassLoader.php create mode 100644 vendor/symfony/class-loader/CHANGELOG.md create mode 100644 vendor/symfony/class-loader/ClassCollectionLoader.php create mode 100644 vendor/symfony/class-loader/ClassLoader.php create mode 100644 vendor/symfony/class-loader/ClassMapGenerator.php create mode 100644 vendor/symfony/class-loader/LICENSE create mode 100644 vendor/symfony/class-loader/MapClassLoader.php create mode 100644 vendor/symfony/class-loader/Psr4ClassLoader.php create mode 100644 vendor/symfony/class-loader/README.md create mode 100644 vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php create mode 100644 vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php create mode 100644 vendor/symfony/class-loader/Tests/ClassLoaderTest.php create mode 100644 vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Baz.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/ApcPrefixCollision/A/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/ApcPrefixCollision/A/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Namespaced/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/ATrait.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/B.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/BTrait.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/CInterface.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/CTrait.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/D.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/E.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/F.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/G.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/GInterface.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/DeclaredClass.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/DeclaredInterface.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithDirMagic.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithFileMagic.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithHaltCompiler.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithStrictTypes.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced2/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced2/Baz.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Namespaced2/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike/Baz.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike/WithComments.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Baz.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/WarmedClass.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/WarmedInterface.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/PrefixCollision/A/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/PrefixCollision/A/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/PrefixCollision/C/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/alpha/PrefixCollision/C/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/PrefixCollision/A/B/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/PrefixCollision/A/B/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/PrefixCollision/C/B/Bar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/beta/PrefixCollision/C/B/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeClass.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/notAClass.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/notPhpFile.md create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/classmap/sameNsMultipleClasses.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/deps/traits.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/fallback/Pearlike/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/fallback/Pearlike2/FooBar.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/includepath/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/php5.4/traits.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/php5.5/class_cons.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/psr-4/Class_With_Underscores.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/psr-4/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/psr-4/Lets/Go/Deeper/Class_With_Underscores.php create mode 100644 vendor/symfony/class-loader/Tests/Fixtures/psr-4/Lets/Go/Deeper/Foo.php create mode 100644 vendor/symfony/class-loader/Tests/Psr4ClassLoaderTest.php create mode 100644 vendor/symfony/class-loader/WinCacheClassLoader.php create mode 100644 vendor/symfony/class-loader/XcacheClassLoader.php create mode 100644 vendor/symfony/class-loader/composer.json create mode 100644 vendor/symfony/class-loader/phpunit.xml.dist create mode 100644 vendor/symfony/config/.gitignore create mode 100644 vendor/symfony/config/CHANGELOG.md create mode 100644 vendor/symfony/config/ConfigCache.php create mode 100644 vendor/symfony/config/ConfigCacheFactory.php create mode 100644 vendor/symfony/config/ConfigCacheFactoryInterface.php create mode 100644 vendor/symfony/config/ConfigCacheInterface.php create mode 100644 vendor/symfony/config/Definition/ArrayNode.php create mode 100644 vendor/symfony/config/Definition/BaseNode.php create mode 100644 vendor/symfony/config/Definition/BooleanNode.php create mode 100644 vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/ExprBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/MergeBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/NodeBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/NodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/NodeParentInterface.php create mode 100644 vendor/symfony/config/Definition/Builder/NormalizationBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php create mode 100644 vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/Builder/TreeBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/ValidationBuilder.php create mode 100644 vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php create mode 100644 vendor/symfony/config/Definition/ConfigurationInterface.php create mode 100644 vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php create mode 100644 vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php create mode 100644 vendor/symfony/config/Definition/EnumNode.php create mode 100644 vendor/symfony/config/Definition/Exception/DuplicateKeyException.php create mode 100644 vendor/symfony/config/Definition/Exception/Exception.php create mode 100644 vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php create mode 100644 vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php create mode 100644 vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php create mode 100644 vendor/symfony/config/Definition/Exception/InvalidTypeException.php create mode 100644 vendor/symfony/config/Definition/Exception/UnsetKeyException.php create mode 100644 vendor/symfony/config/Definition/FloatNode.php create mode 100644 vendor/symfony/config/Definition/IntegerNode.php create mode 100644 vendor/symfony/config/Definition/NodeInterface.php create mode 100644 vendor/symfony/config/Definition/NumericNode.php create mode 100644 vendor/symfony/config/Definition/Processor.php create mode 100644 vendor/symfony/config/Definition/PrototypeNodeInterface.php create mode 100644 vendor/symfony/config/Definition/PrototypedArrayNode.php create mode 100644 vendor/symfony/config/Definition/ScalarNode.php create mode 100644 vendor/symfony/config/Definition/VariableNode.php create mode 100644 vendor/symfony/config/DependencyInjection/ConfigCachePass.php create mode 100644 vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php create mode 100644 vendor/symfony/config/Exception/FileLoaderLoadException.php create mode 100644 vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php create mode 100644 vendor/symfony/config/FileLocator.php create mode 100644 vendor/symfony/config/FileLocatorInterface.php create mode 100644 vendor/symfony/config/LICENSE create mode 100644 vendor/symfony/config/Loader/DelegatingLoader.php create mode 100644 vendor/symfony/config/Loader/FileLoader.php create mode 100644 vendor/symfony/config/Loader/GlobFileLoader.php create mode 100644 vendor/symfony/config/Loader/Loader.php create mode 100644 vendor/symfony/config/Loader/LoaderInterface.php create mode 100644 vendor/symfony/config/Loader/LoaderResolver.php create mode 100644 vendor/symfony/config/Loader/LoaderResolverInterface.php create mode 100644 vendor/symfony/config/README.md create mode 100644 vendor/symfony/config/Resource/ClassExistenceResource.php create mode 100644 vendor/symfony/config/Resource/ComposerResource.php create mode 100644 vendor/symfony/config/Resource/DirectoryResource.php create mode 100644 vendor/symfony/config/Resource/FileExistenceResource.php create mode 100644 vendor/symfony/config/Resource/FileResource.php create mode 100644 vendor/symfony/config/Resource/GlobResource.php create mode 100644 vendor/symfony/config/Resource/ReflectionClassResource.php create mode 100644 vendor/symfony/config/Resource/ResourceInterface.php create mode 100644 vendor/symfony/config/Resource/SelfCheckingResourceChecker.php create mode 100644 vendor/symfony/config/Resource/SelfCheckingResourceInterface.php create mode 100644 vendor/symfony/config/ResourceCheckerConfigCache.php create mode 100644 vendor/symfony/config/ResourceCheckerConfigCacheFactory.php create mode 100644 vendor/symfony/config/ResourceCheckerInterface.php create mode 100644 vendor/symfony/config/Tests/ConfigCacheFactoryTest.php create mode 100644 vendor/symfony/config/Tests/ConfigCacheTest.php create mode 100644 vendor/symfony/config/Tests/Definition/ArrayNodeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/BooleanNodeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php create mode 100644 vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php create mode 100644 vendor/symfony/config/Tests/Definition/EnumNodeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/FinalizationTest.php create mode 100644 vendor/symfony/config/Tests/Definition/FloatNodeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/IntegerNodeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/MergeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/NormalizationTest.php create mode 100644 vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php create mode 100644 vendor/symfony/config/Tests/Definition/ScalarNodeTest.php create mode 100644 vendor/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.php create mode 100644 vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php create mode 100644 vendor/symfony/config/Tests/FileLocatorTest.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Again/foo.xml create mode 100644 vendor/symfony/config/Tests/Fixtures/BadParent.php create mode 100644 vendor/symfony/config/Tests/Fixtures/BarNode.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Resource/.hiddenFile create mode 100644 vendor/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php create mode 100644 vendor/symfony/config/Tests/Fixtures/Util/document_type.xml create mode 100644 vendor/symfony/config/Tests/Fixtures/Util/invalid.xml create mode 100644 vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml create mode 100644 vendor/symfony/config/Tests/Fixtures/Util/schema.xsd create mode 100644 vendor/symfony/config/Tests/Fixtures/Util/valid.xml create mode 100644 vendor/symfony/config/Tests/Fixtures/foo.xml create mode 100644 vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php create mode 100644 vendor/symfony/config/Tests/Loader/FileLoaderTest.php create mode 100644 vendor/symfony/config/Tests/Loader/LoaderResolverTest.php create mode 100644 vendor/symfony/config/Tests/Loader/LoaderTest.php create mode 100644 vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/ComposerResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/FileResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/GlobResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php create mode 100644 vendor/symfony/config/Tests/Resource/ResourceStub.php create mode 100644 vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php create mode 100644 vendor/symfony/config/Tests/Util/XmlUtilsTest.php create mode 100644 vendor/symfony/config/Util/Exception/InvalidXmlException.php create mode 100644 vendor/symfony/config/Util/Exception/XmlParsingException.php create mode 100644 vendor/symfony/config/Util/XmlUtils.php create mode 100644 vendor/symfony/config/composer.json create mode 100644 vendor/symfony/config/phpunit.xml.dist create mode 100644 vendor/symfony/debug/.gitignore create mode 100644 vendor/symfony/debug/BufferingLogger.php create mode 100644 vendor/symfony/debug/CHANGELOG.md create mode 100644 vendor/symfony/debug/Debug.php create mode 100644 vendor/symfony/debug/DebugClassLoader.php create mode 100644 vendor/symfony/debug/ErrorHandler.php create mode 100644 vendor/symfony/debug/Exception/ClassNotFoundException.php create mode 100644 vendor/symfony/debug/Exception/FatalErrorException.php create mode 100644 vendor/symfony/debug/Exception/FatalThrowableError.php create mode 100644 vendor/symfony/debug/Exception/FlattenException.php create mode 100644 vendor/symfony/debug/Exception/OutOfMemoryException.php create mode 100644 vendor/symfony/debug/Exception/SilencedErrorContext.php create mode 100644 vendor/symfony/debug/Exception/UndefinedFunctionException.php create mode 100644 vendor/symfony/debug/Exception/UndefinedMethodException.php create mode 100644 vendor/symfony/debug/ExceptionHandler.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php create mode 100644 vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php create mode 100644 vendor/symfony/debug/LICENSE create mode 100644 vendor/symfony/debug/README.md create mode 100644 vendor/symfony/debug/Tests/DebugClassLoaderTest.php create mode 100644 vendor/symfony/debug/Tests/ErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php create mode 100644 vendor/symfony/debug/Tests/ExceptionHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/ClassAlias.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/DeprecatedClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/DeprecatedInterface.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/ExtendedFinalMethod.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/FinalClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/FinalMethod.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/FinalMethod2Trait.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/InternalClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/InternalInterface.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/InternalTrait.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/InternalTrait2.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/NonDeprecatedInterface.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/PEARClass.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/Throwing.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/ToStringThrower.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/TraitWithInternalMethod.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/casemismatch.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/notPsr0Bis.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/psr4/Psr4CaseMismatch.php create mode 100644 vendor/symfony/debug/Tests/Fixtures/reallyNotPsr0.php create mode 100644 vendor/symfony/debug/Tests/Fixtures2/RequiredTwice.php create mode 100644 vendor/symfony/debug/Tests/HeaderMock.php create mode 100644 vendor/symfony/debug/Tests/MockExceptionHandler.php create mode 100644 vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt create mode 100644 vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt create mode 100644 vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt create mode 100644 vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt create mode 100644 vendor/symfony/debug/composer.json create mode 100644 vendor/symfony/debug/phpunit.xml.dist create mode 100644 vendor/symfony/dependency-injection/.gitignore create mode 100644 vendor/symfony/dependency-injection/Alias.php create mode 100644 vendor/symfony/dependency-injection/Argument/ArgumentInterface.php create mode 100644 vendor/symfony/dependency-injection/Argument/BoundArgument.php create mode 100644 vendor/symfony/dependency-injection/Argument/IteratorArgument.php create mode 100644 vendor/symfony/dependency-injection/Argument/RewindableGenerator.php create mode 100644 vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php create mode 100644 vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php create mode 100644 vendor/symfony/dependency-injection/CHANGELOG.md create mode 100644 vendor/symfony/dependency-injection/ChildDefinition.php create mode 100644 vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/AutowirePass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/Compiler.php create mode 100644 vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php create mode 100644 vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/PassConfig.php create mode 100644 vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.php create mode 100644 vendor/symfony/dependency-injection/Compiler/RepeatedPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php create mode 100644 vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php create mode 100644 vendor/symfony/dependency-injection/Config/ContainerParametersResource.php create mode 100644 vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php create mode 100644 vendor/symfony/dependency-injection/Container.php create mode 100644 vendor/symfony/dependency-injection/ContainerAwareInterface.php create mode 100644 vendor/symfony/dependency-injection/ContainerAwareTrait.php create mode 100644 vendor/symfony/dependency-injection/ContainerBuilder.php create mode 100644 vendor/symfony/dependency-injection/ContainerInterface.php create mode 100644 vendor/symfony/dependency-injection/Definition.php create mode 100644 vendor/symfony/dependency-injection/Dumper/Dumper.php create mode 100644 vendor/symfony/dependency-injection/Dumper/DumperInterface.php create mode 100644 vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php create mode 100644 vendor/symfony/dependency-injection/Dumper/PhpDumper.php create mode 100644 vendor/symfony/dependency-injection/Dumper/XmlDumper.php create mode 100644 vendor/symfony/dependency-injection/Dumper/YamlDumper.php create mode 100644 vendor/symfony/dependency-injection/EnvVarProcessor.php create mode 100644 vendor/symfony/dependency-injection/EnvVarProcessorInterface.php create mode 100644 vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php create mode 100644 vendor/symfony/dependency-injection/Exception/BadMethodCallException.php create mode 100644 vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php create mode 100644 vendor/symfony/dependency-injection/Exception/EnvParameterException.php create mode 100644 vendor/symfony/dependency-injection/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/dependency-injection/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/dependency-injection/Exception/LogicException.php create mode 100644 vendor/symfony/dependency-injection/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php create mode 100644 vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php create mode 100644 vendor/symfony/dependency-injection/Exception/RuntimeException.php create mode 100644 vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php create mode 100644 vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php create mode 100644 vendor/symfony/dependency-injection/ExpressionLanguage.php create mode 100644 vendor/symfony/dependency-injection/ExpressionLanguageProvider.php create mode 100644 vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php create mode 100644 vendor/symfony/dependency-injection/Extension/Extension.php create mode 100644 vendor/symfony/dependency-injection/Extension/ExtensionInterface.php create mode 100644 vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php create mode 100644 vendor/symfony/dependency-injection/LICENSE create mode 100644 vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php create mode 100644 vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php create mode 100644 vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php create mode 100644 vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php create mode 100644 vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php create mode 100644 vendor/symfony/dependency-injection/Loader/ClosureLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php create mode 100644 vendor/symfony/dependency-injection/Loader/DirectoryLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/FileLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/GlobFileLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/IniFileLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/XmlFileLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd create mode 100644 vendor/symfony/dependency-injection/Parameter.php create mode 100644 vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php create mode 100644 vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php create mode 100644 vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php create mode 100644 vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php create mode 100644 vendor/symfony/dependency-injection/README.md create mode 100644 vendor/symfony/dependency-injection/Reference.php create mode 100644 vendor/symfony/dependency-injection/ResettableContainerInterface.php create mode 100644 vendor/symfony/dependency-injection/ServiceLocator.php create mode 100644 vendor/symfony/dependency-injection/ServiceSubscriberInterface.php create mode 100644 vendor/symfony/dependency-injection/TaggedContainerInterface.php create mode 100644 vendor/symfony/dependency-injection/Tests/Argument/RewindableGeneratorTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ChildDefinitionTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/AutowireRequiredMethodsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/CheckArgumentsValidityPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/DefinitionErrorExceptionPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/RegisterServiceSubscribersPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveBindingsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveChildDefinitionsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveClassPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveFactoryClassPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveHotPathPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveNamedArgumentsPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolvePrivatesPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceCheckerTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ContainerTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/CrossCheckTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/DefinitionTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/EnvVarProcessorTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Bar.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/BarInterface.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/CaseSensitiveClass.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Container/ConstructorWithMandatoryArgumentsContainer.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Container/ConstructorWithOptionalArgumentsContainer.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Container/ConstructorWithoutArgumentsContainer.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Container/NoConstructorContainer.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/CustomDefinition.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/DeprecatedClass.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/NamedArgumentsDummy.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ParentNotExists.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/BadClasses/MissingParent.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/Foo.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/FooInterface.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/AnotherSub/DeeperBaz.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/Baz.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir1/Service1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir2/Service2.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/Component1/Dir3/Service3.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/Component2/Dir1/Service4.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/OtherDir/Component2/Dir2/Service5.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/Sub/Bar.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/Sub/BarInterface.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/Sub/NoLoadAbstractBar.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/Sub/NoLoadBarInterface.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/Prototype/Sub/NoLoadBarTrait.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/SimilarArgumentsDummy.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/StubbedTranslator.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/TestServiceSubscriber.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/array.json create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/child.expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/child.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/factory_short_notation.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/services9.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/config/services_autoconfigure_with_parent.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container10.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container15.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container33.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_abstract.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_almost_circular.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_env_in_id.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_inline_requires.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_uninitialized_ref.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/FooVariadic.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/HotPath/C1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/HotPath/C2.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/HotPath/C3.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/HotPath/I1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/HotPath/P1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/HotPath/T1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectExtension.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtensionInPhar.phar create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/autowiring_classes.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/classes.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/ini_with_wrong_ext.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_without_constructor.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/php_with_wrong_ext.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services1.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services10.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services33.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_as_files.txt create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_private.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_public.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_array_params.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_base64_env.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_dedup_lazy_proxy.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_env_in_id.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_inline_requires.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_locator.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_non_shared_lazy.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_frozen.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_in_expression.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_rot13_env.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_subscriber.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/services_uninitialized_ref.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/class_from_id.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/invalid_alias_definition.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/nested_service_without_id.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services28.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_abstract.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_bindings.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_case.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_defaults_with_parent.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_dump_load.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_inline_not_candidate.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof_with_parent.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_named_args.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_prototype.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_without_id.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/xml/xml_with_wrong_ext.php create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_alias.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_instanceof.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_parameters.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_alias.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_defaults.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_instanceof.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_keyword.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bar/services.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/class_from_id.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/foo/services.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/null_config.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services28.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services31_invalid_tags.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_bindings.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_case.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_defaults_with_parent.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_dump_load.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_inline.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_named_args.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace_without_resource.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_underscore.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_only.yml create mode 100644 vendor/symfony/dependency-injection/Tests/Fixtures/yaml/yaml_with_wrong_ext.ini create mode 100644 vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/FileLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/GlobFileLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/LoaderResolverTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ParameterTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ReferenceTest.php create mode 100644 vendor/symfony/dependency-injection/Tests/ServiceLocatorTest.php create mode 100644 vendor/symfony/dependency-injection/TypedReference.php create mode 100644 vendor/symfony/dependency-injection/Variable.php create mode 100644 vendor/symfony/dependency-injection/composer.json create mode 100644 vendor/symfony/dependency-injection/phpunit.xml.dist create mode 100644 vendor/symfony/event-dispatcher/.gitignore create mode 100644 vendor/symfony/event-dispatcher/CHANGELOG.md create mode 100644 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/Debug/WrappedListener.php create mode 100644 vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 vendor/symfony/event-dispatcher/Event.php create mode 100644 vendor/symfony/event-dispatcher/EventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/EventDispatcherInterface.php create mode 100644 vendor/symfony/event-dispatcher/EventSubscriberInterface.php create mode 100644 vendor/symfony/event-dispatcher/GenericEvent.php create mode 100644 vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php create mode 100644 vendor/symfony/event-dispatcher/LICENSE create mode 100644 vendor/symfony/event-dispatcher/README.md create mode 100644 vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/EventTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/GenericEventTest.php create mode 100644 vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php create mode 100644 vendor/symfony/event-dispatcher/composer.json create mode 100644 vendor/symfony/event-dispatcher/phpunit.xml.dist create mode 100644 vendor/symfony/expression-language/.gitignore create mode 100644 vendor/symfony/expression-language/CHANGELOG.md create mode 100644 vendor/symfony/expression-language/Compiler.php create mode 100644 vendor/symfony/expression-language/Expression.php create mode 100644 vendor/symfony/expression-language/ExpressionFunction.php create mode 100644 vendor/symfony/expression-language/ExpressionFunctionProviderInterface.php create mode 100644 vendor/symfony/expression-language/ExpressionLanguage.php create mode 100644 vendor/symfony/expression-language/LICENSE create mode 100644 vendor/symfony/expression-language/Lexer.php create mode 100644 vendor/symfony/expression-language/Node/ArgumentsNode.php create mode 100644 vendor/symfony/expression-language/Node/ArrayNode.php create mode 100644 vendor/symfony/expression-language/Node/BinaryNode.php create mode 100644 vendor/symfony/expression-language/Node/ConditionalNode.php create mode 100644 vendor/symfony/expression-language/Node/ConstantNode.php create mode 100644 vendor/symfony/expression-language/Node/FunctionNode.php create mode 100644 vendor/symfony/expression-language/Node/GetAttrNode.php create mode 100644 vendor/symfony/expression-language/Node/NameNode.php create mode 100644 vendor/symfony/expression-language/Node/Node.php create mode 100644 vendor/symfony/expression-language/Node/UnaryNode.php create mode 100644 vendor/symfony/expression-language/ParsedExpression.php create mode 100644 vendor/symfony/expression-language/Parser.php create mode 100644 vendor/symfony/expression-language/README.md create mode 100644 vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php create mode 100644 vendor/symfony/expression-language/SerializedParsedExpression.php create mode 100644 vendor/symfony/expression-language/SyntaxError.php create mode 100644 vendor/symfony/expression-language/Tests/ExpressionFunctionTest.php create mode 100644 vendor/symfony/expression-language/Tests/ExpressionLanguageTest.php create mode 100644 vendor/symfony/expression-language/Tests/ExpressionTest.php create mode 100644 vendor/symfony/expression-language/Tests/Fixtures/TestProvider.php create mode 100644 vendor/symfony/expression-language/Tests/LexerTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/AbstractNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/ArgumentsNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/ArrayNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/BinaryNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/ConditionalNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/ConstantNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/FunctionNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/GetAttrNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/NameNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/NodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/Node/UnaryNodeTest.php create mode 100644 vendor/symfony/expression-language/Tests/ParsedExpressionTest.php create mode 100644 vendor/symfony/expression-language/Tests/ParserTest.php create mode 100644 vendor/symfony/expression-language/Token.php create mode 100644 vendor/symfony/expression-language/TokenStream.php create mode 100644 vendor/symfony/expression-language/composer.json create mode 100644 vendor/symfony/expression-language/phpunit.xml.dist create mode 100644 vendor/symfony/filesystem/.gitignore create mode 100644 vendor/symfony/filesystem/CHANGELOG.md create mode 100644 vendor/symfony/filesystem/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/filesystem/Exception/FileNotFoundException.php create mode 100644 vendor/symfony/filesystem/Exception/IOException.php create mode 100644 vendor/symfony/filesystem/Exception/IOExceptionInterface.php create mode 100644 vendor/symfony/filesystem/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/filesystem/Filesystem.php create mode 100644 vendor/symfony/filesystem/LICENSE create mode 100644 vendor/symfony/filesystem/README.md create mode 100644 vendor/symfony/filesystem/Tests/ExceptionTest.php create mode 100644 vendor/symfony/filesystem/Tests/FilesystemTest.php create mode 100644 vendor/symfony/filesystem/Tests/FilesystemTestCase.php create mode 100644 vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php create mode 100644 vendor/symfony/filesystem/composer.json create mode 100644 vendor/symfony/filesystem/phpunit.xml.dist create mode 100644 vendor/symfony/finder/.gitignore create mode 100644 vendor/symfony/finder/CHANGELOG.md create mode 100644 vendor/symfony/finder/Comparator/Comparator.php create mode 100644 vendor/symfony/finder/Comparator/DateComparator.php create mode 100644 vendor/symfony/finder/Comparator/NumberComparator.php create mode 100644 vendor/symfony/finder/Exception/AccessDeniedException.php create mode 100644 vendor/symfony/finder/Finder.php create mode 100644 vendor/symfony/finder/Glob.php create mode 100644 vendor/symfony/finder/Iterator/CustomFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/DateRangeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FileTypeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FilecontentFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/FilenameFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/PathFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php create mode 100644 vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php create mode 100644 vendor/symfony/finder/Iterator/SortableIterator.php create mode 100644 vendor/symfony/finder/LICENSE create mode 100644 vendor/symfony/finder/README.md create mode 100644 vendor/symfony/finder/SplFileInfo.php create mode 100644 vendor/symfony/finder/Tests/Comparator/ComparatorTest.php create mode 100644 vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php create mode 100644 vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php create mode 100644 vendor/symfony/finder/Tests/FinderTest.php create mode 100644 vendor/symfony/finder/Tests/Fixtures/.dot/a create mode 100644 vendor/symfony/finder/Tests/Fixtures/.dot/b/c.neon create mode 100644 vendor/symfony/finder/Tests/Fixtures/.dot/b/d.neon create mode 100644 vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/A/a.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy create mode 100644 vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy create mode 100644 vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy create mode 100644 vendor/symfony/finder/Tests/Fixtures/dolor.txt create mode 100644 vendor/symfony/finder/Tests/Fixtures/ipsum.txt create mode 100644 vendor/symfony/finder/Tests/Fixtures/lorem.txt create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/.dot create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/a create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/b/c.neon create mode 100644 vendor/symfony/finder/Tests/Fixtures/one/b/d.neon create mode 100644 vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat create mode 100644 vendor/symfony/finder/Tests/Fixtures/with space/foo.txt create mode 100644 vendor/symfony/finder/Tests/GlobTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/Iterator.php create mode 100644 vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php create mode 100644 vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php create mode 100644 vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php create mode 100644 vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php create mode 100644 vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php create mode 100644 vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php create mode 100644 vendor/symfony/finder/composer.json create mode 100644 vendor/symfony/finder/phpunit.xml.dist create mode 100644 vendor/symfony/framework-bundle/.gitignore create mode 100644 vendor/symfony/framework-bundle/CHANGELOG.md create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/AbstractPhpFileCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/AnnotationsCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/ClassCacheCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/RouterCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/SerializerCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/TemplateFinder.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/TemplateFinderInterface.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/TemplatePathsCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/TranslationsCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/CacheWarmer/ValidatorCacheWarmer.php create mode 100644 vendor/symfony/framework-bundle/Client.php create mode 100644 vendor/symfony/framework-bundle/Command/AboutCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/AbstractConfigCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/AssetsInstallCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/CacheClearCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/CachePoolClearCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/CachePoolPruneCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/CacheWarmupCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/ConfigDebugCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/ContainerAwareCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/ContainerDebugCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/RouterDebugCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/RouterMatchCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/TranslationDebugCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/TranslationUpdateCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/WorkflowDumpCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/XliffLintCommand.php create mode 100644 vendor/symfony/framework-bundle/Command/YamlLintCommand.php create mode 100644 vendor/symfony/framework-bundle/Console/Application.php create mode 100644 vendor/symfony/framework-bundle/Console/Descriptor/Descriptor.php create mode 100644 vendor/symfony/framework-bundle/Console/Descriptor/JsonDescriptor.php create mode 100644 vendor/symfony/framework-bundle/Console/Descriptor/MarkdownDescriptor.php create mode 100644 vendor/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php create mode 100644 vendor/symfony/framework-bundle/Console/Descriptor/XmlDescriptor.php create mode 100644 vendor/symfony/framework-bundle/Console/Helper/DescriptorHelper.php create mode 100644 vendor/symfony/framework-bundle/Controller/AbstractController.php create mode 100644 vendor/symfony/framework-bundle/Controller/Controller.php create mode 100644 vendor/symfony/framework-bundle/Controller/ControllerNameParser.php create mode 100644 vendor/symfony/framework-bundle/Controller/ControllerResolver.php create mode 100644 vendor/symfony/framework-bundle/Controller/ControllerTrait.php create mode 100644 vendor/symfony/framework-bundle/Controller/RedirectController.php create mode 100644 vendor/symfony/framework-bundle/Controller/TemplateController.php create mode 100644 vendor/symfony/framework-bundle/DataCollector/RequestDataCollector.php create mode 100644 vendor/symfony/framework-bundle/DataCollector/RouterDataCollector.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheClearerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheWarmerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConsoleCommandPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddDebugLogProcessorPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/CacheCollectorPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolClearerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPrunerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/ConfigCachePass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/FormPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/LoggingTranslatorPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/ProfilerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/PropertyInfoPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/RoutingResolverPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/SerializerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/TemplatingPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationDumperPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationExtractorPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslatorPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/UnusedTagsPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/Configuration.php create mode 100644 vendor/symfony/framework-bundle/DependencyInjection/FrameworkExtension.php create mode 100644 vendor/symfony/framework-bundle/EventListener/ResolveControllerNameSubscriber.php create mode 100644 vendor/symfony/framework-bundle/EventListener/SessionListener.php create mode 100644 vendor/symfony/framework-bundle/EventListener/TestSessionListener.php create mode 100644 vendor/symfony/framework-bundle/FrameworkBundle.php create mode 100644 vendor/symfony/framework-bundle/HttpCache/HttpCache.php create mode 100644 vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php create mode 100644 vendor/symfony/framework-bundle/LICENSE create mode 100644 vendor/symfony/framework-bundle/README.md create mode 100644 vendor/symfony/framework-bundle/Resources/config/annotations.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/assets.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/cache.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/cache_debug.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/collectors.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/console.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/debug.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/debug_prod.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/esi.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/form.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/form_csrf.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/form_debug.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/fragment_listener.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/fragment_renderer.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/identity_translator.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/lock.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/profiling.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/property_access.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/property_info.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/request.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/routing.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/schema/symfony-1.0.xsd create mode 100644 vendor/symfony/framework-bundle/Resources/config/security_csrf.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/serializer.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/services.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/session.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/ssi.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/templating.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/templating_debug.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/templating_php.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/test.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/translation.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/translation_debug.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/validator.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/validator_debug.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/web.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/web_link.xml create mode 100644 vendor/symfony/framework-bundle/Resources/config/workflow.xml create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/attributes.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/button_attributes.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/button_label.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/button_row.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/button_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/checkbox_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/choice_attributes.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/choice_options.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/choice_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_collapsed.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_expanded.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_options.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/collection_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/color_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/container_attributes.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/date_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/datetime_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/email_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_enctype.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_end.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_errors.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_label.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_rest.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_row.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_rows.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_start.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_widget_compound.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/form_widget_simple.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/hidden_row.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/hidden_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/integer_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/money_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/number_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/password_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/percent_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/radio_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/range_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/repeated_row.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/reset_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/search_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/submit_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/tel_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/textarea_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/time_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/url_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/widget_attributes.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/Form/widget_container_attributes.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/FormTable/button_row.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/FormTable/form_row.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/FormTable/form_widget_compound.html.php create mode 100644 vendor/symfony/framework-bundle/Resources/views/FormTable/hidden_row.html.php create mode 100644 vendor/symfony/framework-bundle/Routing/AnnotatedRouteControllerLoader.php create mode 100644 vendor/symfony/framework-bundle/Routing/DelegatingLoader.php create mode 100644 vendor/symfony/framework-bundle/Routing/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/framework-bundle/Routing/Router.php create mode 100644 vendor/symfony/framework-bundle/Templating/DelegatingEngine.php create mode 100644 vendor/symfony/framework-bundle/Templating/EngineInterface.php create mode 100644 vendor/symfony/framework-bundle/Templating/GlobalVariables.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/ActionsHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/AssetsHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/CodeHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/FormHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/RequestHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/RouterHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/SessionHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/StopwatchHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Helper/TranslatorHelper.php create mode 100644 vendor/symfony/framework-bundle/Templating/Loader/FilesystemLoader.php create mode 100644 vendor/symfony/framework-bundle/Templating/Loader/TemplateLocator.php create mode 100644 vendor/symfony/framework-bundle/Templating/PhpEngine.php create mode 100644 vendor/symfony/framework-bundle/Templating/TemplateFilenameParser.php create mode 100644 vendor/symfony/framework-bundle/Templating/TemplateNameParser.php create mode 100644 vendor/symfony/framework-bundle/Templating/TemplateReference.php create mode 100644 vendor/symfony/framework-bundle/Templating/TimedPhpEngine.php create mode 100644 vendor/symfony/framework-bundle/Test/KernelTestCase.php create mode 100644 vendor/symfony/framework-bundle/Test/WebTestCase.php create mode 100644 vendor/symfony/framework-bundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/CacheWarmer/SerializerCacheWarmerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplateFinderTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/ClientTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Command/CachePruneCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/RouterDebugCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/RouterMatchCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/TranslationDebugCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/TranslationUpdateCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Command/YamlLintCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/ApplicationTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/Descriptor/AbstractDescriptorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/Descriptor/JsonDescriptorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/Descriptor/ObjectsProvider.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/Descriptor/TextDescriptorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Console/Descriptor/XmlDescriptorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/AbstractControllerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/ControllerNameParserTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/ControllerResolverTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/ControllerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/ControllerTraitTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/RedirectControllerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Controller/TemplateControllerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CacheCollectorPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPrunerPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/FormPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/ConfigurationTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_version_strategy_as_service.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/default_config.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_and_ssi_without_fragments.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/full.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_enabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/profiler.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_info.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/request.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_legacy_cache.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/session.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/ssi_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_no_assets.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_assets_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_translation_domain.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/web_link.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_arguments_and_service.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_type_and_service.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_enabled.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_version_strategy_as_service.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_and_ssi_without_fragments.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/full.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock_named.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_enabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/request.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_legacy_cache.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/session.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/ssi_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_no_assets.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_translation_domain.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_arguments_and_service.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_type_and_service.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_enabled.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_version_strategy_as_service.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_and_ssi_without_fragments.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/full.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock_named.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_enabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/request.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_legacy_cache.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/session.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/ssi_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_no_assets.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_assets_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_translation_domain.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_arguments_and_service.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_type_and_service.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_enabled.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/FrameworkExtensionTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/config/serializer/foo.yml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/config/validator/foo.xml create mode 100644 vendor/symfony/framework-bundle/Tests/DependencyInjection/translations/test_default.en.xlf create mode 100644 vendor/symfony/framework-bundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/BaseBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/base.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/controller/base.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/this.is.a.template.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/DeclaredClass.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.json create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.md create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.txt create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Resources/BaseBundle/views/base.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Resources/BaseBundle/views/controller/custom.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Resources/translations/messages.fr.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/resource.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/this.is.a.template.format.engine create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/translation.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Author.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Person.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Resources/author.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Resources/person.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TemplatePathsCache/templates-empty.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TemplatePathsCache/templates.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Article.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Author.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Category.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Person.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Resources/author.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Resources/categories.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Resources/person.xml create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/Validation/SubCategory.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/WarmedClass.php create mode 100644 vendor/symfony/framework-bundle/Tests/Fixtures/templates.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/AnnotatedControllerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/AutowiringTypesTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/TestBundle.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/CachePoolClearCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/CachePoolsTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/ConfigDebugCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/ConfigDumpReferenceCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/ContainerDebugCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/ContainerDumpTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/DebugAutowiringCommandTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/FragmentTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/ProfilerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/PropertyInfoTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/SerializerTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/SessionTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/SubRequestsTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/WebTestCase.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/routing.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AppKernel.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_custom_config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/routing.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/routing.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/routing.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Resources/views/fragment.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Session/bundles.php create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Session/config.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/Session/routing.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/config/default.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Functional/app/config/framework.yml create mode 100644 vendor/symfony/framework-bundle/Tests/Kernel/ConcreteMicroKernel.php create mode 100644 vendor/symfony/framework-bundle/Tests/Kernel/MicroKernelTraitTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Routing/DelegatingLoaderTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Routing/RedirectableUrlMatcherTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Routing/RouterTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/DelegatingEngineTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/GlobalVariablesTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/AssetsHelperTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTranslator.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/RequestHelperTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Child/form_label.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/SessionHelperTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Helper/StopwatchHelperTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/Loader/TemplateLocatorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/PhpEngineTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/TemplateFilenameParserTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/TemplateNameParserTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/TemplateReferenceTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/TemplateTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Templating/TimedPhpEngineTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/TestCase.php create mode 100644 vendor/symfony/framework-bundle/Tests/Translation/PhpExtractorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Translation/TranslatorTest.php create mode 100644 vendor/symfony/framework-bundle/Tests/Validator/ConstraintValidatorFactoryTest.php create mode 100644 vendor/symfony/framework-bundle/Translation/PhpExtractor.php create mode 100644 vendor/symfony/framework-bundle/Translation/PhpStringTokenParser.php create mode 100644 vendor/symfony/framework-bundle/Translation/TranslationLoader.php create mode 100644 vendor/symfony/framework-bundle/Translation/Translator.php create mode 100644 vendor/symfony/framework-bundle/Validator/ConstraintValidatorFactory.php create mode 100644 vendor/symfony/framework-bundle/composer.json create mode 100644 vendor/symfony/framework-bundle/phpunit.xml.dist create mode 100644 vendor/symfony/http-foundation/.gitignore create mode 100644 vendor/symfony/http-foundation/AcceptHeader.php create mode 100644 vendor/symfony/http-foundation/AcceptHeaderItem.php create mode 100644 vendor/symfony/http-foundation/ApacheRequest.php create mode 100644 vendor/symfony/http-foundation/BinaryFileResponse.php create mode 100644 vendor/symfony/http-foundation/CHANGELOG.md create mode 100644 vendor/symfony/http-foundation/Cookie.php create mode 100644 vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php create mode 100644 vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php create mode 100644 vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php create mode 100644 vendor/symfony/http-foundation/ExpressionRequestMatcher.php create mode 100644 vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/FileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/NoFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/PartialFileException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/http-foundation/File/Exception/UploadException.php create mode 100644 vendor/symfony/http-foundation/File/File.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php create mode 100644 vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php create mode 100644 vendor/symfony/http-foundation/File/Stream.php create mode 100644 vendor/symfony/http-foundation/File/UploadedFile.php create mode 100644 vendor/symfony/http-foundation/FileBag.php create mode 100644 vendor/symfony/http-foundation/HeaderBag.php create mode 100644 vendor/symfony/http-foundation/HeaderUtils.php create mode 100644 vendor/symfony/http-foundation/IpUtils.php create mode 100644 vendor/symfony/http-foundation/JsonResponse.php create mode 100644 vendor/symfony/http-foundation/LICENSE create mode 100644 vendor/symfony/http-foundation/ParameterBag.php create mode 100644 vendor/symfony/http-foundation/README.md create mode 100644 vendor/symfony/http-foundation/RedirectResponse.php create mode 100644 vendor/symfony/http-foundation/Request.php create mode 100644 vendor/symfony/http-foundation/RequestMatcher.php create mode 100644 vendor/symfony/http-foundation/RequestMatcherInterface.php create mode 100644 vendor/symfony/http-foundation/RequestStack.php create mode 100644 vendor/symfony/http-foundation/Response.php create mode 100644 vendor/symfony/http-foundation/ResponseHeaderBag.php create mode 100644 vendor/symfony/http-foundation/ServerBag.php create mode 100644 vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php create mode 100644 vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php create mode 100644 vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php create mode 100644 vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php create mode 100644 vendor/symfony/http-foundation/Session/Flash/FlashBag.php create mode 100644 vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php create mode 100644 vendor/symfony/http-foundation/Session/Session.php create mode 100644 vendor/symfony/http-foundation/Session/SessionBagInterface.php create mode 100644 vendor/symfony/http-foundation/Session/SessionBagProxy.php create mode 100644 vendor/symfony/http-foundation/Session/SessionInterface.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/MetadataBag.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php create mode 100644 vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php create mode 100644 vendor/symfony/http-foundation/StreamedResponse.php create mode 100644 vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php create mode 100644 vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ApacheRequestTest.php create mode 100644 vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/CookieTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php create mode 100644 vendor/symfony/http-foundation/Tests/File/FakeFile.php create mode 100644 vendor/symfony/http-foundation/Tests/File/FileTest.php create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/test create mode 100644 vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif create mode 100644 vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php create mode 100644 vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php create mode 100644 vendor/symfony/http-foundation/Tests/FileBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected create mode 100644 vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php create mode 100644 vendor/symfony/http-foundation/Tests/HeaderBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/HeaderUtilsTest.php create mode 100644 vendor/symfony/http-foundation/Tests/IpUtilsTest.php create mode 100644 vendor/symfony/http-foundation/Tests/JsonResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ParameterBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RedirectResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RequestMatcherTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RequestStackTest.php create mode 100644 vendor/symfony/http-foundation/Tests/RequestTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/ResponseTestCase.php create mode 100644 vendor/symfony/http-foundation/Tests/ServerBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/SessionTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.expected create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.expected create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.expected create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.expected create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MemcachedSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MigratingSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php create mode 100644 vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php create mode 100644 vendor/symfony/http-foundation/Tests/StreamedResponseTest.php create mode 100644 vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng create mode 100644 vendor/symfony/http-foundation/Tests/schema/iana-registry.rng create mode 100644 vendor/symfony/http-foundation/composer.json create mode 100644 vendor/symfony/http-foundation/phpunit.xml.dist create mode 100644 vendor/symfony/http-kernel/.gitignore create mode 100644 vendor/symfony/http-kernel/Bundle/Bundle.php create mode 100644 vendor/symfony/http-kernel/Bundle/BundleInterface.php create mode 100644 vendor/symfony/http-kernel/CHANGELOG.md create mode 100644 vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php create mode 100644 vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php create mode 100644 vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php create mode 100644 vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php create mode 100644 vendor/symfony/http-kernel/Client.php create mode 100644 vendor/symfony/http-kernel/Config/FileLocator.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php create mode 100644 vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php create mode 100644 vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ControllerReference.php create mode 100644 vendor/symfony/http-kernel/Controller/ControllerResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php create mode 100644 vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php create mode 100644 vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php create mode 100644 vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php create mode 100644 vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php create mode 100644 vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php create mode 100644 vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/DataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php create mode 100644 vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/EventDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php create mode 100644 vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php create mode 100644 vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php create mode 100644 vendor/symfony/http-kernel/Debug/FileLinkFormatter.php create mode 100644 vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/Extension.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php create mode 100644 vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php create mode 100644 vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php create mode 100644 vendor/symfony/http-kernel/Event/FilterControllerEvent.php create mode 100644 vendor/symfony/http-kernel/Event/FilterResponseEvent.php create mode 100644 vendor/symfony/http-kernel/Event/FinishRequestEvent.php create mode 100644 vendor/symfony/http-kernel/Event/GetResponseEvent.php create mode 100644 vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php create mode 100644 vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php create mode 100644 vendor/symfony/http-kernel/Event/KernelEvent.php create mode 100644 vendor/symfony/http-kernel/Event/PostResponseEvent.php create mode 100644 vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/DumpListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ExceptionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/FragmentListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/LocaleListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ProfilerListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ResponseListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/RouterListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/SaveSessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/SessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/SurrogateListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/TestSessionListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/TranslatorListener.php create mode 100644 vendor/symfony/http-kernel/EventListener/ValidateRequestListener.php create mode 100644 vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/BadRequestHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/ConflictHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/GoneHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/HttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php create mode 100644 vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/NotFoundHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php create mode 100644 vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php create mode 100644 vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/FragmentHandler.php create mode 100644 vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php create mode 100644 vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php create mode 100644 vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php create mode 100644 vendor/symfony/http-kernel/HttpCache/Esi.php create mode 100644 vendor/symfony/http-kernel/HttpCache/HttpCache.php create mode 100644 vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php create mode 100644 vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php create mode 100644 vendor/symfony/http-kernel/HttpCache/Ssi.php create mode 100644 vendor/symfony/http-kernel/HttpCache/Store.php create mode 100644 vendor/symfony/http-kernel/HttpCache/StoreInterface.php create mode 100644 vendor/symfony/http-kernel/HttpCache/SubRequestHandler.php create mode 100644 vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php create mode 100644 vendor/symfony/http-kernel/HttpKernel.php create mode 100644 vendor/symfony/http-kernel/HttpKernelInterface.php create mode 100644 vendor/symfony/http-kernel/Kernel.php create mode 100644 vendor/symfony/http-kernel/KernelEvents.php create mode 100644 vendor/symfony/http-kernel/KernelInterface.php create mode 100644 vendor/symfony/http-kernel/LICENSE create mode 100644 vendor/symfony/http-kernel/Log/DebugLoggerInterface.php create mode 100644 vendor/symfony/http-kernel/Log/Logger.php create mode 100644 vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php create mode 100644 vendor/symfony/http-kernel/Profiler/Profile.php create mode 100644 vendor/symfony/http-kernel/Profiler/Profiler.php create mode 100644 vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php create mode 100644 vendor/symfony/http-kernel/README.md create mode 100644 vendor/symfony/http-kernel/RebootableInterface.php create mode 100644 vendor/symfony/http-kernel/Resources/welcome.html.php create mode 100644 vendor/symfony/http-kernel/TerminableInterface.php create mode 100644 vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php create mode 100644 vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/ClientTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php create mode 100644 vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php create mode 100644 vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php create mode 100644 vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/BadRequestHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/ConflictHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/GoneHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/HttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/MethodNotAllowedHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/NotFoundHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/PreconditionFailedHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/PreconditionRequiredHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/ServiceUnavailableHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Exception/UnsupportedMediaTypeHttpExceptionTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/123/Kernel123.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Controller/BasicTypesController.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/FooCommand.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/BaseBundle/hide.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/ChildBundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/Resources/FooBundle/foo.txt create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/TestClient.php create mode 100644 vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/SubRequestHandlerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php create mode 100644 vendor/symfony/http-kernel/Tests/HttpKernelTest.php create mode 100644 vendor/symfony/http-kernel/Tests/KernelTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Log/LoggerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Logger.php create mode 100644 vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php create mode 100644 vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php create mode 100644 vendor/symfony/http-kernel/Tests/TestHttpKernel.php create mode 100644 vendor/symfony/http-kernel/Tests/UriSignerTest.php create mode 100644 vendor/symfony/http-kernel/UriSigner.php create mode 100644 vendor/symfony/http-kernel/composer.json create mode 100644 vendor/symfony/http-kernel/phpunit.xml.dist create mode 100644 vendor/symfony/inflector/Inflector.php create mode 100644 vendor/symfony/inflector/LICENSE create mode 100644 vendor/symfony/inflector/README.md create mode 100644 vendor/symfony/inflector/Tests/InflectorTest.php create mode 100644 vendor/symfony/inflector/composer.json create mode 100644 vendor/symfony/inflector/phpunit.xml.dist create mode 100644 vendor/symfony/monolog-bridge/.gitignore create mode 100644 vendor/symfony/monolog-bridge/CHANGELOG.md create mode 100644 vendor/symfony/monolog-bridge/Formatter/ConsoleFormatter.php create mode 100644 vendor/symfony/monolog-bridge/Formatter/VarDumperFormatter.php create mode 100644 vendor/symfony/monolog-bridge/Handler/ChromePhpHandler.php create mode 100644 vendor/symfony/monolog-bridge/Handler/ConsoleHandler.php create mode 100644 vendor/symfony/monolog-bridge/Handler/FingersCrossed/HttpCodeActivationStrategy.php create mode 100644 vendor/symfony/monolog-bridge/Handler/FingersCrossed/NotFoundActivationStrategy.php create mode 100644 vendor/symfony/monolog-bridge/Handler/FirePHPHandler.php create mode 100644 vendor/symfony/monolog-bridge/Handler/ServerLogHandler.php create mode 100644 vendor/symfony/monolog-bridge/Handler/SwiftMailerHandler.php create mode 100644 vendor/symfony/monolog-bridge/LICENSE create mode 100644 vendor/symfony/monolog-bridge/Logger.php create mode 100644 vendor/symfony/monolog-bridge/Processor/DebugProcessor.php create mode 100644 vendor/symfony/monolog-bridge/Processor/TokenProcessor.php create mode 100644 vendor/symfony/monolog-bridge/Processor/WebProcessor.php create mode 100644 vendor/symfony/monolog-bridge/README.md create mode 100644 vendor/symfony/monolog-bridge/Tests/Handler/ConsoleHandlerTest.php create mode 100644 vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php create mode 100644 vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php create mode 100644 vendor/symfony/monolog-bridge/Tests/LoggerTest.php create mode 100644 vendor/symfony/monolog-bridge/Tests/Processor/DebugProcessorTest.php create mode 100644 vendor/symfony/monolog-bridge/Tests/Processor/TokenProcessorTest.php create mode 100644 vendor/symfony/monolog-bridge/Tests/Processor/WebProcessorTest.php create mode 100644 vendor/symfony/monolog-bridge/composer.json create mode 100644 vendor/symfony/monolog-bridge/phpunit.xml.dist create mode 100644 vendor/symfony/options-resolver/.gitignore create mode 100644 vendor/symfony/options-resolver/CHANGELOG.md create mode 100644 vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php create mode 100644 vendor/symfony/options-resolver/Exception/AccessException.php create mode 100644 vendor/symfony/options-resolver/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/options-resolver/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/options-resolver/Exception/InvalidOptionsException.php create mode 100644 vendor/symfony/options-resolver/Exception/MissingOptionsException.php create mode 100644 vendor/symfony/options-resolver/Exception/NoConfigurationException.php create mode 100644 vendor/symfony/options-resolver/Exception/NoSuchOptionException.php create mode 100644 vendor/symfony/options-resolver/Exception/OptionDefinitionException.php create mode 100644 vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php create mode 100644 vendor/symfony/options-resolver/LICENSE create mode 100644 vendor/symfony/options-resolver/Options.php create mode 100644 vendor/symfony/options-resolver/OptionsResolver.php create mode 100644 vendor/symfony/options-resolver/README.md create mode 100644 vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php create mode 100644 vendor/symfony/options-resolver/Tests/OptionsResolverTest.php create mode 100644 vendor/symfony/options-resolver/composer.json create mode 100644 vendor/symfony/options-resolver/phpunit.xml.dist create mode 100644 vendor/symfony/polyfill-ctype/Ctype.php create mode 100644 vendor/symfony/polyfill-ctype/LICENSE create mode 100644 vendor/symfony/polyfill-ctype/README.md create mode 100644 vendor/symfony/polyfill-ctype/bootstrap.php create mode 100644 vendor/symfony/polyfill-ctype/composer.json create mode 100644 vendor/symfony/polyfill-mbstring/LICENSE create mode 100644 vendor/symfony/polyfill-mbstring/Mbstring.php create mode 100644 vendor/symfony/polyfill-mbstring/README.md create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php create mode 100644 vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php create mode 100644 vendor/symfony/polyfill-mbstring/bootstrap.php create mode 100644 vendor/symfony/polyfill-mbstring/composer.json create mode 100644 vendor/symfony/polyfill-php70/LICENSE create mode 100644 vendor/symfony/polyfill-php70/Php70.php create mode 100644 vendor/symfony/polyfill-php70/README.md create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/AssertionError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/Error.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/ParseError.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php create mode 100644 vendor/symfony/polyfill-php70/Resources/stubs/TypeError.php create mode 100644 vendor/symfony/polyfill-php70/bootstrap.php create mode 100644 vendor/symfony/polyfill-php70/composer.json create mode 100644 vendor/symfony/property-access/.gitignore create mode 100644 vendor/symfony/property-access/CHANGELOG.md create mode 100644 vendor/symfony/property-access/Exception/AccessException.php create mode 100644 vendor/symfony/property-access/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/property-access/Exception/InvalidArgumentException.php create mode 100644 vendor/symfony/property-access/Exception/InvalidPropertyPathException.php create mode 100644 vendor/symfony/property-access/Exception/NoSuchIndexException.php create mode 100644 vendor/symfony/property-access/Exception/NoSuchPropertyException.php create mode 100644 vendor/symfony/property-access/Exception/OutOfBoundsException.php create mode 100644 vendor/symfony/property-access/Exception/RuntimeException.php create mode 100644 vendor/symfony/property-access/Exception/UnexpectedTypeException.php create mode 100644 vendor/symfony/property-access/LICENSE create mode 100644 vendor/symfony/property-access/PropertyAccess.php create mode 100644 vendor/symfony/property-access/PropertyAccessor.php create mode 100644 vendor/symfony/property-access/PropertyAccessorBuilder.php create mode 100644 vendor/symfony/property-access/PropertyAccessorInterface.php create mode 100644 vendor/symfony/property-access/PropertyPath.php create mode 100644 vendor/symfony/property-access/PropertyPathBuilder.php create mode 100644 vendor/symfony/property-access/PropertyPathInterface.php create mode 100644 vendor/symfony/property-access/PropertyPathIterator.php create mode 100644 vendor/symfony/property-access/PropertyPathIteratorInterface.php create mode 100644 vendor/symfony/property-access/README.md create mode 100644 vendor/symfony/property-access/StringUtil.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/NonTraversableArrayObject.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/ReturnTyped.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TestClass.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TestClassIsWritable.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TestClassMagicCall.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TestClassMagicGet.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TestClassSetValue.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TestClassTypeErrorInsideCall.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/Ticket5775Object.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TraversableArrayObject.php create mode 100644 vendor/symfony/property-access/Tests/Fixtures/TypeHinted.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorArrayAccessTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorArrayObjectTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorArrayTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorBuilderTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorCollectionTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorNonTraversableArrayObjectTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyAccessorTraversableArrayObjectTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyPathBuilderTest.php create mode 100644 vendor/symfony/property-access/Tests/PropertyPathTest.php create mode 100644 vendor/symfony/property-access/Tests/StringUtilTest.php create mode 100644 vendor/symfony/property-access/composer.json create mode 100644 vendor/symfony/property-access/phpunit.xml.dist create mode 100644 vendor/symfony/routing/.gitignore create mode 100644 vendor/symfony/routing/Annotation/Route.php create mode 100644 vendor/symfony/routing/CHANGELOG.md create mode 100644 vendor/symfony/routing/CompiledRoute.php create mode 100644 vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php create mode 100644 vendor/symfony/routing/Exception/ExceptionInterface.php create mode 100644 vendor/symfony/routing/Exception/InvalidParameterException.php create mode 100644 vendor/symfony/routing/Exception/MethodNotAllowedException.php create mode 100644 vendor/symfony/routing/Exception/MissingMandatoryParametersException.php create mode 100644 vendor/symfony/routing/Exception/NoConfigurationException.php create mode 100644 vendor/symfony/routing/Exception/ResourceNotFoundException.php create mode 100644 vendor/symfony/routing/Exception/RouteNotFoundException.php create mode 100644 vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php create mode 100644 vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php create mode 100644 vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php create mode 100644 vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php create mode 100644 vendor/symfony/routing/Generator/UrlGenerator.php create mode 100644 vendor/symfony/routing/Generator/UrlGeneratorInterface.php create mode 100644 vendor/symfony/routing/LICENSE create mode 100644 vendor/symfony/routing/Loader/AnnotationClassLoader.php create mode 100644 vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php create mode 100644 vendor/symfony/routing/Loader/AnnotationFileLoader.php create mode 100644 vendor/symfony/routing/Loader/ClosureLoader.php create mode 100644 vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php create mode 100644 vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php create mode 100644 vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php create mode 100644 vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php create mode 100644 vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php create mode 100644 vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php create mode 100644 vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php create mode 100644 vendor/symfony/routing/Loader/DirectoryLoader.php create mode 100644 vendor/symfony/routing/Loader/GlobFileLoader.php create mode 100644 vendor/symfony/routing/Loader/ObjectRouteLoader.php create mode 100644 vendor/symfony/routing/Loader/PhpFileLoader.php create mode 100644 vendor/symfony/routing/Loader/XmlFileLoader.php create mode 100644 vendor/symfony/routing/Loader/YamlFileLoader.php create mode 100644 vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd create mode 100644 vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php create mode 100644 vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php create mode 100644 vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php create mode 100644 vendor/symfony/routing/Matcher/RequestMatcherInterface.php create mode 100644 vendor/symfony/routing/Matcher/TraceableUrlMatcher.php create mode 100644 vendor/symfony/routing/Matcher/UrlMatcher.php create mode 100644 vendor/symfony/routing/Matcher/UrlMatcherInterface.php create mode 100644 vendor/symfony/routing/README.md create mode 100644 vendor/symfony/routing/RequestContext.php create mode 100644 vendor/symfony/routing/RequestContextAwareInterface.php create mode 100644 vendor/symfony/routing/Route.php create mode 100644 vendor/symfony/routing/RouteCollection.php create mode 100644 vendor/symfony/routing/RouteCollectionBuilder.php create mode 100644 vendor/symfony/routing/RouteCompiler.php create mode 100644 vendor/symfony/routing/RouteCompilerInterface.php create mode 100644 vendor/symfony/routing/Router.php create mode 100644 vendor/symfony/routing/RouterInterface.php create mode 100644 vendor/symfony/routing/Tests/Annotation/RouteTest.php create mode 100644 vendor/symfony/routing/Tests/CompiledRouteTest.php create mode 100644 vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/AbstractClassController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/ActionPathController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/DefaultValueController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/ExplicitLocalizedActionPathController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/InvokableController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/InvokableLocalizedController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedActionPathController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedMethodActionControllers.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixLocalizedActionController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixMissingLocaleActionController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixMissingRouteLocaleActionController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/LocalizedPrefixWithRouteWithoutLocale.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/MethodActionControllers.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/MissingRouteNameController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/NothingButNameController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/PrefixedActionLocalizedRouteController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/PrefixedActionPathController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/AnnotationFixtures/RouteWithPrefixController.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/CustomCompiledRoute.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/annotated.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/bad_format.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/bar.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/routing.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/controller/routing.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher10.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher11.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher12.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher13.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher8.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher9.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/empty.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/file_resource.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/foo.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/foo1.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/bar.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/bar.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/baz.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/baz.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/import_single.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/import_single.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/php_dsl.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_bar.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_baz.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/incomplete.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/list_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/list_null_values.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/importer-with-controller-default.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/importing-localized-route.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/localized-route.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/missing-locale-in-importer.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/not-localized.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/officially_formatted_locales.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/localized/route-without-path-or-locales.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/map_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/map_null_values.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/missing_id.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/missing_path.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalid.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalid.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/null_values.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/php_dsl.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/php_dsl_i18n.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_i18n.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_root.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/php_object_dsl.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/special_route_name.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validpattern.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/validpattern.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validpattern.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validresource.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/validresource.xml create mode 100644 vendor/symfony/routing/Tests/Fixtures/validresource.yml create mode 100644 vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php create mode 100644 vendor/symfony/routing/Tests/Fixtures/withdoctype.xml create mode 100644 vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php create mode 100644 vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/FileLocatorStub.php create mode 100644 vendor/symfony/routing/Tests/Loader/GlobFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/DumpedUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php create mode 100644 vendor/symfony/routing/Tests/RequestContextTest.php create mode 100644 vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php create mode 100644 vendor/symfony/routing/Tests/RouteCollectionTest.php create mode 100644 vendor/symfony/routing/Tests/RouteCompilerTest.php create mode 100644 vendor/symfony/routing/Tests/RouteTest.php create mode 100644 vendor/symfony/routing/Tests/RouterTest.php create mode 100644 vendor/symfony/routing/composer.json create mode 100644 vendor/symfony/routing/phpunit.xml.dist create mode 100644 vendor/webmozart/assert/.composer-auth.json create mode 100644 vendor/webmozart/assert/.styleci.yml create mode 100644 vendor/webmozart/assert/CHANGELOG.md create mode 100644 vendor/webmozart/assert/LICENSE create mode 100644 vendor/webmozart/assert/README.md create mode 100644 vendor/webmozart/assert/composer.json create mode 100644 vendor/webmozart/assert/src/Assert.php create mode 100644 vendor/winzou/state-machine/.gitignore create mode 100644 vendor/winzou/state-machine/.travis.yml create mode 100644 vendor/winzou/state-machine/LICENSE create mode 100644 vendor/winzou/state-machine/README.md create mode 100644 vendor/winzou/state-machine/composer.json create mode 100644 vendor/winzou/state-machine/examples/DomainObject.php create mode 100644 vendor/winzou/state-machine/examples/simple.php create mode 100644 vendor/winzou/state-machine/spec/SM/Callback/CallbackSpec.php create mode 100644 vendor/winzou/state-machine/spec/SM/Callback/CascadeTransitionCallbackSpec.php create mode 100644 vendor/winzou/state-machine/spec/SM/DummyObject.php create mode 100644 vendor/winzou/state-machine/spec/SM/Extension/Twig/SMExtensionSpec.php create mode 100644 vendor/winzou/state-machine/spec/SM/Factory/FactorySpec.php create mode 100644 vendor/winzou/state-machine/spec/SM/StateMachine/StateMachineSpec.php create mode 100644 vendor/winzou/state-machine/src/SM/Callback/Callback.php create mode 100644 vendor/winzou/state-machine/src/SM/Callback/CallbackFactory.php create mode 100644 vendor/winzou/state-machine/src/SM/Callback/CallbackFactoryInterface.php create mode 100644 vendor/winzou/state-machine/src/SM/Callback/CallbackInterface.php create mode 100644 vendor/winzou/state-machine/src/SM/Callback/CascadeTransitionCallback.php create mode 100644 vendor/winzou/state-machine/src/SM/Event/SMEvents.php create mode 100644 vendor/winzou/state-machine/src/SM/Event/TransitionEvent.php create mode 100644 vendor/winzou/state-machine/src/SM/Extension/Twig/SMExtension.php create mode 100644 vendor/winzou/state-machine/src/SM/Factory/AbstractFactory.php create mode 100644 vendor/winzou/state-machine/src/SM/Factory/ClearableFactoryInterface.php create mode 100644 vendor/winzou/state-machine/src/SM/Factory/Factory.php create mode 100644 vendor/winzou/state-machine/src/SM/Factory/FactoryInterface.php create mode 100644 vendor/winzou/state-machine/src/SM/SMException.php create mode 100644 vendor/winzou/state-machine/src/SM/StateMachine/StateMachine.php create mode 100644 vendor/winzou/state-machine/src/SM/StateMachine/StateMachineInterface.php create mode 100644 vendor/zendframework/zend-stdlib/CHANGELOG.md create mode 100644 vendor/zendframework/zend-stdlib/LICENSE.md create mode 100644 vendor/zendframework/zend-stdlib/README.md create mode 100644 vendor/zendframework/zend-stdlib/composer.json create mode 100644 vendor/zendframework/zend-stdlib/src/AbstractOptions.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArrayObject.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArraySerializableInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArrayStack.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArrayUtils.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeRemoveKey.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeReplaceKey.php create mode 100644 vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeReplaceKeyInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/ConsoleHelper.php create mode 100644 vendor/zendframework/zend-stdlib/src/DispatchableInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/ErrorHandler.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/BadMethodCallException.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/DomainException.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/ExceptionInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/ExtensionNotLoadedException.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/InvalidArgumentException.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/LogicException.php create mode 100644 vendor/zendframework/zend-stdlib/src/Exception/RuntimeException.php create mode 100644 vendor/zendframework/zend-stdlib/src/FastPriorityQueue.php create mode 100644 vendor/zendframework/zend-stdlib/src/Glob.php create mode 100644 vendor/zendframework/zend-stdlib/src/Guard/AllGuardsTrait.php create mode 100644 vendor/zendframework/zend-stdlib/src/Guard/ArrayOrTraversableGuardTrait.php create mode 100644 vendor/zendframework/zend-stdlib/src/Guard/EmptyGuardTrait.php create mode 100644 vendor/zendframework/zend-stdlib/src/Guard/NullGuardTrait.php create mode 100644 vendor/zendframework/zend-stdlib/src/InitializableInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/JsonSerializable.php create mode 100644 vendor/zendframework/zend-stdlib/src/Message.php create mode 100644 vendor/zendframework/zend-stdlib/src/MessageInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/ParameterObjectInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/Parameters.php create mode 100644 vendor/zendframework/zend-stdlib/src/ParametersInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/PriorityList.php create mode 100644 vendor/zendframework/zend-stdlib/src/PriorityQueue.php create mode 100644 vendor/zendframework/zend-stdlib/src/Request.php create mode 100644 vendor/zendframework/zend-stdlib/src/RequestInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/Response.php create mode 100644 vendor/zendframework/zend-stdlib/src/ResponseInterface.php create mode 100644 vendor/zendframework/zend-stdlib/src/SplPriorityQueue.php create mode 100644 vendor/zendframework/zend-stdlib/src/SplQueue.php create mode 100644 vendor/zendframework/zend-stdlib/src/SplStack.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringUtils.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringWrapper/AbstractStringWrapper.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringWrapper/Iconv.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringWrapper/Intl.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringWrapper/MbString.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringWrapper/Native.php create mode 100644 vendor/zendframework/zend-stdlib/src/StringWrapper/StringWrapperInterface.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/AbstractExampleFactory.php b/AbstractExampleFactory.php new file mode 100644 index 0000000..e1ade8b --- /dev/null +++ b/AbstractExampleFactory.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace PhpMob\DataFixture; + +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * @author Jan Góralski + */ +abstract class AbstractExampleFactory implements ExampleFactoryInterface +{ + /** + * @param OptionsResolver $resolver + */ + abstract protected function configureOptions(OptionsResolver $resolver); +} diff --git a/AbstractResourceFixture.php b/AbstractResourceFixture.php new file mode 100644 index 0000000..e8ada5d --- /dev/null +++ b/AbstractResourceFixture.php @@ -0,0 +1,122 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace PhpMob\DataFixture; + +use Doctrine\Common\Persistence\ObjectManager; +use Sylius\Bundle\FixturesBundle\Fixture\FixtureInterface; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * @author Kamil Kokot + */ +abstract class AbstractResourceFixture implements FixtureInterface +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var ExampleFactoryInterface + */ + private $exampleFactory; + + /** + * @var OptionsResolver + */ + private $optionsResolver; + + /** + * @param ObjectManager $objectManager + * @param ExampleFactoryInterface $exampleFactory + */ + public function __construct(ObjectManager $objectManager, ExampleFactoryInterface $exampleFactory) + { + $this->objectManager = $objectManager; + $this->exampleFactory = $exampleFactory; + + $this->optionsResolver = + (new OptionsResolver()) + ->setDefault('random', 0) + ->setAllowedTypes('random', 'int') + ->setDefault('prototype', []) + ->setAllowedTypes('prototype', 'array') + ->setDefault('custom', []) + ->setAllowedTypes('custom', 'array') + ->setNormalizer('custom', function (Options $options, array $custom) { + if ($options['random'] <= 0) { + return $custom; + } + + return array_merge($custom, array_fill(0, $options['random'], $options['prototype'])); + }) + ; + } + + /** + * @param array $options + */ + final public function load(array $options): void + { + $options = $this->optionsResolver->resolve($options); + + $i = 0; + foreach ($options['custom'] as $resourceOptions) { + $resource = $this->exampleFactory->create($resourceOptions); + + $this->objectManager->persist($resource); + + ++$i; + + if (0 === ($i % 10)) { + $this->objectManager->flush(); + $this->objectManager->clear(); + } + } + + $this->objectManager->flush(); + $this->objectManager->clear(); + } + + /** + * {@inheritdoc} + */ + final public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $optionsNode = $treeBuilder->root($this->getName()); + + $optionsNode->children()->integerNode('random')->min(0)->defaultValue(0); + + /** @var ArrayNodeDefinition $resourcesNode */ + $resourcesNode = $optionsNode->children()->arrayNode('custom'); + + /** @var ArrayNodeDefinition $resourceNode */ + $resourceNode = $resourcesNode->requiresAtLeastOneElement()->prototype('array'); + $this->configureResourceNode($resourceNode); + + return $treeBuilder; + } + + /** + * @param ArrayNodeDefinition $resourceNode + */ + protected function configureResourceNode(ArrayNodeDefinition $resourceNode) + { + // empty + } +} diff --git a/ExampleFactoryInterface.php b/ExampleFactoryInterface.php new file mode 100644 index 0000000..f515cb9 --- /dev/null +++ b/ExampleFactoryInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace PhpMob\DataFixture; + +/** + * @author Kamil Kokot + */ +interface ExampleFactoryInterface +{ + /** + * @param array $options + * + * @return object + */ + public function create(array $options = []); +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ff5e6e6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 liverbool + +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. \ No newline at end of file diff --git a/LocaleAwareFactoryTrait.php b/LocaleAwareFactoryTrait.php new file mode 100644 index 0000000..7145052 --- /dev/null +++ b/LocaleAwareFactoryTrait.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace PhpMob\DataFixture; + +use Sylius\Component\Resource\Model\TranslatableInterface; +use Sylius\Component\Resource\Translation\Provider\TranslationLocaleProviderInterface; +use Symfony\Component\PropertyAccess\PropertyAccess; + +trait LocaleAwareFactoryTrait +{ + /** + * @var TranslationLocaleProviderInterface + */ + private $translationProvider; + + /** + * @param TranslationLocaleProviderInterface $translationProvider + */ + public function setTranslationProvider(TranslationLocaleProviderInterface $translationProvider) + { + $this->translationProvider = $translationProvider; + } + + /** + * @param TranslatableInterface $object + * @param array $properties + * @param array $data + * + * @throws \TypeError + */ + protected function setLocalizedData(TranslatableInterface $object, array $properties, array $data) + { + $accessor = PropertyAccess::createPropertyAccessor(); + + foreach ($this->translationProvider->getDefinedLocalesCodes() as $localeCode) { + $object->setCurrentLocale($localeCode); + $object->setFallbackLocale($localeCode); + + foreach ($properties as $property) { + $value = $data[$property]; + $value = is_string($value) + ? $value + : $value[$localeCode]; + + $accessor->setValue($object, $property, $value); + } + } + } +} diff --git a/OptionsResolver/LazyOption.php b/OptionsResolver/LazyOption.php new file mode 100644 index 0000000..8d19d0b --- /dev/null +++ b/OptionsResolver/LazyOption.php @@ -0,0 +1,170 @@ +setDefault('option', LazyOption::randomOne($repository)) + * ->setNormalizer('option', LazyOption::findOneBy($repository, 'code')) + * ; + * + * Returns: + * - null if user explicitly set it (['option' => null]) + * - random one if user skipped that option ([]) + * - specific one if user defined that option (['option' => 'CODE']) + */ +final class LazyOption +{ + /** + * @param RepositoryInterface $repository + * + * @return \Closure + */ + public static function randomOne(RepositoryInterface $repository): \Closure + { + return function (Options $options) use ($repository) { + $objects = $repository->findAll(); + + if ($objects instanceof Collection) { + $objects = $objects->toArray(); + } + + Assert::notEmpty($objects); + + return $objects[array_rand($objects)]; + }; + } + + /** + * @param RepositoryInterface $repository + * @param int $chanceOfRandomOne + * + * @return \Closure + */ + public static function randomOneOrNull(RepositoryInterface $repository, int $chanceOfRandomOne): \Closure + { + return function (Options $options) use ($repository, $chanceOfRandomOne) { + if (mt_rand(1, 100) > $chanceOfRandomOne) { + return null; + } + + $objects = $repository->findAll(); + + if ($objects instanceof Collection) { + $objects = $objects->toArray(); + } + + return 0 === count($objects) ? null : $objects[array_rand($objects)]; + }; + } + + /** + * @param RepositoryInterface $repository + * @param int $amount + * + * @return \Closure + */ + public static function randomOnes(RepositoryInterface $repository, int $amount): \Closure + { + return function (Options $options) use ($repository, $amount) { + $objects = $repository->findAll(); + + if ($objects instanceof Collection) { + $objects = $objects->toArray(); + } + + $selectedObjects = []; + for (; $amount > 0 && count($objects) > 0; --$amount) { + $randomKey = array_rand($objects); + + $selectedObjects[] = $objects[$randomKey]; + + unset($objects[$randomKey]); + } + + return $selectedObjects; + }; + } + + /** + * @param RepositoryInterface $repository + * + * @return \Closure + */ + public static function all(RepositoryInterface $repository): \Closure + { + return function (Options $options) use ($repository) { + return $repository->findAll(); + }; + } + + /** + * @param RepositoryInterface $repository + * @param string $field + * + * @return \Closure + */ + public static function findBy(RepositoryInterface $repository, string $field): \Closure + { + return function (Options $options, $previousValues) use ($repository, $field) { + if (null === $previousValues || [] === $previousValues) { + return $previousValues; + } + + Assert::isArray($previousValues); + + $resources = []; + foreach ($previousValues as $previousValue) { + if (is_object($previousValue)) { + $resources[] = $previousValue; + } else { + $resources[] = $repository->findOneBy([$field => $previousValue]); + } + } + + return $resources; + }; + } + + /** + * @param RepositoryInterface $repository + * @param string $field + * + * @return \Closure + */ + public static function findOneBy(RepositoryInterface $repository, string $field): \Closure + { + return function (Options $options, $previousValue) use ($repository, $field) { + if (null === $previousValue || [] === $previousValue) { + return $previousValue; + } + + if (is_object($previousValue)) { + return $previousValue; + } + + return $repository->findOneBy([$field => $previousValue]); + }; + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f984f6 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# data-fixture diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..df3a4eb --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "name": "phpmob/data-fixture", + "type": "library", + "description": "Just abstraction data fixture.", + "keywords": ["data-fixture"], + "homepage": "http://github.com/phpmob/data-fixture", + "license": "MIT", + "authors": [ + { + "name": "Ishmael Doss", + "email": "nukboon@gmail.com" + } + ], + "require": { + "php": ">=7.1", + "doctrine/common": "^2.4", + "sylius/resource": "^1.0", + "sylius/fixtures-bundle": "^1.2", + "symfony/config": "^3.2", + "symfony/options-resolver": "^3.2", + "symfony/property-access": "^3.2", + "webmozart/assert": "^1.3" + }, + "require-dev": { + }, + "autoload": { + "psr-0": { + "PhpMob\\DataFixture": "" + } + }, + "target-dir": "PhpMob/DataFixture" +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..9cbd2ac --- /dev/null +++ b/composer.lock @@ -0,0 +1,2809 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "content-hash": "f410edda33c27f205a2259023a36370d", + "packages": [ + { + "name": "behat/transliterator", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "psr-0": { + "Behat\\Transliterator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ], + "time": "2017-04-04T11:38:05+00:00" + }, + { + "name": "doctrine/annotations", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2017-12-06T07:11:42+00:00" + }, + { + "name": "doctrine/cache", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", + "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "shasum": "" + }, + "require": { + "php": "~7.1" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "^1.1", + "doctrine/coding-standard": "^4.0", + "mongodb/mongodb": "^1.1", + "phpunit/phpunit": "^7.0", + "predis/predis": "~1.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2018-08-21T18:01:43+00:00" + }, + { + "name": "doctrine/collections", + "version": "v1.5.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2017-07-22T10:37:32+00:00" + }, + { + "name": "doctrine/common", + "version": "v2.9.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a210246d286c77d2b89040f8691ba7b3a713d2c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a210246d286c77d2b89040f8691ba7b3a713d2c1", + "reference": "a210246d286c77d2b89040f8691ba7b3a713d2c1", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "doctrine/cache": "^1.0", + "doctrine/collections": "^1.0", + "doctrine/event-manager": "^1.0", + "doctrine/inflector": "^1.0", + "doctrine/lexer": "^1.0", + "doctrine/persistence": "^1.0", + "doctrine/reflection": "^1.0", + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^1.0", + "phpunit/phpunit": "^6.3", + "squizlabs/php_codesniffer": "^3.0", + "symfony/phpunit-bridge": "^4.0.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2018-07-12T21:16:12+00:00" + }, + { + "name": "doctrine/data-fixtures", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a", + "reference": "3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.2", + "php": "^7.1" + }, + "conflict": { + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "doctrine/dbal": "^2.5.4", + "doctrine/orm": "^2.5.4", + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM with PHP 7", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database" + ], + "time": "2018-03-20T09:06:36+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Event Manager component", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "eventdispatcher", + "eventmanager" + ], + "time": "2018-06-11T11:59:03+00:00" + }, + { + "name": "doctrine/inflector", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2018-01-09T20:05:19+00:00" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09T13:34:57+00:00" + }, + { + "name": "doctrine/persistence", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/persistence.git", + "reference": "af1ec238659a83e320f03e0e454e200f689b4b97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/af1ec238659a83e320f03e0e454e200f689b4b97", + "reference": "af1ec238659a83e320f03e0e454e200f689b4b97", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "doctrine/cache": "^1.0", + "doctrine/collections": "^1.0", + "doctrine/event-manager": "^1.0", + "doctrine/reflection": "^1.0", + "php": "^7.1" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "phpstan/phpstan": "^0.8", + "phpunit/phpunit": "^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Persistence abstractions.", + "homepage": "https://doctrine-project.org/projects/persistence.html", + "keywords": [ + "persistence" + ], + "time": "2018-07-12T12:37:50+00:00" + }, + { + "name": "doctrine/reflection", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/reflection.git", + "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/reflection/zipball/02538d3f95e88eb397a5f86274deb2c6175c2ab6", + "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "doctrine/common": "^2.8", + "phpstan/phpstan": "^0.9.2", + "phpstan/phpstan-phpunit": "^0.9.4", + "phpunit/phpunit": "^7.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Reflection component", + "homepage": "https://www.doctrine-project.org/projects/reflection.html", + "keywords": [ + "reflection" + ], + "time": "2018-06-14T14:45:07+00:00" + }, + { + "name": "gedmo/doctrine-extensions", + "version": "v2.4.36", + "source": { + "type": "git", + "url": "https://github.com/Atlantic18/DoctrineExtensions.git", + "reference": "87c78ff9fd4b90460386f753d95622f6fbbfcb27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Atlantic18/DoctrineExtensions/zipball/87c78ff9fd4b90460386f753d95622f6fbbfcb27", + "reference": "87c78ff9fd4b90460386f753d95622f6fbbfcb27", + "shasum": "" + }, + "require": { + "behat/transliterator": "~1.2", + "doctrine/common": "~2.4", + "php": ">=5.3.2" + }, + "conflict": { + "doctrine/annotations": "<1.2" + }, + "require-dev": { + "doctrine/common": ">=2.5.0", + "doctrine/mongodb-odm": ">=1.0.2", + "doctrine/orm": ">=2.5.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.5", + "symfony/yaml": "~2.6|~3.0|~4.0" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Gedmo\\": "lib/Gedmo" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Buchmann", + "email": "david@liip.ch" + }, + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + } + ], + "description": "Doctrine2 behavioral extensions", + "homepage": "http://gediminasm.org/", + "keywords": [ + "Blameable", + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "loggable", + "nestedset", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree", + "uploadable" + ], + "time": "2018-07-26T12:16:35+00:00" + }, + { + "name": "monolog/monolog", + "version": "1.23.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2017-06-19T01:22:40+00:00" + }, + { + "name": "pagerfanta/pagerfanta", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/whiteoctober/Pagerfanta.git", + "reference": "15770d9d7f6e8e07af568aed104a51f869591e73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/15770d9d7f6e8e07af568aed104a51f869591e73", + "reference": "15770d9d7f6e8e07af568aed104a51f869591e73", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "doctrine/phpcr-odm": "1.*", + "jackalope/jackalope-doctrine-dbal": "1.*", + "jmikola/geojson": "~1.0", + "mandango/mandango": "~1.0@dev", + "mandango/mondator": "~1.0@dev", + "phpunit/phpunit": "^5.7|^6", + "propel/propel": "~2.0@dev", + "propel/propel1": "~1.6", + "ruflin/elastica": "~1.3", + "solarium/solarium": "~3.1" + }, + "suggest": { + "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.", + "doctrine/orm": "To use the DoctrineORMAdapter.", + "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0", + "mandango/mandango": "To use the MandangoAdapter.", + "propel/propel": "To use the Propel2Adapter", + "propel/propel1": "To use the PropelAdapter", + "solarium/solarium": "To use the SolariumAdapter." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Pagerfanta\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pablo Díez", + "email": "pablodip@gmail.com" + } + ], + "description": "Pagination for PHP 5.3", + "keywords": [ + "page", + "pagination", + "paginator", + "paging" + ], + "time": "2018-05-17T09:23:52+00:00" + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.99", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "shasum": "" + }, + "require": { + "php": "^7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "time": "2018-07-02T15:55:56+00:00" + }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" + }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "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-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/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2016-10-10T12:19:37+00:00" + }, + { + "name": "psr/simple-cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "time": "2017-10-23T01:57:42+00:00" + }, + { + "name": "sylius/fixtures-bundle", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/Sylius/SyliusFixturesBundle.git", + "reference": "bc930b9cfc9c8d96a3f46543be56bdb4e5badfe4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Sylius/SyliusFixturesBundle/zipball/bc930b9cfc9c8d96a3f46543be56bdb4e5badfe4", + "reference": "bc930b9cfc9c8d96a3f46543be56bdb4e5badfe4", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "^1.2", + "monolog/monolog": "^1.8", + "php": "^7.2", + "symfony/framework-bundle": "^3.4|^4.1.1", + "symfony/monolog-bridge": "^3.4|^4.1.1", + "webmozart/assert": "^1.0", + "zendframework/zend-stdlib": "^3.0" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^1.3", + "doctrine/orm": "^2.5", + "matthiasnoback/symfony-config-test": "^3.0", + "matthiasnoback/symfony-dependency-injection-test": "^2.0", + "phpspec/phpspec": "^4.0", + "phpunit/phpunit": "^6.5", + "polishsymfonycommunity/symfony-mocker-container": "^1.0", + "symfony/browser-kit": "^3.4|^4.1.1", + "symfony/dependency-injection": "^3.4|^4.1.1", + "symfony/security-csrf": "^3.4|^4.1.1", + "symfony/templating": "^3.4|^4.1.1", + "symfony/translation": "^3.4|^4.1.1", + "symfony/twig-bundle": "^3.4|^4.1.1", + "twig/twig": "^2.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Sylius\\Bundle\\FixturesBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Community contributions", + "homepage": "http://github.com/Sylius/Sylius/contributors" + }, + { + "name": "Kamil Kokot", + "homepage": "http://kamil.kokot.me" + }, + { + "name": "Sylius project", + "homepage": "http://sylius.com" + } + ], + "description": "Configurable fixtures for Symfony applications.", + "homepage": "http://sylius.com", + "keywords": [ + "fixtures", + "sylius", + "symfony" + ], + "time": "2018-10-03T08:07:33+00:00" + }, + { + "name": "sylius/resource", + "version": "v1.3.1", + "source": { + "type": "git", + "url": "https://github.com/Sylius/Resource.git", + "reference": "2b93ce7e90e5e0d00d7a90f018f8d354f0c0a45f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Sylius/Resource/zipball/2b93ce7e90e5e0d00d7a90f018f8d354f0c0a45f", + "reference": "2b93ce7e90e5e0d00d7a90f018f8d354f0c0a45f", + "shasum": "" + }, + "require": { + "doctrine/common": "^2.6", + "gedmo/doctrine-extensions": "^2.4", + "pagerfanta/pagerfanta": "^1.0|^2.0", + "php": "^7.2", + "symfony/event-dispatcher": "^3.4|^4.1.1", + "symfony/property-access": "^3.4|^4.1.1", + "winzou/state-machine": "^0.3" + }, + "require-dev": { + "phpspec/phpspec": "^4.0", + "sylius/locale": "^1.0" + }, + "suggest": { + "sylius/locale": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Sylius\\Component\\Resource\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Community contributions", + "homepage": "http://github.com/Sylius/Sylius/contributors" + }, + { + "name": "Paweł Jędrzejewski", + "homepage": "http://pjedrzejewski.com" + }, + { + "name": "Sylius project", + "homepage": "http://sylius.com" + } + ], + "description": "Basic resource interfaces for PHP applications.", + "homepage": "http://sylius.com", + "keywords": [ + "api", + "doctrine", + "ecommerce", + "resource", + "shop", + "sylius" + ], + "time": "2018-10-03T08:07:33+00:00" + }, + { + "name": "symfony/cache", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "05ce0ddc8bc1ffe592105398fc2c725cb3080a38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/05ce0ddc8bc1ffe592105398fc2c725cb3080a38", + "reference": "05ce0ddc8bc1ffe592105398fc2c725cb3080a38", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/cache": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0" + }, + "conflict": { + "symfony/var-dumper": "<3.4" + }, + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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 Cache component with PSR-6, PSR-16, and tags", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "time": "2018-09-30T03:38:13+00:00" + }, + { + "name": "symfony/class-loader", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "f31333bdff54c7595f834d510a6d2325573ddb36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/f31333bdff54c7595f834d510a6d2325573ddb36", + "reference": "f31333bdff54c7595f834d510a6d2325573ddb36", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "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 ClassLoader Component", + "homepage": "https://symfony.com", + "time": "2018-10-02T12:28:39+00:00" + }, + { + "name": "symfony/config", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "e5389132dc6320682de3643091121c048ff796b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3", + "reference": "e5389132dc6320682de3643091121c048ff796b3", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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 Config Component", + "homepage": "https://symfony.com", + "time": "2018-09-08T13:15:14+00:00" + }, + { + "name": "symfony/debug", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "e3f76ce6198f81994e019bb2b4e533e9de1b9b90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/e3f76ce6198f81994e019bb2b4e533e9de1b9b90", + "reference": "e3f76ce6198f81994e019bb2b4e533e9de1b9b90", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "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", + "time": "2018-10-02T16:36:10+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v4.0.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "36401d55d9be1fddd4e8be983b2a7e0b9a5f29ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/36401d55d9be1fddd4e8be983b2a7e0b9a5f29ac", + "reference": "36401d55d9be1fddd4e8be983b2a7e0b9a5f29ac", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/finder": "<3.4", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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 DependencyInjection Component", + "homepage": "https://symfony.com", + "time": "2018-08-01T08:23:45+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e", + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "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", + "time": "2018-07-26T09:10:45+00:00" + }, + { + "name": "symfony/expression-language", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "065bba63c61c96fd2d4fbd01b28de058e6f8779a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/065bba63c61c96fd2d4fbd01b28de058e6f8779a", + "reference": "065bba63c61c96fd2d4fbd01b28de058e6f8779a", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/cache": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "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 ExpressionLanguage Component", + "homepage": "https://symfony.com", + "time": "2018-07-26T09:10:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "596d12b40624055c300c8b619755b748ca5cf0b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/596d12b40624055c300c8b619755b748ca5cf0b5", + "reference": "596d12b40624055c300c8b619755b748ca5cf0b5", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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 Filesystem Component", + "homepage": "https://symfony.com", + "time": "2018-10-02T12:40:59+00:00" + }, + { + "name": "symfony/finder", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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 Finder Component", + "homepage": "https://symfony.com", + "time": "2018-10-03T08:47:56+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "3736bf6f3578613a010954ee66c597e7b1bed75b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/3736bf6f3578613a010954ee66c597e7b1bed75b", + "reference": "3736bf6f3578613a010954ee66c597e7b1bed75b", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^5.5.9|>=7.0.8", + "symfony/cache": "~3.4|~4.0", + "symfony/class-loader": "~3.2", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4.3|^4.0.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^3.4.5|^4.0.5" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/asset": "~3.3|~4.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/form": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/property-info": "~3.3|~4.0", + "symfony/security-core": "~3.2|~4.0", + "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", + "symfony/serializer": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~3.2|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "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 FrameworkBundle", + "homepage": "https://symfony.com", + "time": "2018-10-03T08:41:12+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "d528136617ff24f530e70df9605acc1b788b08d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d528136617ff24f530e70df9605acc1b788b08d4", + "reference": "d528136617ff24f530e70df9605acc1b788b08d4", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/expression-language": "~3.4|~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "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", + "time": "2018-10-03T08:48:45+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v4.0.14", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "569c6ec5aff02421ac6f3417807972fea48140e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/569c6ec5aff02421ac6f3417807972fea48140e2", + "reference": "569c6ec5aff02421ac6f3417807972fea48140e2", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0", + "symfony/debug": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", + "symfony/var-dumper": "<3.4", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4.10|^4.0.10", + "symfony/dom-crawler": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/var-dumper": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "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", + "time": "2018-08-01T14:57:57+00:00" + }, + { + "name": "symfony/inflector", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/inflector.git", + "reference": "07810b5c88ec0c2e98972571a40a126b44664e13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/inflector/zipball/07810b5c88ec0c2e98972571a40a126b44664e13", + "reference": "07810b5c88ec0c2e98972571a40a126b44664e13", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Inflector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Inflector Component", + "homepage": "https://symfony.com", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string", + "symfony", + "words" + ], + "time": "2018-07-26T08:55:25+00:00" + }, + { + "name": "symfony/monolog-bridge", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bridge.git", + "reference": "858737f5ec0266ed37b6b687020283b6e78ae220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/858737f5ec0266ed37b6b687020283b6e78ae220", + "reference": "858737f5ec0266ed37b6b687020283b6e78ae220", + "shasum": "" + }, + "require": { + "monolog/monolog": "~1.19", + "php": "^7.1.3", + "symfony/http-kernel": "~3.4|~4.0" + }, + "conflict": { + "symfony/console": "<3.4", + "symfony/http-foundation": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", + "symfony/event-dispatcher": "Needed when using log messages in console commands.", + "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", + "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." + }, + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Monolog\\": "" + }, + "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 Monolog Bridge", + "homepage": "https://symfony.com", + "time": "2018-09-21T12:49:42+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "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 OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "time": "2018-09-17T17:29:18+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-08-06T14:22:27+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-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": "2018-08-06T14:22:27+00:00" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.9.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "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": "2018-08-06T14:22:27+00:00" + }, + { + "name": "symfony/property-access", + "version": "v3.4.17", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "a62b882330f24b43f4409e50a0692aa433947460" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/a62b882330f24b43f4409e50a0692aa433947460", + "reference": "a62b882330f24b43f4409e50a0692aa433947460", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/inflector": "~3.1|~4.0", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "symfony/cache": "~3.1|~4.0" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "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 PropertyAccess Component", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "time": "2018-10-02T12:28:39+00:00" + }, + { + "name": "symfony/routing", + "version": "v4.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "537803f0bdfede36b9acef052d2e4d447d9fa0e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/537803f0bdfede36b9acef052d2e4d447d9fa0e9", + "reference": "537803f0bdfede36b9acef052d2e4d447d9fa0e9", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~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": "4.1-dev" + } + }, + "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" + ], + "time": "2018-10-02T12:40:59+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" + }, + { + "name": "winzou/state-machine", + "version": "0.3.3", + "source": { + "type": "git", + "url": "https://github.com/winzou/state-machine.git", + "reference": "37f03a316b9a461ed443906e158bab8d358542df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/winzou/state-machine/zipball/37f03a316b9a461ed443906e158bab8d358542df", + "reference": "37f03a316b9a461ed443906e158bab8d358542df", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/event-dispatcher": "~2.1|~3.0|~4.0", + "symfony/expression-language": "~2.4|~3.0|~4.0", + "symfony/property-access": "~2.1|~3.0|~4.0" + }, + "require-dev": { + "phpspec/phpspec": "~2.0", + "twig/twig": "~1.0" + }, + "suggest": { + "twig/twig": "Access the state machine in your twig templates (~1.0)" + }, + "type": "library", + "autoload": { + "psr-0": { + "SM": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexandre Bacco", + "email": "alexandre.bacco@gmail.com", + "homepage": "http://alex.bacco.fr" + } + ], + "description": "A very lightweight yet powerful PHP state machine", + "homepage": "https://github.com/winzou/StateMachine", + "keywords": [ + "callback", + "event", + "state", + "statemachine" + ], + "time": "2018-02-11T18:07:15+00:00" + }, + { + "name": "zendframework/zend-stdlib", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-stdlib.git", + "reference": "66536006722aff9e62d1b331025089b7ec71c065" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/66536006722aff9e62d1b331025089b7ec71c065", + "reference": "66536006722aff9e62d1b331025089b7ec71c065", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "keywords": [ + "ZendFramework", + "stdlib", + "zf" + ], + "time": "2018-08-28T21:34:05+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.1" + }, + "platform-dev": [] +} diff --git a/vendor/autoload.php b/vendor/autoload.php new file mode 100644 index 0000000..c12b717 --- /dev/null +++ b/vendor/autoload.php @@ -0,0 +1,7 @@ +=5.3.3" + }, + + "require-dev": { + "php-yaoi/php-yaoi": "^1.0", + "chuyskywalker/rolling-curl": "^3.1" + }, + + "autoload": { + "psr-0": { + "Behat\\Transliterator": "src/" + } + }, + + "autoload-dev": { + "psr-4": { + "Behat\\Tests\\Transliterator\\": "tests" + } + }, + + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + } +} diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php b/vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php new file mode 100644 index 0000000..87a07c7 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/SyncTool.php @@ -0,0 +1,213 @@ + '}', + '\\\\' => '\\', + '\\{' => '{', + '\\@' => '@', + '\\$' => '$', + ); + + $this->tokenizer = new Parser(); + $this->tokenizer->addLineStopper('#'); + $this->tokenizer->addQuote('qq{', '}', $escape); + $this->tokenizer->addQuote('q{', '}', $escape); + $this->tokenizer->addQuote('"', '"'); + $this->tokenizer->addQuote("'", "'"); + $this->tokenizer->addBracket('[', ']'); + $this->tokenizer->addDelimiter(';'); + + $this->renderer = new Renderer(); + $this->renderer + ->setBindKey('-~z', 'z~-') + ->strip('#') + ->keepBoundaries('['); + } + + public static function setUpDefinition(\Yaoi\Command\Definition $definition, $options) + { + $definition->name = 'update-data'; + $definition->description = 'Tool for converting char tables for Behat/Transliterator from Perl to PHP'; + } + + public function performAction() + { + $rollingCurl = new RollingCurl(); + + foreach ($this->getPerlTablesUrlList() as $url) { + $rollingCurl->get($url); + } + + $rollingCurl->setCallback(function (Request $request, RollingCurl $rollingCurl) { + $this->response->addContent($request->getUrl()); + $content = $request->getResponseText(); + $this->parsePerlTable($content); + }) + ->execute(); + } + + private function removePhpCharTable($phpFilePath, $reason) + { + $this->response->addContent($reason); + if (file_exists($phpFilePath)) { + if (unlink($phpFilePath)) { + $this->response->success('Deleted'); + } else { + $this->response->error('Failed to delete'); + } + } else { + $this->response->success('No PHP file, skipped'); + } + } + + private function pushItem($item) + { + if ($this->itemIndex >= 16) { + $this->phpTable = trim($this->phpTable); + $this->phpTable .= "\n"; + $this->itemIndex = 0; + } + ++$this->itemIndex; + + $item = new StringValue($item); + if ($item->starts('\x') || $item->starts('\n')) { + $this->phpTable .= '"' . $item . '", '; + $this->nonQuestionBoxFound = true; + } else { + // TODO check if this hack should be removed for chinese letters + if ($item->value === '[?] ') { + $item->value = '[?]'; + } + // + + if ($item->value !== '[?]') { + $this->nonQuestionBoxFound = true; + } + + $this->phpTable .= "'" . str_replace(array('\\', '\''), array('\\\\', '\\\''), $item) . "', "; + } + } + + private function tokenizePerlTable($content) + { + $tokens = $this->tokenizer->tokenize($content); + + $expression = $this->renderer->getExpression($tokens); + $statement = $expression->getStatement(); + /** @var Parsed[] $binds */ + $binds = $expression->getBinds(); + + $parser = new StringParser($statement); + $block = (string)$parser->inner('$Text::Unidecode::Char[', ']'); + if (!$block) { + throw new \Exception('Block not found'); + } + $this->block = $this->renderer->getExpression($binds[$block])->getStatement(); + + $itemsBind = (string)$parser->inner('[', ']'); + + if (!$itemsBind) { + $items = array(); + } + else { + $items = $binds[$itemsBind]; + } + + return $items; + } + + private function parsePerlTable($content) + { + $items = $this->tokenizePerlTable($content); + + $phpFilePath = __DIR__ . '/data/' . substr($this->block, 1) . '.php'; + if (!$items) { + $this->removePhpCharTable($phpFilePath, 'Empty char table for block ' . $this->block); + return; + } + + $this->phpTable = <<block] = array( + +PHP; + + $itemsExpression = $this->renderer->getExpression($items); + $itemsStatement = $itemsExpression->getStatement(); + $itemsBinds = $itemsExpression->getBinds(); + + $itemsStatement = explode(',', $itemsStatement); + $this->itemIndex = 0; + $this->nonQuestionBoxFound = false; + foreach ($itemsStatement as $item) { + $item = trim($item); + if (!$item) { + break; + } + + if (isset($itemsBinds[$item])) { + /** @var Token $token */ + $token = $itemsBinds[$item]; + $item = $token->unEscapedContent; + } + + $this->pushItem($item); + } + + if ($this->nonQuestionBoxFound) { + $this->phpTable = trim($this->phpTable) . "\n" . ');' . "\n"; + if (file_put_contents($phpFilePath, $this->phpTable)) { + $this->response->success('Block ' . $this->block . ' converted to ' . $phpFilePath); + } else { + $this->response->error('Failed to save ' . $phpFilePath); + } + } else { + $this->removePhpCharTable($phpFilePath, 'Block ' . $this->block . ' contains only [?]'); + } + + } + + private function getPerlTablesUrlList() + { + $client = new Client(); + $list = array(); + $page = $client->fetch('http://cpansearch.perl.org/src/SBURKE/Text-Unidecode-' . self::LIB_VERSION . '/lib/Text/Unidecode/'); + foreach (StringParser::create($page)->innerAll('.pm">', '') as $xXXpm) { + $list[] = 'http://cpansearch.perl.org/src/SBURKE/Text-Unidecode-' . self::LIB_VERSION . '/lib/Text/Unidecode/' + . $xXXpm; + } + return $list; + } +} + diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php b/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php new file mode 100644 index 0000000..5dc07d3 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/Transliterator.php @@ -0,0 +1,590 @@ + + * @author Jonathan H. Wage + * @author + */ +abstract class Transliterator +{ + /** + * Checks whether a string has utf7 characters in it. + * + * By bmorel at ssi dot fr + * + * @param string $string + * + * @return bool + */ + public static function seemsUtf8($string) + { + $stringLength = strlen($string); + + for ($i = 0; $i < $stringLength; ++$i) { + if (ord($string[$i]) < 0x80) { // 0bbbbbbb + continue; + } elseif ((ord($string[$i]) & 0xE0) == 0xC0) { // 110bbbbb + $n = 1; + } elseif ((ord($string[$i]) & 0xF0) == 0xE0) { //1110bbbb + $n = 2; + } elseif ((ord($string[$i]) & 0xF8) == 0xF0) { // 11110bbb + $n = 3; + } elseif ((ord($string[$i]) & 0xFC) == 0xF8) { // 111110bb + $n = 4; + } elseif ((ord($string[$i]) & 0xFE) == 0xFC) { // 1111110b + $n = 5; + } else { + return false; // Does not match any model + } + + for ($j = 0; $j < $n; ++$j) { // n bytes matching 10bbbbbb follow ? + if (++$i === $stringLength || ((ord($string[$i]) & 0xC0) !== 0x80)) { + return false; + } + } + } + + return true; + } + + /** + * Replaces accentuated chars (and a few others) with their ASCII base char. + * + * @see Transliterator::utf8ToAscii for a full transliteration to ASCII + * + * @param string $string String to unaccent + * + * @return string Unaccented string + */ + public static function unaccent($string) + { + if (!preg_match('/[\x80-\xff]/', $string)) { + return $string; + } + + if (self::seemsUtf8($string)) { + $chars = array( + // Decompositions for Latin-1 Supplement + chr(195).chr(128) => 'A', + chr(195).chr(129) => 'A', + chr(195).chr(130) => 'A', + chr(195).chr(131) => 'A', + chr(195).chr(132) => 'A', + chr(195).chr(133) => 'A', + chr(195).chr(135) => 'C', + chr(195).chr(136) => 'E', + chr(195).chr(137) => 'E', + chr(195).chr(138) => 'E', + chr(195).chr(139) => 'E', + chr(195).chr(140) => 'I', + chr(195).chr(141) => 'I', + chr(195).chr(142) => 'I', + chr(195).chr(143) => 'I', + chr(195).chr(145) => 'N', + chr(195).chr(146) => 'O', + chr(195).chr(147) => 'O', + chr(195).chr(148) => 'O', + chr(195).chr(149) => 'O', + chr(195).chr(150) => 'O', + chr(195).chr(153) => 'U', + chr(195).chr(154) => 'U', + chr(195).chr(155) => 'U', + chr(195).chr(156) => 'U', + chr(195).chr(157) => 'Y', + chr(195).chr(159) => 's', + chr(195).chr(160) => 'a', + chr(195).chr(161) => 'a', + chr(195).chr(162) => 'a', + chr(195).chr(163) => 'a', + chr(195).chr(164) => 'a', + chr(195).chr(165) => 'a', + chr(195).chr(167) => 'c', + chr(195).chr(168) => 'e', + chr(195).chr(169) => 'e', + chr(195).chr(170) => 'e', + chr(195).chr(171) => 'e', + chr(195).chr(172) => 'i', + chr(195).chr(173) => 'i', + chr(195).chr(174) => 'i', + chr(195).chr(175) => 'i', + chr(195).chr(177) => 'n', + chr(195).chr(178) => 'o', + chr(195).chr(179) => 'o', + chr(195).chr(180) => 'o', + chr(195).chr(181) => 'o', + chr(195).chr(182) => 'o', + chr(195).chr(182) => 'o', + chr(195).chr(185) => 'u', + chr(195).chr(186) => 'u', + chr(195).chr(187) => 'u', + chr(195).chr(188) => 'u', + chr(195).chr(189) => 'y', + chr(195).chr(191) => 'y', + // Decompositions for Latin Extended-A + chr(196).chr(128) => 'A', + chr(196).chr(129) => 'a', + chr(196).chr(130) => 'A', + chr(196).chr(131) => 'a', + chr(196).chr(132) => 'A', + chr(196).chr(133) => 'a', + chr(196).chr(134) => 'C', + chr(196).chr(135) => 'c', + chr(196).chr(136) => 'C', + chr(196).chr(137) => 'c', + chr(196).chr(138) => 'C', + chr(196).chr(139) => 'c', + chr(196).chr(140) => 'C', + chr(196).chr(141) => 'c', + chr(196).chr(142) => 'D', + chr(196).chr(143) => 'd', + chr(196).chr(144) => 'D', + chr(196).chr(145) => 'd', + chr(196).chr(146) => 'E', + chr(196).chr(147) => 'e', + chr(196).chr(148) => 'E', + chr(196).chr(149) => 'e', + chr(196).chr(150) => 'E', + chr(196).chr(151) => 'e', + chr(196).chr(152) => 'E', + chr(196).chr(153) => 'e', + chr(196).chr(154) => 'E', + chr(196).chr(155) => 'e', + chr(196).chr(156) => 'G', + chr(196).chr(157) => 'g', + chr(196).chr(158) => 'G', + chr(196).chr(159) => 'g', + chr(196).chr(160) => 'G', + chr(196).chr(161) => 'g', + chr(196).chr(162) => 'G', + chr(196).chr(163) => 'g', + chr(196).chr(164) => 'H', + chr(196).chr(165) => 'h', + chr(196).chr(166) => 'H', + chr(196).chr(167) => 'h', + chr(196).chr(168) => 'I', + chr(196).chr(169) => 'i', + chr(196).chr(170) => 'I', + chr(196).chr(171) => 'i', + chr(196).chr(172) => 'I', + chr(196).chr(173) => 'i', + chr(196).chr(174) => 'I', + chr(196).chr(175) => 'i', + chr(196).chr(176) => 'I', + chr(196).chr(177) => 'i', + chr(196).chr(178) => 'IJ', + chr(196).chr(179) => 'ij', + chr(196).chr(180) => 'J', + chr(196).chr(181) => 'j', + chr(196).chr(182) => 'K', + chr(196).chr(183) => 'k', + chr(196).chr(184) => 'k', + chr(196).chr(185) => 'L', + chr(196).chr(186) => 'l', + chr(196).chr(187) => 'L', + chr(196).chr(188) => 'l', + chr(196).chr(189) => 'L', + chr(196).chr(190) => 'l', + chr(196).chr(191) => 'L', + chr(197).chr(128) => 'l', + chr(197).chr(129) => 'L', + chr(197).chr(130) => 'l', + chr(197).chr(131) => 'N', + chr(197).chr(132) => 'n', + chr(197).chr(133) => 'N', + chr(197).chr(134) => 'n', + chr(197).chr(135) => 'N', + chr(197).chr(136) => 'n', + chr(197).chr(137) => 'N', + chr(197).chr(138) => 'n', + chr(197).chr(139) => 'N', + chr(197).chr(140) => 'O', + chr(197).chr(141) => 'o', + chr(197).chr(142) => 'O', + chr(197).chr(143) => 'o', + chr(197).chr(144) => 'O', + chr(197).chr(145) => 'o', + chr(197).chr(146) => 'OE', + chr(197).chr(147) => 'oe', + chr(197).chr(148) => 'R', + chr(197).chr(149) => 'r', + chr(197).chr(150) => 'R', + chr(197).chr(151) => 'r', + chr(197).chr(152) => 'R', + chr(197).chr(153) => 'r', + chr(197).chr(154) => 'S', + chr(197).chr(155) => 's', + chr(197).chr(156) => 'S', + chr(197).chr(157) => 's', + chr(197).chr(158) => 'S', + chr(197).chr(159) => 's', + chr(197).chr(160) => 'S', + chr(197).chr(161) => 's', + chr(197).chr(162) => 'T', + chr(197).chr(163) => 't', + chr(197).chr(164) => 'T', + chr(197).chr(165) => 't', + chr(197).chr(166) => 'T', + chr(197).chr(167) => 't', + chr(197).chr(168) => 'U', + chr(197).chr(169) => 'u', + chr(197).chr(170) => 'U', + chr(197).chr(171) => 'u', + chr(197).chr(172) => 'U', + chr(197).chr(173) => 'u', + chr(197).chr(174) => 'U', + chr(197).chr(175) => 'u', + chr(197).chr(176) => 'U', + chr(197).chr(177) => 'u', + chr(197).chr(178) => 'U', + chr(197).chr(179) => 'u', + chr(197).chr(180) => 'W', + chr(197).chr(181) => 'w', + chr(197).chr(182) => 'Y', + chr(197).chr(183) => 'y', + chr(197).chr(184) => 'Y', + chr(197).chr(185) => 'Z', + chr(197).chr(186) => 'z', + chr(197).chr(187) => 'Z', + chr(197).chr(188) => 'z', + chr(197).chr(189) => 'Z', + chr(197).chr(190) => 'z', + chr(197).chr(191) => 's', + // Euro Sign + chr(226).chr(130).chr(172) => 'E', + // GBP (Pound) Sign + chr(194).chr(163) => '', + 'Ä' => 'Ae', + 'ä' => 'ae', + 'Ü' => 'Ue', + 'ü' => 'ue', + 'Ö' => 'Oe', + 'ö' => 'oe', + 'ß' => 'ss', + // Norwegian characters + 'Å' => 'Aa', + 'Æ' => 'Ae', + 'Ø' => 'O', + 'æ' => 'a', + 'ø' => 'o', + 'å' => 'aa', + ); + + $string = strtr($string, $chars); + } else { + $chars = array(); + // Assume ISO-8859-1 if not UTF-8 + $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158) + .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194) + .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202) + .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210) + .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218) + .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227) + .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235) + .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243) + .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251) + .chr(252).chr(253).chr(255); + + $chars['out'] = 'EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'; + + $string = strtr($string, $chars['in'], $chars['out']); + + $doubleChars = array(); + $doubleChars['in'] = array( + chr(140), + chr(156), + chr(198), + chr(208), + chr(222), + chr(223), + chr(230), + chr(240), + chr(254), + ); + $doubleChars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th'); + $string = str_replace($doubleChars['in'], $doubleChars['out'], $string); + } + + return $string; + } + + /** + * Transliterates an UTF-8 string to ASCII. + * + * US-ASCII transliterations of Unicode text + * Ported Sean M. Burke's Text::Unidecode Perl module (He did all the hard work!) + * Warning: you should only pass this well formed UTF-8! + * Be aware it works by making a copy of the input string which it appends transliterated + * characters to - it uses a PHP output buffer to do this - it means, memory use will increase, + * requiring up to the same amount again as the input string. + * + * @see http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm + * + * @author + * + * @param string $str UTF-8 string to convert + * @param string $unknown Character use if character unknown (default to ?) + * + * @return string US-ASCII string + */ + public static function utf8ToAscii($str, $unknown = '?') + { + static $UTF8_TO_ASCII; + + if (strlen($str) == 0) { + return ''; + } + + preg_match_all('/.{1}|[^\x00]{1,1}$/us', $str, $ar); + $chars = $ar[0]; + + foreach ($chars as $i => $c) { + if (ord($c{0}) >= 0 && ord($c{0}) <= 127) { + continue; + } // ASCII - next please + if (ord($c{0}) >= 192 && ord($c{0}) <= 223) { + $ord = (ord($c{0}) - 192) * 64 + (ord($c{1}) - 128); + } + if (ord($c{0}) >= 224 && ord($c{0}) <= 239) { + $ord = (ord($c{0}) - 224) * 4096 + (ord($c{1}) - 128) * 64 + (ord($c{2}) - 128); + } + if (ord($c{0}) >= 240 && ord($c{0}) <= 247) { + $ord = (ord($c{0}) - 240) * 262144 + (ord($c{1}) - 128) * 4096 + (ord($c{2}) - 128) * 64 + (ord($c{3}) - 128); + } + if (ord($c{0}) >= 248 && ord($c{0}) <= 251) { + $ord = (ord($c{0}) - 248) * 16777216 + (ord($c{1}) - 128) * 262144 + (ord($c{2}) - 128) * 4096 + (ord($c{3}) - 128) * 64 + (ord($c{4}) - 128); + } + if (ord($c{0}) >= 252 && ord($c{0}) <= 253) { + $ord = (ord($c{0}) - 252) * 1073741824 + (ord($c{1}) - 128) * 16777216 + (ord($c{2}) - 128) * 262144 + (ord($c{3}) - 128) * 4096 + (ord($c{4}) - 128) * 64 + (ord($c{5}) - 128); + } + if (ord($c{0}) >= 254 && ord($c{0}) <= 255) { + $chars{$i} = $unknown; + continue; + } //error + + $bank = $ord >> 8; + + if (!array_key_exists($bank, (array) $UTF8_TO_ASCII)) { + $bankfile = __DIR__.'/data/'.sprintf('x%02x', $bank).'.php'; + if (file_exists($bankfile)) { + include $bankfile; + } else { + $UTF8_TO_ASCII[$bank] = array(); + } + } + + $newchar = $ord & 255; + if (array_key_exists($newchar, $UTF8_TO_ASCII[$bank])) { + $chars{$i} = $UTF8_TO_ASCII[$bank][$newchar]; + } else { + $chars{$i} = $unknown; + } + } + + return implode('', $chars); + } + + /** + * Generates a slug of the text. + * + * Does not transliterate correctly eastern languages. + * + * @see Transliterator::unaccent for the transliteration logic + * + * @param string $text + * @param string $separator + * + * @return string + */ + public static function urlize($text, $separator = '-') + { + $text = self::unaccent($text); + + return self::postProcessText($text, $separator); + } + + /** + * Generates a slug of the text after transliterating the UTF-8 string to ASCII. + * + * Uses transliteration tables to convert any kind of utf8 character. + * + * @param string $text + * @param string $separator + * + * @return string $text + */ + public static function transliterate($text, $separator = '-') + { + if (preg_match('/[\x80-\xff]/', $text) && self::validUtf8($text)) { + $text = self::utf8ToAscii($text); + } + + return self::postProcessText($text, $separator); + } + + /** + * Tests a string as to whether it's valid UTF-8 and supported by the + * Unicode standard. + * + * Note: this function has been modified to simple return true or false + * + * @author + * + * @param string $str UTF-8 encoded string + * + * @return bool + * + * @see http://hsivonen.iki.fi/php-utf8/ + */ + public static function validUtf8($str) + { + $mState = 0; // cached expected number of octets after the current octet + // until the beginning of the next UTF8 character sequence + $mUcs4 = 0; // cached Unicode character + $mBytes = 1; // cached expected number of octets in the current sequence + + $len = strlen($str); + for ($i = 0; $i < $len; ++$i) { + $in = ord($str{$i}); + if ($mState == 0) { + // When mState is zero we expect either a US-ASCII character or a + // multi-octet sequence. + if (0 == (0x80 & ($in))) { + // US-ASCII, pass straight through. + $mBytes = 1; + } elseif (0xC0 == (0xE0 & ($in))) { + // First octet of 2 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x1F) << 6; + $mState = 1; + $mBytes = 2; + } elseif (0xE0 == (0xF0 & ($in))) { + // First octet of 3 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x0F) << 12; + $mState = 2; + $mBytes = 3; + } elseif (0xF0 == (0xF8 & ($in))) { + // First octet of 4 octet sequence + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x07) << 18; + $mState = 3; + $mBytes = 4; + } elseif (0xF8 == (0xFC & ($in))) { + /* First octet of 5 octet sequence. + * + * This is illegal because the encoded codepoint must be either + * (a) not the shortest form or + * (b) outside the Unicode range of 0-0x10FFFF. + * Rather than trying to resynchronize, we will carry on until the end + * of the sequence and let the later error handling code catch it. + */ + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 0x03) << 24; + $mState = 4; + $mBytes = 5; + } elseif (0xFC == (0xFE & ($in))) { + // First octet of 6 octet sequence, see comments for 5 octet sequence. + $mUcs4 = ($in); + $mUcs4 = ($mUcs4 & 1) << 30; + $mState = 5; + $mBytes = 6; + } else { + /* Current octet is neither in the US-ASCII range nor a legal first + * octet of a multi-octet sequence. + */ + return false; + } + } else { + // When mState is non-zero, we expect a continuation of the multi-octet + // sequence + if (0x80 == (0xC0 & ($in))) { + // Legal continuation. + $shift = ($mState - 1) * 6; + $tmp = $in; + $tmp = ($tmp & 0x0000003F) << $shift; + $mUcs4 |= $tmp; + /* + * End of the multi-octet sequence. mUcs4 now contains the final + * Unicode codepoint to be output + */ + if (0 == --$mState) { + /* + * Check for illegal sequences and codepoints. + */ + // From Unicode 3.1, non-shortest form is illegal + if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || + ((3 == $mBytes) && ($mUcs4 < 0x0800)) || + ((4 == $mBytes) && ($mUcs4 < 0x10000)) || + (4 < $mBytes) || + // From Unicode 3.2, surrogate characters are illegal + (($mUcs4 & 0xFFFFF800) == 0xD800) || + // Codepoints outside the Unicode range are illegal + ($mUcs4 > 0x10FFFF) + ) { + return false; + } + //initialize UTF8 cache + $mState = 0; + $mUcs4 = 0; + $mBytes = 1; + } + } else { + /* + *((0xC0 & (*in) != 0x80) && (mState != 0)) + * Incomplete multi-octet sequence. + */ + return false; + } + } + } + + return true; + } + + /** + * Cleans up the text and adds separator. + * + * @param string $text + * @param string $separator + * + * @return string + */ + private static function postProcessText($text, $separator) + { + if (function_exists('mb_strtolower')) { + $text = mb_strtolower($text); + } else { + $text = strtolower($text); + } + + // Remove apostrophes which are not used as quotes around a string + $text = preg_replace('/(\\w)\'(\\w)/', '${1}${2}', $text); + + // Replace all none word characters with a space + $text = preg_replace('/\W/', ' ', $text); + + // More stripping. Replace spaces with dashes + $text = strtolower(preg_replace('/[^A-Za-z0-9\/]+/', $separator, + preg_replace('/([a-z\d])([A-Z])/', '\1_\2', + preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2', + preg_replace('/::/', '/', $text))))); + + return trim($text, $separator); + } +} diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php new file mode 100644 index 0000000..9177c74 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x00.php @@ -0,0 +1,19 @@ +', '?', +'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', +'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', +'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', "\x7f", +'EUR', '', ',', 'f', ',,', '...', '+', '++', '^', '%0', 'S', '<', 'OE', '', 'Z', '', +'', '\'', '\'', '"', '"', '*', '-', '--', '~', 'tm', 's', '>', 'oe', '', 'z', 'Y', +' ', '!', 'C/', 'PS', '$?', 'Y=', '|', 'SS', '"', '(c)', 'a', '<<', '!', '', '(r)', '-', +'deg', '+-', '2', '3', '\'', 'u', 'P', '*', ',', '1', 'o', '>>', '1/4', '1/2', '3/4', '?', +'A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', +'D', 'N', 'O', 'O', 'O', 'O', 'O', 'x', 'O', 'U', 'U', 'U', 'U', 'Y', 'Th', 'ss', +'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', +'d', 'n', 'o', 'o', 'o', 'o', 'o', '/', 'o', 'u', 'u', 'u', 'u', 'y', 'th', 'y', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php new file mode 100644 index 0000000..d746817 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x01.php @@ -0,0 +1,19 @@ +', '^', 'V', '^', 'V', '\'', '-', '/', '\\', ',', '_', '\\', '/', +':', '.', '`', '\'', '^', 'V', '+', '-', 'V', '.', '@', ',', '~', '"', 'R', 'X', +'G', 'l', 's', 'x', '?', '', '', '', '', '', '', '', 'V', '=', '"', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php new file mode 100644 index 0000000..a923ec0 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x03.php @@ -0,0 +1,19 @@ +', '[?]', '[?]', '[?]', +'f', 'v', 'u', 'yr', 'y', 'w', 'th', 'th', 'a', 'o', 'ac', 'ae', 'o', 'o', 'o', 'oe', +'on', 'r', 'k', 'c', 'k', 'g', 'ng', 'g', 'g', 'w', 'h', 'h', 'h', 'h', 'n', 'n', +'n', 'i', 'e', 'j', 'g', 'ae', 'a', 'eo', 'p', 'z', 's', 's', 's', 'c', 'z', 't', +'t', 'd', 'b', 'b', 'p', 'p', 'e', 'm', 'm', 'm', 'l', 'l', 'ng', 'ng', 'd', 'o', +'ear', 'ior', 'qu', 'qu', 'qu', 's', 'yr', 'yr', 'yr', 'q', 'x', '.', ':', '+', '17', '18', +'19', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x17.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x17.php new file mode 100644 index 0000000..ebeba64 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x17.php @@ -0,0 +1,19 @@ +', '.', '..', '...', '.', "\n", "\n\n", '', '', '', '', '', ' ', +'%0', '%00', '\'', '\'\'', '\'\'\'', '`', '``', '```', '^', '<', '>', '*', '!!', '!?', '-', '_', +'-', '^', '***', '--', '/', '-[', ']-', '[?]', '?!', '!?', '7', 'PP', '(]', '[)', '[?]', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', +'0', '', '', '', '4', '5', '6', '7', '8', '9', '+', '-', '=', '(', ')', 'n', +'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '-', '=', '(', ')', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'ECU', 'CL', 'Cr', 'FF', 'L', 'mil', 'N', 'Pts', 'Rs', 'W', 'NS', 'D', 'EUR', 'K', 'T', 'Dr', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x21.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x21.php new file mode 100644 index 0000000..281989a --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x21.php @@ -0,0 +1,19 @@ +', '>', '>', '>', '>', '>', 'V', 'V', 'V', 'V', +'<', '<', '<', '<', '<', '<', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', +'*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', '*', +'*', '*', '*', '*', '*', '*', '*', '#', '#', '#', '#', '#', '^', '^', '^', 'O', +'#', '#', '#', '#', '#', '#', '#', '#', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x26.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x26.php new file mode 100644 index 0000000..a64a3ea --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x26.php @@ -0,0 +1,19 @@ +', 'n', 't', 'q', +',', '*', '5', '<', '-', 'u', '8', 'v', '.', '%', '[', '$', '+', 'x', '!', '&', +';', ':', '4', '\\', '0', 'z', '7', '(', '_', '?', 'w', ']', '#', 'y', ')', '=', +'[d7]', '[d17]', '[d27]', '[d127]', '[d37]', '[d137]', '[d237]', '[d1237]', '[d47]', '[d147]', '[d247]', '[d1247]', '[d347]', '[d1347]', '[d2347]', '[d12347]', +'[d57]', '[d157]', '[d257]', '[d1257]', '[d357]', '[d1357]', '[d2357]', '[d12357]', '[d457]', '[d1457]', '[d2457]', '[d12457]', '[d3457]', '[d13457]', '[d23457]', '[d123457]', +'[d67]', '[d167]', '[d267]', '[d1267]', '[d367]', '[d1367]', '[d2367]', '[d12367]', '[d467]', '[d1467]', '[d2467]', '[d12467]', '[d3467]', '[d13467]', '[d23467]', '[d123467]', +'[d567]', '[d1567]', '[d2567]', '[d12567]', '[d3567]', '[d13567]', '[d23567]', '[d123567]', '[d4567]', '[d14567]', '[d24567]', '[d124567]', '[d34567]', '[d134567]', '[d234567]', '[d1234567]', +'[d8]', '[d18]', '[d28]', '[d128]', '[d38]', '[d138]', '[d238]', '[d1238]', '[d48]', '[d148]', '[d248]', '[d1248]', '[d348]', '[d1348]', '[d2348]', '[d12348]', +'[d58]', '[d158]', '[d258]', '[d1258]', '[d358]', '[d1358]', '[d2358]', '[d12358]', '[d458]', '[d1458]', '[d2458]', '[d12458]', '[d3458]', '[d13458]', '[d23458]', '[d123458]', +'[d68]', '[d168]', '[d268]', '[d1268]', '[d368]', '[d1368]', '[d2368]', '[d12368]', '[d468]', '[d1468]', '[d2468]', '[d12468]', '[d3468]', '[d13468]', '[d23468]', '[d123468]', +'[d568]', '[d1568]', '[d2568]', '[d12568]', '[d3568]', '[d13568]', '[d23568]', '[d123568]', '[d4568]', '[d14568]', '[d24568]', '[d124568]', '[d34568]', '[d134568]', '[d234568]', '[d1234568]', +'[d78]', '[d178]', '[d278]', '[d1278]', '[d378]', '[d1378]', '[d2378]', '[d12378]', '[d478]', '[d1478]', '[d2478]', '[d12478]', '[d3478]', '[d13478]', '[d23478]', '[d123478]', +'[d578]', '[d1578]', '[d2578]', '[d12578]', '[d3578]', '[d13578]', '[d23578]', '[d123578]', '[d4578]', '[d14578]', '[d24578]', '[d124578]', '[d34578]', '[d134578]', '[d234578]', '[d1234578]', +'[d678]', '[d1678]', '[d2678]', '[d12678]', '[d3678]', '[d13678]', '[d23678]', '[d123678]', '[d4678]', '[d14678]', '[d24678]', '[d124678]', '[d34678]', '[d134678]', '[d234678]', '[d1234678]', +'[d5678]', '[d15678]', '[d25678]', '[d125678]', '[d35678]', '[d135678]', '[d235678]', '[d1235678]', '[d45678]', '[d145678]', '[d245678]', '[d1245678]', '[d345678]', '[d1345678]', '[d2345678]', '[d12345678]', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x30.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x30.php new file mode 100644 index 0000000..86201c1 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x30.php @@ -0,0 +1,19 @@ + ', '<<', '>> ', '[', '] ', '{', '} ', +'[(', ')] ', '@', 'X ', '[', '] ', '[[', ']] ', '((', ')) ', '[[', ']] ', '~ ', '``', '\'\'', ',,', +'@', '1', '2', '3', '4', '5', '6', '7', '8', '9', '', '', '', '', '', '', +'~', '+', '+', '+', '+', '', '@', ' // ', '+10+', '+20+', '+30+', '[?]', '[?]', '[?]', '', '', +'[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', +'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'si', 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', +'da', 'ti', 'di', 'tu', 'tu', 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', +'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', +'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', +'wi', 'we', 'wo', 'n', 'vu', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '"', '"', '[?]', +'[?]', 'a', 'a', 'i', 'i', 'u', 'u', 'e', 'e', 'o', 'o', 'ka', 'ga', 'ki', 'gi', 'ku', +'gu', 'ke', 'ge', 'ko', 'go', 'sa', 'za', 'si', 'zi', 'su', 'zu', 'se', 'ze', 'so', 'zo', 'ta', +'da', 'ti', 'di', 'tu', 'tu', 'du', 'te', 'de', 'to', 'do', 'na', 'ni', 'nu', 'ne', 'no', 'ha', +'ba', 'pa', 'hi', 'bi', 'pi', 'hu', 'bu', 'pu', 'he', 'be', 'pe', 'ho', 'bo', 'po', 'ma', 'mi', +'mu', 'me', 'mo', 'ya', 'ya', 'yu', 'yu', 'yo', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wa', +'wi', 'we', 'wo', 'n', 'vu', 'ka', 'ke', 'va', 'vi', 've', 'vo', '', '', '"', '"', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x31.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x31.php new file mode 100644 index 0000000..45c827e --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x31.php @@ -0,0 +1,19 @@ +>', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'(g)', '(n)', '(d)', '(r)', '(m)', '(b)', '(s)', '()', '(j)', '(c)', '(k)', '(t)', '(p)', '(h)', '(ga)', '(na)', +'(da)', '(ra)', '(ma)', '(ba)', '(sa)', '(a)', '(ja)', '(ca)', '(ka)', '(ta)', '(pa)', '(ha)', '[?]', '[?]', '[?]', 'KIS ', +'(1) ', '(2) ', '(3) ', '(4) ', '(5) ', '(6) ', '(7) ', '(8) ', '(9) ', '(10) ', '(Yue) ', '(Huo) ', '(Shui) ', '(Mu) ', '(Jin) ', '(Tu) ', +'(Ri) ', '(Zhu) ', '(You) ', '(She) ', '(Ming) ', '(Te) ', '(Cai) ', '(Zhu) ', '(Lao) ', '(Mi) ', '(Nan) ', '(Nu) ', '(Shi) ', '(You) ', '(Yin) ', '(Zhu) ', +'(Xiang) ', '(Xiu) ', '(Xie) ', '(Zheng) ', '(Shang) ', '(Zhong) ', '(Xia) ', '(Zuo) ', '(You) ', '(Yi) ', '(Zong) ', '(Xue) ', '(Jian) ', '(Qi) ', '(Zi) ', '(Xie) ', +'(Ye) ', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', +'1M', '2M', '3M', '4M', '5M', '6M', '7M', '8M', '9M', '10M', '11M', '12M', '[?]', '[?]', '[?]', '[?]', +'a', 'i', 'u', 'u', 'o', 'ka', 'ki', 'ku', 'ke', 'ko', 'sa', 'si', 'su', 'se', 'so', 'ta', +'ti', 'tu', 'te', 'to', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'hi', 'hu', 'he', 'ho', 'ma', 'mi', +'mu', 'me', 'mo', 'ya', 'yu', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'wi', 'we', 'wo', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/x33.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/x33.php new file mode 100644 index 0000000..15095c1 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/x33.php @@ -0,0 +1,19 @@ +> ', '<', +'> ', '[', '] ', '{', '}', '[?]', '[?]', '[?]', '[?]', '', '', '', '', '', '', '', +',', ',', '.', '', ';', ':', '?', '!', '-', '(', ')', '{', '}', '{', '}', '#', +'&', '*', '+', '-', '<', '>', '=', '', '\\', '$', '%', '@', '[?]', '[?]', '[?]', '[?]', +'', '', '', '[?]', '', '[?]', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', +'', '', '', '', '', '', '', '', '', '', '', '', '', '[?]', '[?]', '', +); diff --git a/vendor/behat/transliterator/src/Behat/Transliterator/data/xff.php b/vendor/behat/transliterator/src/Behat/Transliterator/data/xff.php new file mode 100644 index 0000000..b415a06 --- /dev/null +++ b/vendor/behat/transliterator/src/Behat/Transliterator/data/xff.php @@ -0,0 +1,19 @@ +', '?', +'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', +'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', +'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', +'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '[?]', +'[?]', '.', '[', ']', ',', '*', 'wo', 'a', 'i', 'u', 'e', 'o', 'ya', 'yu', 'yo', 'tu', +'+', 'a', 'i', 'u', 'e', 'o', 'ka', 'ki', 'ku', 'ke', 'ko', 'sa', 'si', 'su', 'se', 'so', +'ta', 'ti', 'tu', 'te', 'to', 'na', 'ni', 'nu', 'ne', 'no', 'ha', 'hi', 'hu', 'he', 'ho', 'ma', +'mi', 'mu', 'me', 'mo', 'ya', 'yu', 'yo', 'ra', 'ri', 'ru', 're', 'ro', 'wa', 'n', ':', ';', +'', 'g', 'gg', 'gs', 'n', 'nj', 'nh', 'd', 'dd', 'r', 'lg', 'lm', 'lb', 'ls', 'lt', 'lp', +'rh', 'm', 'b', 'bb', 'bs', 's', 'ss', '', 'j', 'jj', 'c', 'k', 't', 'p', 'h', '[?]', +'[?]', '[?]', 'a', 'ae', 'ya', 'yae', 'eo', 'e', '[?]', '[?]', 'yeo', 'ye', 'o', 'wa', 'wae', 'oe', +'[?]', '[?]', 'yo', 'u', 'weo', 'we', 'wi', 'yu', '[?]', '[?]', 'eu', 'yi', 'i', '[?]', '[?]', '[?]', +'/C', 'PS', '!', '-', '|', 'Y=', 'W=', '[?]', '|', '-', '|', '-', '|', '#', 'O', '[?]', +'[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '[?]', '{', '|', '}', '', '', '', '', +); diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php new file mode 100644 index 0000000..2c72175 --- /dev/null +++ b/vendor/composer/ClassLoader.php @@ -0,0 +1,445 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Autoload; + +/** + * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. + * + * $loader = new \Composer\Autoload\ClassLoader(); + * + * // register classes with namespaces + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (eg. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * This class is loosely based on the Symfony UniversalClassLoader. + * + * @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 +{ + // PSR-4 + private $prefixLengthsPsr4 = array(); + private $prefixDirsPsr4 = array(); + private $fallbackDirsPsr4 = array(); + + // PSR-0 + private $prefixesPsr0 = array(); + private $fallbackDirsPsr0 = array(); + + private $useIncludePath = false; + private $classMap = array(); + private $classMapAuthoritative = false; + private $missingClasses = array(); + private $apcuPrefix; + + public function getPrefixes() + { + if (!empty($this->prefixesPsr0)) { + return call_user_func_array('array_merge', $this->prefixesPsr0); + } + + return array(); + } + + public function getPrefixesPsr4() + { + return $this->prefixDirsPsr4; + } + + public function getFallbackDirs() + { + return $this->fallbackDirsPsr0; + } + + public function getFallbackDirsPsr4() + { + return $this->fallbackDirsPsr4; + } + + public function getClassMap() + { + return $this->classMap; + } + + /** + * @param array $classMap Class to filename map + */ + public function addClassMap(array $classMap) + { + if ($this->classMap) { + $this->classMap = array_merge($this->classMap, $classMap); + } else { + $this->classMap = $classMap; + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, either + * appending or prepending to the ones previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + */ + public function add($prefix, $paths, $prepend = false) + { + if (!$prefix) { + if ($prepend) { + $this->fallbackDirsPsr0 = array_merge( + (array) $paths, + $this->fallbackDirsPsr0 + ); + } else { + $this->fallbackDirsPsr0 = array_merge( + $this->fallbackDirsPsr0, + (array) $paths + ); + } + + return; + } + + $first = $prefix[0]; + if (!isset($this->prefixesPsr0[$first][$prefix])) { + $this->prefixesPsr0[$first][$prefix] = (array) $paths; + + return; + } + if ($prepend) { + $this->prefixesPsr0[$first][$prefix] = array_merge( + (array) $paths, + $this->prefixesPsr0[$first][$prefix] + ); + } else { + $this->prefixesPsr0[$first][$prefix] = array_merge( + $this->prefixesPsr0[$first][$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, either + * 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-4 base directories + * @param bool $prepend Whether to prepend the directories + * + * @throws \InvalidArgumentException + */ + public function addPsr4($prefix, $paths, $prepend = false) + { + if (!$prefix) { + // Register directories for the root namespace. + if ($prepend) { + $this->fallbackDirsPsr4 = array_merge( + (array) $paths, + $this->fallbackDirsPsr4 + ); + } else { + $this->fallbackDirsPsr4 = array_merge( + $this->fallbackDirsPsr4, + (array) $paths + ); + } + } elseif (!isset($this->prefixDirsPsr4[$prefix])) { + // Register directories for a new namespace. + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } elseif ($prepend) { + // Prepend directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + (array) $paths, + $this->prefixDirsPsr4[$prefix] + ); + } else { + // Append directories for an already registered namespace. + $this->prefixDirsPsr4[$prefix] = array_merge( + $this->prefixDirsPsr4[$prefix], + (array) $paths + ); + } + } + + /** + * Registers a set of PSR-0 directories for a given prefix, + * replacing any others previously set for this prefix. + * + * @param string $prefix The prefix + * @param array|string $paths The PSR-0 base directories + */ + public function set($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr0 = (array) $paths; + } else { + $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; + } + } + + /** + * Registers a set of PSR-4 directories for a given namespace, + * replacing any others previously set for this namespace. + * + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param array|string $paths The PSR-4 base directories + * + * @throws \InvalidArgumentException + */ + public function setPsr4($prefix, $paths) + { + if (!$prefix) { + $this->fallbackDirsPsr4 = (array) $paths; + } else { + $length = strlen($prefix); + if ('\\' !== $prefix[$length - 1]) { + throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); + } + $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; + $this->prefixDirsPsr4[$prefix] = (array) $paths; + } + } + + /** + * Turns on searching the include path for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Turns off searching the prefix and fallback directories for classes + * that have not been registered with the class map. + * + * @param bool $classMapAuthoritative + */ + public function setClassMapAuthoritative($classMapAuthoritative) + { + $this->classMapAuthoritative = $classMapAuthoritative; + } + + /** + * Should class lookup fail if not found in the current class map? + * + * @return bool + */ + public function isClassMapAuthoritative() + { + 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. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * @return bool|null True if loaded, null otherwise + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + includeFile($file); + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|false The path if found, false otherwise + */ + public function findFile($class) + { + // class map lookup + if (isset($this->classMap[$class])) { + return $this->classMap[$class]; + } + 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 (false === $file && defined('HHVM_VERSION')) { + $file = $this->findFileWithExtension($class, '.hh'); + } + + if (null !== $this->apcuPrefix) { + apcu_add($this->apcuPrefix.$class, $file); + } + + if (false === $file) { + // Remember that this class does not exist. + $this->missingClasses[$class] = true; + } + + return $file; + } + + private function findFileWithExtension($class, $ext) + { + // PSR-4 lookup + $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; + + $first = $class[0]; + if (isset($this->prefixLengthsPsr4[$first])) { + $subPath = $class; + while (false !== $lastPos = strrpos($subPath, '\\')) { + $subPath = substr($subPath, 0, $lastPos); + $search = $subPath.'\\'; + if (isset($this->prefixDirsPsr4[$search])) { + foreach ($this->prefixDirsPsr4[$search] as $dir) { + $length = $this->prefixLengthsPsr4[$first][$search]; + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + return $file; + } + } + } + } + } + + // PSR-4 fallback dirs + foreach ($this->fallbackDirsPsr4 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { + return $file; + } + } + + // PSR-0 lookup + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) + . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); + } else { + // PEAR-like class name + $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; + } + + if (isset($this->prefixesPsr0[$first])) { + foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { + if (0 === strpos($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + } + } + } + + // PSR-0 fallback dirs + foreach ($this->fallbackDirsPsr0 as $dir) { + if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { + return $file; + } + } + + // PSR-0 include paths. + if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { + return $file; + } + + return false; + } +} + +/** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + */ +function includeFile($file) +{ + include $file; +} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE new file mode 100644 index 0000000..f27399a --- /dev/null +++ b/vendor/composer/LICENSE @@ -0,0 +1,21 @@ + +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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php new file mode 100644 index 0000000..3a14165 --- /dev/null +++ b/vendor/composer/autoload_classmap.php @@ -0,0 +1,16 @@ + $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php', + 'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php', + 'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php', + 'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php', + 'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php', + 'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php', + 'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php', +); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php new file mode 100644 index 0000000..7c37897 --- /dev/null +++ b/vendor/composer/autoload_files.php @@ -0,0 +1,12 @@ + $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', +); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php new file mode 100644 index 0000000..49f2ccc --- /dev/null +++ b/vendor/composer/autoload_namespaces.php @@ -0,0 +1,15 @@ + array($vendorDir . '/winzou/state-machine/src'), + 'PhpMob\\DataFixture' => array($baseDir . '/'), + 'Pagerfanta\\' => array($vendorDir . '/pagerfanta/pagerfanta/src'), + 'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib'), + 'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'), + 'Behat\\Transliterator' => array($vendorDir . '/behat/transliterator/src'), +); diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php new file mode 100644 index 0000000..6f110d2 --- /dev/null +++ b/vendor/composer/autoload_psr4.php @@ -0,0 +1,44 @@ + array($vendorDir . '/zendframework/zend-stdlib/src'), + 'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), + 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), + 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), + 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), + 'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'), + 'Symfony\\Component\\PropertyAccess\\' => array($vendorDir . '/symfony/property-access'), + 'Symfony\\Component\\OptionsResolver\\' => array($vendorDir . '/symfony/options-resolver'), + 'Symfony\\Component\\Inflector\\' => array($vendorDir . '/symfony/inflector'), + 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), + 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), + 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), + 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), + 'Symfony\\Component\\ExpressionLanguage\\' => array($vendorDir . '/symfony/expression-language'), + 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), + 'Symfony\\Component\\DependencyInjection\\' => array($vendorDir . '/symfony/dependency-injection'), + 'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'), + 'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'), + 'Symfony\\Component\\ClassLoader\\' => array($vendorDir . '/symfony/class-loader'), + 'Symfony\\Component\\Cache\\' => array($vendorDir . '/symfony/cache'), + 'Symfony\\Bundle\\FrameworkBundle\\' => array($vendorDir . '/symfony/framework-bundle'), + 'Symfony\\Bridge\\Monolog\\' => array($vendorDir . '/symfony/monolog-bridge'), + 'Sylius\\Component\\Resource\\' => array($vendorDir . '/sylius/resource'), + 'Sylius\\Bundle\\FixturesBundle\\' => array($vendorDir . '/sylius/fixtures-bundle'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), + 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'), + 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), + 'Gedmo\\' => array($vendorDir . '/gedmo/doctrine-extensions/lib/Gedmo'), + 'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'), + 'Doctrine\\Common\\DataFixtures\\' => array($vendorDir . '/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures'), + 'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'), + 'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'), + 'Doctrine\\Common\\' => array($vendorDir . '/doctrine/reflection/lib/Doctrine/Common', $vendorDir . '/doctrine/event-manager/lib/Doctrine/Common', $vendorDir . '/doctrine/persistence/lib/Doctrine/Common', $vendorDir . '/doctrine/common/lib/Doctrine/Common'), +); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php new file mode 100644 index 0000000..bd86424 --- /dev/null +++ b/vendor/composer/autoload_real.php @@ -0,0 +1,90 @@ += 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + if ($useStaticLoader) { + require_once __DIR__ . '/autoload_static.php'; + + call_user_func(\Composer\Autoload\ComposerStaticInitd96e1b8d5b9816b1e211c7a102346a13::getInitializer($loader)); + } else { + $map = require __DIR__ . '/autoload_namespaces.php'; + foreach ($map as $namespace => $path) { + $loader->set($namespace, $path); + } + + $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); + } + } + + spl_autoload_register(array('ComposerAutoloaderInitd96e1b8d5b9816b1e211c7a102346a13', 'autoload'), true, true); + + $loader->register(true); + + if ($useStaticLoader) { + $includeFiles = Composer\Autoload\ComposerStaticInitd96e1b8d5b9816b1e211c7a102346a13::$files; + } else { + $includeFiles = require __DIR__ . '/autoload_files.php'; + } + foreach ($includeFiles as $fileIdentifier => $file) { + composerRequired96e1b8d5b9816b1e211c7a102346a13($fileIdentifier, $file); + } + + return $loader; + } + + public static function autoload($class) + { + $dir = dirname(dirname(__DIR__)) . '/'; + $prefixes = array('PhpMob\\DataFixture'); + foreach ($prefixes as $prefix) { + if (0 !== strpos($class, $prefix)) { + continue; + } + $path = $dir . implode('/', array_slice(explode('\\', $class), 2)).'.php'; + if (!$path = stream_resolve_include_path($path)) { + return false; + } + require $path; + + return true; + } + } +} + +function composerRequired96e1b8d5b9816b1e211c7a102346a13($fileIdentifier, $file) +{ + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + } +} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php new file mode 100644 index 0000000..136962d --- /dev/null +++ b/vendor/composer/autoload_static.php @@ -0,0 +1,279 @@ + __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', + ); + + public static $prefixLengthsPsr4 = array ( + 'Z' => + array ( + 'Zend\\Stdlib\\' => 12, + ), + 'W' => + array ( + 'Webmozart\\Assert\\' => 17, + ), + 'S' => + array ( + 'Symfony\\Polyfill\\Php70\\' => 23, + 'Symfony\\Polyfill\\Mbstring\\' => 26, + 'Symfony\\Polyfill\\Ctype\\' => 23, + 'Symfony\\Component\\Routing\\' => 26, + 'Symfony\\Component\\PropertyAccess\\' => 33, + 'Symfony\\Component\\OptionsResolver\\' => 34, + 'Symfony\\Component\\Inflector\\' => 28, + 'Symfony\\Component\\HttpKernel\\' => 29, + 'Symfony\\Component\\HttpFoundation\\' => 33, + 'Symfony\\Component\\Finder\\' => 25, + 'Symfony\\Component\\Filesystem\\' => 29, + 'Symfony\\Component\\ExpressionLanguage\\' => 37, + 'Symfony\\Component\\EventDispatcher\\' => 34, + 'Symfony\\Component\\DependencyInjection\\' => 38, + 'Symfony\\Component\\Debug\\' => 24, + 'Symfony\\Component\\Config\\' => 25, + 'Symfony\\Component\\ClassLoader\\' => 30, + 'Symfony\\Component\\Cache\\' => 24, + 'Symfony\\Bundle\\FrameworkBundle\\' => 31, + 'Symfony\\Bridge\\Monolog\\' => 23, + 'Sylius\\Component\\Resource\\' => 26, + 'Sylius\\Bundle\\FixturesBundle\\' => 29, + ), + 'P' => + array ( + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Log\\' => 8, + 'Psr\\Container\\' => 14, + 'Psr\\Cache\\' => 10, + ), + 'M' => + array ( + 'Monolog\\' => 8, + ), + 'G' => + array ( + 'Gedmo\\' => 6, + ), + 'D' => + array ( + 'Doctrine\\Common\\Inflector\\' => 26, + 'Doctrine\\Common\\DataFixtures\\' => 29, + 'Doctrine\\Common\\Cache\\' => 22, + 'Doctrine\\Common\\Annotations\\' => 28, + 'Doctrine\\Common\\' => 16, + ), + ); + + public static $prefixDirsPsr4 = array ( + 'Zend\\Stdlib\\' => + array ( + 0 => __DIR__ . '/..' . '/zendframework/zend-stdlib/src', + ), + 'Webmozart\\Assert\\' => + array ( + 0 => __DIR__ . '/..' . '/webmozart/assert/src', + ), + 'Symfony\\Polyfill\\Php70\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', + ), + 'Symfony\\Polyfill\\Mbstring\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', + ), + 'Symfony\\Polyfill\\Ctype\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', + ), + 'Symfony\\Component\\Routing\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/routing', + ), + 'Symfony\\Component\\PropertyAccess\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/property-access', + ), + 'Symfony\\Component\\OptionsResolver\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/options-resolver', + ), + 'Symfony\\Component\\Inflector\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/inflector', + ), + 'Symfony\\Component\\HttpKernel\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-kernel', + ), + 'Symfony\\Component\\HttpFoundation\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/http-foundation', + ), + 'Symfony\\Component\\Finder\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/finder', + ), + 'Symfony\\Component\\Filesystem\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/filesystem', + ), + 'Symfony\\Component\\ExpressionLanguage\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/expression-language', + ), + 'Symfony\\Component\\EventDispatcher\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', + ), + 'Symfony\\Component\\DependencyInjection\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/dependency-injection', + ), + 'Symfony\\Component\\Debug\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/debug', + ), + 'Symfony\\Component\\Config\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/config', + ), + 'Symfony\\Component\\ClassLoader\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/class-loader', + ), + 'Symfony\\Component\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/cache', + ), + 'Symfony\\Bundle\\FrameworkBundle\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/framework-bundle', + ), + 'Symfony\\Bridge\\Monolog\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/monolog-bridge', + ), + 'Sylius\\Component\\Resource\\' => + array ( + 0 => __DIR__ . '/..' . '/sylius/resource', + ), + 'Sylius\\Bundle\\FixturesBundle\\' => + array ( + 0 => __DIR__ . '/..' . '/sylius/fixtures-bundle', + ), + 'Psr\\SimpleCache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/Psr/Log', + ), + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), + 'Psr\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/cache/src', + ), + 'Monolog\\' => + array ( + 0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog', + ), + 'Gedmo\\' => + array ( + 0 => __DIR__ . '/..' . '/gedmo/doctrine-extensions/lib/Gedmo', + ), + 'Doctrine\\Common\\Inflector\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Common/Inflector', + ), + 'Doctrine\\Common\\DataFixtures\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures', + ), + 'Doctrine\\Common\\Cache\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache', + ), + 'Doctrine\\Common\\Annotations\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/annotations/lib/Doctrine/Common/Annotations', + ), + 'Doctrine\\Common\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/reflection/lib/Doctrine/Common', + 1 => __DIR__ . '/..' . '/doctrine/event-manager/lib/Doctrine/Common', + 2 => __DIR__ . '/..' . '/doctrine/persistence/lib/Doctrine/Common', + 3 => __DIR__ . '/..' . '/doctrine/common/lib/Doctrine/Common', + ), + ); + + public static $prefixesPsr0 = array ( + 'S' => + array ( + 'SM' => + array ( + 0 => __DIR__ . '/..' . '/winzou/state-machine/src', + ), + ), + 'P' => + array ( + 'PhpMob\\DataFixture' => + array ( + 0 => __DIR__ . '/../..' . '/', + ), + 'Pagerfanta\\' => + array ( + 0 => __DIR__ . '/..' . '/pagerfanta/pagerfanta/src', + ), + ), + 'D' => + array ( + 'Doctrine\\Common\\Lexer\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/lexer/lib', + ), + 'Doctrine\\Common\\Collections\\' => + array ( + 0 => __DIR__ . '/..' . '/doctrine/collections/lib', + ), + ), + 'B' => + array ( + 'Behat\\Transliterator' => + array ( + 0 => __DIR__ . '/..' . '/behat/transliterator/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 = ComposerStaticInitd96e1b8d5b9816b1e211c7a102346a13::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInitd96e1b8d5b9816b1e211c7a102346a13::$prefixDirsPsr4; + $loader->prefixesPsr0 = ComposerStaticInitd96e1b8d5b9816b1e211c7a102346a13::$prefixesPsr0; + $loader->classMap = ComposerStaticInitd96e1b8d5b9816b1e211c7a102346a13::$classMap; + + }, null, ClassLoader::class); + } +} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json new file mode 100644 index 0000000..a6bfc64 --- /dev/null +++ b/vendor/composer/installed.json @@ -0,0 +1,2879 @@ +[ + { + "name": "psr/simple-cache", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2017-10-23T01:57:42+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ] + }, + { + "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/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ] + }, + { + "name": "psr/cache", + "version": "1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "time": "2016-08-06T20:24:11+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ] + }, + { + "name": "symfony/cache", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "05ce0ddc8bc1ffe592105398fc2c725cb3080a38" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/05ce0ddc8bc1ffe592105398fc2c725cb3080a38", + "reference": "05ce0ddc8bc1ffe592105398fc2c725cb3080a38", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/cache": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0" + }, + "conflict": { + "symfony/var-dumper": "<3.4" + }, + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "time": "2018-09-30T03:38:13+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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 Cache component with PSR-6, PSR-16, and tags", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ] + }, + { + "name": "symfony/expression-language", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "065bba63c61c96fd2d4fbd01b28de058e6f8779a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/065bba63c61c96fd2d4fbd01b28de058e6f8779a", + "reference": "065bba63c61c96fd2d4fbd01b28de058e6f8779a", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/cache": "~3.4|~4.0" + }, + "time": "2018-07-26T09:10:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "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 ExpressionLanguage Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.9.0", + "version_normalized": "1.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", + "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "time": "2018-08-06T14:22:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ] + }, + { + "name": "symfony/inflector", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/inflector.git", + "reference": "07810b5c88ec0c2e98972571a40a126b44664e13" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/inflector/zipball/07810b5c88ec0c2e98972571a40a126b44664e13", + "reference": "07810b5c88ec0c2e98972571a40a126b44664e13", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "time": "2018-07-26T08:55:25+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Inflector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Inflector Component", + "homepage": "https://symfony.com", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string", + "symfony", + "words" + ] + }, + { + "name": "paragonie/random_compat", + "version": "v9.99.99", + "version_normalized": "9.99.99.0", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "reference": "84b4dfb120c6f9b4ff7b3685f9b8f1aa365a0c95", + "shasum": "" + }, + "require": { + "php": "^7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "time": "2018-07-02T15:55:56+00:00", + "type": "library", + "installation-source": "dist", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ] + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.9.0", + "version_normalized": "1.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0|~2.0|~9.99", + "php": ">=5.3.3" + }, + "time": "2018-08-06T14:22:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-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/property-access", + "version": "v3.4.17", + "version_normalized": "3.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "a62b882330f24b43f4409e50a0692aa433947460" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/a62b882330f24b43f4409e50a0692aa433947460", + "reference": "a62b882330f24b43f4409e50a0692aa433947460", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/inflector": "~3.1|~4.0", + "symfony/polyfill-php70": "~1.0" + }, + "require-dev": { + "symfony/cache": "~3.1|~4.0" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "time": "2018-10-02T12:28:39+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "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 PropertyAccess Component", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ] + }, + { + "name": "symfony/event-dispatcher", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bfb30c2ad377615a463ebbc875eba64a99f6aa3e", + "reference": "bfb30c2ad377615a463ebbc875eba64a99f6aa3e", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0" + }, + "suggest": { + "symfony/dependency-injection": "", + "symfony/http-kernel": "" + }, + "time": "2018-07-26T09:10:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-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": "winzou/state-machine", + "version": "0.3.3", + "version_normalized": "0.3.3.0", + "source": { + "type": "git", + "url": "https://github.com/winzou/state-machine.git", + "reference": "37f03a316b9a461ed443906e158bab8d358542df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/winzou/state-machine/zipball/37f03a316b9a461ed443906e158bab8d358542df", + "reference": "37f03a316b9a461ed443906e158bab8d358542df", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/event-dispatcher": "~2.1|~3.0|~4.0", + "symfony/expression-language": "~2.4|~3.0|~4.0", + "symfony/property-access": "~2.1|~3.0|~4.0" + }, + "require-dev": { + "phpspec/phpspec": "~2.0", + "twig/twig": "~1.0" + }, + "suggest": { + "twig/twig": "Access the state machine in your twig templates (~1.0)" + }, + "time": "2018-02-11T18:07:15+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "SM": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexandre Bacco", + "email": "alexandre.bacco@gmail.com", + "homepage": "http://alex.bacco.fr" + } + ], + "description": "A very lightweight yet powerful PHP state machine", + "homepage": "https://github.com/winzou/StateMachine", + "keywords": [ + "callback", + "event", + "state", + "statemachine" + ] + }, + { + "name": "pagerfanta/pagerfanta", + "version": "v2.0.1", + "version_normalized": "2.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/whiteoctober/Pagerfanta.git", + "reference": "15770d9d7f6e8e07af568aed104a51f869591e73" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/whiteoctober/Pagerfanta/zipball/15770d9d7f6e8e07af568aed104a51f869591e73", + "reference": "15770d9d7f6e8e07af568aed104a51f869591e73", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "doctrine/phpcr-odm": "1.*", + "jackalope/jackalope-doctrine-dbal": "1.*", + "jmikola/geojson": "~1.0", + "mandango/mandango": "~1.0@dev", + "mandango/mondator": "~1.0@dev", + "phpunit/phpunit": "^5.7|^6", + "propel/propel": "~2.0@dev", + "propel/propel1": "~1.6", + "ruflin/elastica": "~1.3", + "solarium/solarium": "~3.1" + }, + "suggest": { + "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.", + "doctrine/orm": "To use the DoctrineORMAdapter.", + "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0", + "mandango/mandango": "To use the MandangoAdapter.", + "propel/propel": "To use the Propel2Adapter", + "propel/propel1": "To use the PropelAdapter", + "solarium/solarium": "To use the SolariumAdapter." + }, + "time": "2018-05-17T09:23:52+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Pagerfanta\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Pablo Díez", + "email": "pablodip@gmail.com" + } + ], + "description": "Pagination for PHP 5.3", + "keywords": [ + "page", + "pagination", + "paginator", + "paging" + ] + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "time": "2014-09-09T13:34:57+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ] + }, + { + "name": "doctrine/annotations", + "version": "v1.6.0", + "version_normalized": "1.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^6.4" + }, + "time": "2017-12-06T07:11:42+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ] + }, + { + "name": "doctrine/reflection", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/reflection.git", + "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/reflection/zipball/02538d3f95e88eb397a5f86274deb2c6175c2ab6", + "reference": "02538d3f95e88eb397a5f86274deb2c6175c2ab6", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "ext-tokenizer": "*", + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "doctrine/common": "^2.8", + "phpstan/phpstan": "^0.9.2", + "phpstan/phpstan-phpunit": "^0.9.4", + "phpunit/phpunit": "^7.0", + "squizlabs/php_codesniffer": "^3.0" + }, + "time": "2018-06-14T14:45:07+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Reflection component", + "homepage": "https://www.doctrine-project.org/projects/reflection.html", + "keywords": [ + "reflection" + ] + }, + { + "name": "doctrine/event-manager", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "phpunit/phpunit": "^7.0" + }, + "time": "2018-06-11T11:59:03+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Event Manager component", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "eventdispatcher", + "eventmanager" + ] + }, + { + "name": "doctrine/collections", + "version": "v1.5.0", + "version_normalized": "1.5.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "~0.1@dev", + "phpunit/phpunit": "^5.7" + }, + "time": "2017-07-22T10:37:32+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ] + }, + { + "name": "doctrine/cache", + "version": "v1.8.0", + "version_normalized": "1.8.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57", + "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57", + "shasum": "" + }, + "require": { + "php": "~7.1" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "^1.1", + "doctrine/coding-standard": "^4.0", + "mongodb/mongodb": "^1.1", + "phpunit/phpunit": "^7.0", + "predis/predis": "~1.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + }, + "time": "2018-08-21T18:01:43+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ] + }, + { + "name": "doctrine/persistence", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/persistence.git", + "reference": "af1ec238659a83e320f03e0e454e200f689b4b97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/af1ec238659a83e320f03e0e454e200f689b4b97", + "reference": "af1ec238659a83e320f03e0e454e200f689b4b97", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "doctrine/cache": "^1.0", + "doctrine/collections": "^1.0", + "doctrine/event-manager": "^1.0", + "doctrine/reflection": "^1.0", + "php": "^7.1" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "require-dev": { + "doctrine/coding-standard": "^4.0", + "phpstan/phpstan": "^0.8", + "phpunit/phpunit": "^7.0" + }, + "time": "2018-07-12T12:37:50+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Doctrine Persistence abstractions.", + "homepage": "https://doctrine-project.org/projects/persistence.html", + "keywords": [ + "persistence" + ] + }, + { + "name": "doctrine/inflector", + "version": "v1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a", + "shasum": "" + }, + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^6.2" + }, + "time": "2018-01-09T20:05:19+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ] + }, + { + "name": "doctrine/common", + "version": "v2.9.0", + "version_normalized": "2.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a210246d286c77d2b89040f8691ba7b3a713d2c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a210246d286c77d2b89040f8691ba7b3a713d2c1", + "reference": "a210246d286c77d2b89040f8691ba7b3a713d2c1", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0", + "doctrine/cache": "^1.0", + "doctrine/collections": "^1.0", + "doctrine/event-manager": "^1.0", + "doctrine/inflector": "^1.0", + "doctrine/lexer": "^1.0", + "doctrine/persistence": "^1.0", + "doctrine/reflection": "^1.0", + "php": "^7.1" + }, + "require-dev": { + "doctrine/coding-standard": "^1.0", + "phpunit/phpunit": "^6.3", + "squizlabs/php_codesniffer": "^3.0", + "symfony/phpunit-bridge": "^4.0.5" + }, + "time": "2018-07-12T21:16:12+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ] + }, + { + "name": "behat/transliterator", + "version": "v1.2.0", + "version_normalized": "1.2.0.0", + "source": { + "type": "git", + "url": "https://github.com/Behat/Transliterator.git", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0" + }, + "time": "2017-04-04T11:38:05+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-0": { + "Behat\\Transliterator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Artistic-1.0" + ], + "description": "String transliterator", + "keywords": [ + "i18n", + "slug", + "transliterator" + ] + }, + { + "name": "gedmo/doctrine-extensions", + "version": "v2.4.36", + "version_normalized": "2.4.36.0", + "source": { + "type": "git", + "url": "https://github.com/Atlantic18/DoctrineExtensions.git", + "reference": "87c78ff9fd4b90460386f753d95622f6fbbfcb27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Atlantic18/DoctrineExtensions/zipball/87c78ff9fd4b90460386f753d95622f6fbbfcb27", + "reference": "87c78ff9fd4b90460386f753d95622f6fbbfcb27", + "shasum": "" + }, + "require": { + "behat/transliterator": "~1.2", + "doctrine/common": "~2.4", + "php": ">=5.3.2" + }, + "conflict": { + "doctrine/annotations": "<1.2" + }, + "require-dev": { + "doctrine/common": ">=2.5.0", + "doctrine/mongodb-odm": ">=1.0.2", + "doctrine/orm": ">=2.5.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.5", + "symfony/yaml": "~2.6|~3.0|~4.0" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "time": "2018-07-26T12:16:35+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Gedmo\\": "lib/Gedmo" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David Buchmann", + "email": "david@liip.ch" + }, + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + } + ], + "description": "Doctrine2 behavioral extensions", + "homepage": "http://gediminasm.org/", + "keywords": [ + "Blameable", + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "loggable", + "nestedset", + "sluggable", + "sortable", + "timestampable", + "translatable", + "tree", + "uploadable" + ] + }, + { + "name": "sylius/resource", + "version": "v1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Sylius/Resource.git", + "reference": "2b93ce7e90e5e0d00d7a90f018f8d354f0c0a45f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Sylius/Resource/zipball/2b93ce7e90e5e0d00d7a90f018f8d354f0c0a45f", + "reference": "2b93ce7e90e5e0d00d7a90f018f8d354f0c0a45f", + "shasum": "" + }, + "require": { + "doctrine/common": "^2.6", + "gedmo/doctrine-extensions": "^2.4", + "pagerfanta/pagerfanta": "^1.0|^2.0", + "php": "^7.2", + "symfony/event-dispatcher": "^3.4|^4.1.1", + "symfony/property-access": "^3.4|^4.1.1", + "winzou/state-machine": "^0.3" + }, + "require-dev": { + "phpspec/phpspec": "^4.0", + "sylius/locale": "^1.0" + }, + "suggest": { + "sylius/locale": "^1.0" + }, + "time": "2018-10-03T08:07:33+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sylius\\Component\\Resource\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Community contributions", + "homepage": "http://github.com/Sylius/Sylius/contributors" + }, + { + "name": "Paweł Jędrzejewski", + "homepage": "http://pjedrzejewski.com" + }, + { + "name": "Sylius project", + "homepage": "http://sylius.com" + } + ], + "description": "Basic resource interfaces for PHP applications.", + "homepage": "http://sylius.com", + "keywords": [ + "api", + "doctrine", + "ecommerce", + "resource", + "shop", + "sylius" + ] + }, + { + "name": "zendframework/zend-stdlib", + "version": "3.2.1", + "version_normalized": "3.2.1.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-stdlib.git", + "reference": "66536006722aff9e62d1b331025089b7ec71c065" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/66536006722aff9e62d1b331025089b7ec71c065", + "reference": "66536006722aff9e62d1b331025089b7ec71c065", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpbench/phpbench": "^0.13", + "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2", + "zendframework/zend-coding-standard": "~1.0.0" + }, + "time": "2018-08-28T21:34:05+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev", + "dev-develop": "3.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Zend\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "SPL extensions, array utilities, error handlers, and more", + "keywords": [ + "ZendFramework", + "stdlib", + "zf" + ] + }, + { + "name": "webmozart/assert", + "version": "1.3.0", + "version_normalized": "1.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "time": "2018-01-29T19:49:41+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ] + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.9.0", + "version_normalized": "1.9.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", + "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2018-08-06T14:22:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.9-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/http-foundation", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "d528136617ff24f530e70df9605acc1b788b08d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d528136617ff24f530e70df9605acc1b788b08d4", + "reference": "d528136617ff24f530e70df9605acc1b788b08d4", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/expression-language": "~3.4|~4.0" + }, + "time": "2018-10-03T08:48:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-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/debug", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "e3f76ce6198f81994e019bb2b4e533e9de1b9b90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/e3f76ce6198f81994e019bb2b4e533e9de1b9b90", + "reference": "e3f76ce6198f81994e019bb2b4e533e9de1b9b90", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "~3.4|~4.0" + }, + "time": "2018-10-02T16:36:10+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-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/http-kernel", + "version": "v4.0.14", + "version_normalized": "4.0.14.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "569c6ec5aff02421ac6f3417807972fea48140e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/569c6ec5aff02421ac6f3417807972fea48140e2", + "reference": "569c6ec5aff02421ac6f3417807972fea48140e2", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/log": "~1.0", + "symfony/debug": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", + "symfony/var-dumper": "<3.4", + "twig/twig": "<1.34|<2.4,>=2" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/cache": "~1.0", + "symfony/browser-kit": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4.10|^4.0.10", + "symfony/dom-crawler": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/var-dumper": "" + }, + "time": "2018-08-01T14:57:57+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-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": "monolog/monolog", + "version": "1.23.0", + "version_normalized": "1.23.0.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" + }, + "time": "2017-06-19T01:22:40+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ] + }, + { + "name": "symfony/monolog-bridge", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bridge.git", + "reference": "858737f5ec0266ed37b6b687020283b6e78ae220" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/858737f5ec0266ed37b6b687020283b6e78ae220", + "reference": "858737f5ec0266ed37b6b687020283b6e78ae220", + "shasum": "" + }, + "require": { + "monolog/monolog": "~1.19", + "php": "^7.1.3", + "symfony/http-kernel": "~3.4|~4.0" + }, + "conflict": { + "symfony/console": "<3.4", + "symfony/http-foundation": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", + "symfony/event-dispatcher": "Needed when using log messages in console commands.", + "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", + "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." + }, + "time": "2018-09-21T12:49:42+00:00", + "type": "symfony-bridge", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Monolog\\": "" + }, + "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 Monolog Bridge", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/routing", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "537803f0bdfede36b9acef052d2e4d447d9fa0e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/537803f0bdfede36b9acef052d2e4d447d9fa0e9", + "reference": "537803f0bdfede36b9acef052d2e4d447d9fa0e9", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "symfony/yaml": "<3.4" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "psr/log": "~1.0", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~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-10-02T12:40:59+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-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": "symfony/finder", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/1f17195b44543017a9c9b2d437c670627e96ad06", + "reference": "1f17195b44543017a9c9b2d437c670627e96ad06", + "shasum": "" + }, + "require": { + "php": "^7.1.3" + }, + "time": "2018-10-03T08:47:56+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "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 Finder Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/filesystem", + "version": "v4.1.6", + "version_normalized": "4.1.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "596d12b40624055c300c8b619755b748ca5cf0b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/596d12b40624055c300c8b619755b748ca5cf0b5", + "reference": "596d12b40624055c300c8b619755b748ca5cf0b5", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "time": "2018-10-02T12:40:59+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "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 Filesystem Component", + "homepage": "https://symfony.com" + }, + { + "name": "psr/container", + "version": "1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "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-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": "symfony/config", + "version": "v3.4.17", + "version_normalized": "3.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "e5389132dc6320682de3643091121c048ff796b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3", + "reference": "e5389132dc6320682de3643091121c048ff796b3", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "time": "2018-09-08T13:15:14+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "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 Config Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/dependency-injection", + "version": "v4.0.14", + "version_normalized": "4.0.14.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "36401d55d9be1fddd4e8be983b2a7e0b9a5f29ac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/36401d55d9be1fddd4e8be983b2a7e0b9a5f29ac", + "reference": "36401d55d9be1fddd4e8be983b2a7e0b9a5f29ac", + "shasum": "" + }, + "require": { + "php": "^7.1.3", + "psr/container": "^1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/finder": "<3.4", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "require-dev": { + "symfony/config": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0" + }, + "suggest": { + "symfony/config": "", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "time": "2018-08-01T08:23:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "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 DependencyInjection Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/class-loader", + "version": "v3.4.17", + "version_normalized": "3.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/class-loader.git", + "reference": "f31333bdff54c7595f834d510a6d2325573ddb36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/f31333bdff54c7595f834d510a6d2325573ddb36", + "reference": "f31333bdff54c7595f834d510a6d2325573ddb36", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "time": "2018-10-02T12:28:39+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\ClassLoader\\": "" + }, + "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 ClassLoader Component", + "homepage": "https://symfony.com" + }, + { + "name": "symfony/framework-bundle", + "version": "v3.4.17", + "version_normalized": "3.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "3736bf6f3578613a010954ee66c597e7b1bed75b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/3736bf6f3578613a010954ee66c597e7b1bed75b", + "reference": "3736bf6f3578613a010954ee66c597e7b1bed75b", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": "^5.5.9|>=7.0.8", + "symfony/cache": "~3.4|~4.0", + "symfony/class-loader": "~3.2", + "symfony/config": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4.3|^4.0.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^3.4.5|^4.0.5" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.3" + }, + "require-dev": { + "doctrine/annotations": "~1.0", + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "symfony/asset": "~3.3|~4.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/form": "~3.4|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/property-info": "~3.3|~4.0", + "symfony/security-core": "~3.2|~4.0", + "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", + "symfony/serializer": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "symfony/web-link": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~3.2|~4.0", + "twig/twig": "~1.34|~2.4" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/property-info": "For using the property_info service", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering", + "symfony/yaml": "For using the debug:config and lint:yaml commands" + }, + "time": "2018-10-03T08:41:12+00:00", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "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 FrameworkBundle", + "homepage": "https://symfony.com" + }, + { + "name": "doctrine/data-fixtures", + "version": "v1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a", + "reference": "3a1e2c3c600e615a2dffe56d4ca0875cc5233e0a", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.2", + "php": "^7.1" + }, + "conflict": { + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "doctrine/dbal": "^2.5.4", + "doctrine/orm": "^2.5.4", + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM with PHP 7", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "time": "2018-03-20T09:06:36+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database" + ] + }, + { + "name": "sylius/fixtures-bundle", + "version": "v1.3.1", + "version_normalized": "1.3.1.0", + "source": { + "type": "git", + "url": "https://github.com/Sylius/SyliusFixturesBundle.git", + "reference": "bc930b9cfc9c8d96a3f46543be56bdb4e5badfe4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Sylius/SyliusFixturesBundle/zipball/bc930b9cfc9c8d96a3f46543be56bdb4e5badfe4", + "reference": "bc930b9cfc9c8d96a3f46543be56bdb4e5badfe4", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "^1.2", + "monolog/monolog": "^1.8", + "php": "^7.2", + "symfony/framework-bundle": "^3.4|^4.1.1", + "symfony/monolog-bridge": "^3.4|^4.1.1", + "webmozart/assert": "^1.0", + "zendframework/zend-stdlib": "^3.0" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^1.3", + "doctrine/orm": "^2.5", + "matthiasnoback/symfony-config-test": "^3.0", + "matthiasnoback/symfony-dependency-injection-test": "^2.0", + "phpspec/phpspec": "^4.0", + "phpunit/phpunit": "^6.5", + "polishsymfonycommunity/symfony-mocker-container": "^1.0", + "symfony/browser-kit": "^3.4|^4.1.1", + "symfony/dependency-injection": "^3.4|^4.1.1", + "symfony/security-csrf": "^3.4|^4.1.1", + "symfony/templating": "^3.4|^4.1.1", + "symfony/translation": "^3.4|^4.1.1", + "symfony/twig-bundle": "^3.4|^4.1.1", + "twig/twig": "^2.0" + }, + "time": "2018-10-03T08:07:33+00:00", + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Sylius\\Bundle\\FixturesBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Community contributions", + "homepage": "http://github.com/Sylius/Sylius/contributors" + }, + { + "name": "Kamil Kokot", + "homepage": "http://kamil.kokot.me" + }, + { + "name": "Sylius project", + "homepage": "http://sylius.com" + } + ], + "description": "Configurable fixtures for Symfony applications.", + "homepage": "http://sylius.com", + "keywords": [ + "fixtures", + "sylius", + "symfony" + ] + }, + { + "name": "symfony/options-resolver", + "version": "v3.4.17", + "version_normalized": "3.4.17.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "time": "2018-09-17T17:29:18+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "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 OptionsResolver Component", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ] + } +] diff --git a/vendor/doctrine/annotations/CHANGELOG.md b/vendor/doctrine/annotations/CHANGELOG.md new file mode 100644 index 0000000..100b006 --- /dev/null +++ b/vendor/doctrine/annotations/CHANGELOG.md @@ -0,0 +1,130 @@ +## Changelog + +### 1.5.0 + +This release increments the minimum supported PHP version to 7.1.0. + +Also, HHVM official support has been dropped. + +Some noticeable performance improvements to annotation autoloading +have been applied, making failed annotation autoloading less heavy +on the filesystem access. + +- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException +- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci +- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135) thanks to @jrjohnson +- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137) thanks to @Ocramius + + +### 1.4.0 + +This release fix an issue were some annotations could be not loaded if the namespace in the use statement started with a backslash. +It also update the tests and drop the support for php 5.X + +- [115: Missing annotations with the latest composer version](https://github.com/doctrine/annotations/issues/115) thanks to @pascalporedda +- [120: Missing annotations with the latest composer version](https://github.com/doctrine/annotations/pull/120) thanks to @gnat42 +- [121: Adding a more detailed explanation of the test](https://github.com/doctrine/annotations/pull/121) thanks to @mikeSimonson +- [101: Test annotation parameters containing space](https://github.com/doctrine/annotations/pull/101) thanks to @mikeSimonson +- [111: Cleanup: move to correct phpunit assertions](https://github.com/doctrine/annotations/pull/111) thanks to @Ocramius +- [112: Removes support for PHP 5.x](https://github.com/doctrine/annotations/pull/112) thanks to @railto +- [113: bumped phpunit version to 5.7](https://github.com/doctrine/annotations/pull/113) thanks to @gabbydgab +- [114: Enhancement: Use SVG Travis build badge](https://github.com/doctrine/annotations/pull/114) thanks to @localheinz +- [118: Integrating PHPStan](https://github.com/doctrine/annotations/pull/118) thanks to @ondrejmirtes + +### 1.3.1 - 2016-12-30 + +This release fixes an issue with ignored annotations that were already +autoloaded, causing the `SimpleAnnotationReader` to pick them up +anyway. [#110](https://github.com/doctrine/annotations/pull/110) + +Additionally, an issue was fixed in the `CachedReader`, which was +not correctly checking the freshness of cached annotations when +traits were defined on a class. [#105](https://github.com/doctrine/annotations/pull/105) + +Total issues resolved: **2** + +- [105: Return single max timestamp](https://github.com/doctrine/annotations/pull/105) +- [110: setIgnoreNotImportedAnnotations(true) didn’t work for existing classes](https://github.com/doctrine/annotations/pull/110) + +### 1.3.0 + +This release introduces a PHP version bump. `doctrine/annotations` now requires PHP +5.6 or later to be installed. + +A series of additional improvements have been introduced: + + * support for PHP 7 "grouped use statements" + * support for ignoring entire namespace names + via `Doctrine\Common\Annotations\AnnotationReader::addGlobalIgnoredNamespace()` and + `Doctrine\Common\Annotations\DocParser::setIgnoredAnnotationNamespaces()`. This will + allow you to ignore annotations from namespaces that you cannot autoload + * testing all parent classes and interfaces when checking if the annotation cache + in the `CachedReader` is fresh + * simplifying the cache keys used by the `CachedReader`: keys are no longer artificially + namespaced, since `Doctrine\Common\Cache` already supports that + * corrected parsing of multibyte strings when `mbstring.func_overload` is enabled + * corrected parsing of annotations when `"\t"` is put before the first annotation + in a docblock + * allow skipping non-imported annotations when a custom `DocParser` is passed to + the `AnnotationReader` constructor + +Total issues resolved: **15** + +- [45: DocParser can now ignore whole namespaces](https://github.com/doctrine/annotations/pull/45) +- [57: Switch to the docker-based infrastructure on Travis](https://github.com/doctrine/annotations/pull/57) +- [59: opcache.load_comments has been removed from PHP 7](https://github.com/doctrine/annotations/pull/59) +- [62: [CachedReader\ Test traits and parent class to see if cache is fresh](https://github.com/doctrine/annotations/pull/62) +- [65: Remove cache salt making key unnecessarily long](https://github.com/doctrine/annotations/pull/65) +- [66: Fix of incorrect parsing multibyte strings](https://github.com/doctrine/annotations/pull/66) +- [68: Annotations that are indented by tab are not processed.](https://github.com/doctrine/annotations/issues/68) +- [69: Support for Group Use Statements](https://github.com/doctrine/annotations/pull/69) +- [70: Allow tab character before first annotation in DocBlock](https://github.com/doctrine/annotations/pull/70) +- [74: Ignore not registered annotations fix](https://github.com/doctrine/annotations/pull/74) +- [92: Added tests for AnnotationRegistry class.](https://github.com/doctrine/annotations/pull/92) +- [96: Fix/#62 check trait and parent class ttl in annotations](https://github.com/doctrine/annotations/pull/96) +- [97: Feature - #45 - allow ignoring entire namespaces](https://github.com/doctrine/annotations/pull/97) +- [98: Enhancement/#65 remove cache salt from cached reader](https://github.com/doctrine/annotations/pull/98) +- [99: Fix - #70 - allow tab character before first annotation in docblock](https://github.com/doctrine/annotations/pull/99) + +### 1.2.4 + +Total issues resolved: **1** + +- [51: FileCacheReader::saveCacheFile::unlink fix](https://github.com/doctrine/annotations/pull/51) + +### 1.2.3 + +Total issues resolved: [**2**](https://github.com/doctrine/annotations/milestones/v1.2.3) + +- [49: #46 - applying correct `chmod()` to generated cache file](https://github.com/doctrine/annotations/pull/49) +- [50: Hotfix: match escaped quotes (revert #44)](https://github.com/doctrine/annotations/pull/50) + +### 1.2.2 + +Total issues resolved: **4** + +- [43: Exclude files from distribution with .gitattributes](https://github.com/doctrine/annotations/pull/43) +- [44: Update DocLexer.php](https://github.com/doctrine/annotations/pull/44) +- [46: A plain "file_put_contents" can cause havoc](https://github.com/doctrine/annotations/pull/46) +- [48: Deprecating the `FileCacheReader` in 1.2.2: will be removed in 2.0.0](https://github.com/doctrine/annotations/pull/48) + +### 1.2.1 + +Total issues resolved: **4** + +- [38: fixes doctrine/common#326](https://github.com/doctrine/annotations/pull/38) +- [39: Remove superfluous NS](https://github.com/doctrine/annotations/pull/39) +- [41: Warn if load_comments is not enabled.](https://github.com/doctrine/annotations/pull/41) +- [42: Clean up unused uses](https://github.com/doctrine/annotations/pull/42) + +### 1.2.0 + + * HHVM support + * Allowing dangling comma in annotations + * Excluded annotations are no longer autoloaded + * Importing namespaces also in traits + * Added support for `::class` 5.5-style constant, works also in 5.3 and 5.4 + +### 1.1.0 + + * Add Exception when ZendOptimizer+ or Opcache is configured to drop comments diff --git a/vendor/doctrine/annotations/LICENSE b/vendor/doctrine/annotations/LICENSE new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/vendor/doctrine/annotations/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/annotations/README.md b/vendor/doctrine/annotations/README.md new file mode 100644 index 0000000..8f89ea5 --- /dev/null +++ b/vendor/doctrine/annotations/README.md @@ -0,0 +1,17 @@ +# Doctrine Annotations + +[![Build Status](https://travis-ci.org/doctrine/annotations.svg?branch=master)](https://travis-ci.org/doctrine/annotations) +[![Dependency Status](https://www.versioneye.com/package/php--doctrine--annotations/badge.png)](https://www.versioneye.com/package/php--doctrine--annotations) +[![Reference Status](https://www.versioneye.com/php/doctrine:annotations/reference_badge.svg)](https://www.versioneye.com/php/doctrine:annotations/references) +[![Total Downloads](https://poser.pugx.org/doctrine/annotations/downloads.png)](https://packagist.org/packages/doctrine/annotations) +[![Latest Stable Version](https://poser.pugx.org/doctrine/annotations/v/stable.png)](https://packagist.org/packages/doctrine/annotations) + +Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)). + +## Documentation + +See the [doctrine-project website](http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html). + +## Changelog + +See [CHANGELOG.md](CHANGELOG.md). diff --git a/vendor/doctrine/annotations/composer.json b/vendor/doctrine/annotations/composer.json new file mode 100644 index 0000000..cd5b630 --- /dev/null +++ b/vendor/doctrine/annotations/composer.json @@ -0,0 +1,34 @@ +{ + "name": "doctrine/annotations", + "type": "library", + "description": "Docblock Annotations Parser", + "keywords": ["annotations", "docblock", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": "^7.1", + "doctrine/lexer": "1.*" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "^6.4" + }, + "autoload": { + "psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } + }, + "autoload-dev": { + "psr-4": { "Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php new file mode 100644 index 0000000..a79a0f8 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation.php @@ -0,0 +1,79 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Annotations class. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class Annotation +{ + /** + * Value property. Common among all derived classes. + * + * @var string + */ + public $value; + + /** + * Constructor. + * + * @param array $data Key-value for properties to be defined in this class. + */ + public final function __construct(array $data) + { + foreach ($data as $key => $value) { + $this->$key = $value; + } + } + + /** + * Error handler for unknown property accessor in Annotation class. + * + * @param string $name Unknown property name. + * + * @throws \BadMethodCallException + */ + public function __get($name) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } + + /** + * Error handler for unknown property mutator in Annotation class. + * + * @param string $name Unknown property name. + * @param mixed $value Property value. + * + * @throws \BadMethodCallException + */ + public function __set($name, $value) + { + throw new \BadMethodCallException( + sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this)) + ); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php new file mode 100644 index 0000000..dbef6df --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attribute.php @@ -0,0 +1,47 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the attribute type during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Attribute +{ + /** + * @var string + */ + public $name; + + /** + * @var string + */ + public $type; + + /** + * @var boolean + */ + public $required = false; +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php new file mode 100644 index 0000000..53134e3 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Attributes.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the types of all declared attributes during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Attributes +{ + /** + * @var array + */ + public $value; +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php new file mode 100644 index 0000000..e122a75 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Enum.php @@ -0,0 +1,84 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the available values during the parsing process. + * + * @since 2.4 + * @author Fabio B. Silva + * + * @Annotation + * @Attributes({ + * @Attribute("value", required = true, type = "array"), + * @Attribute("literal", required = false, type = "array") + * }) + */ +final class Enum +{ + /** + * @var array + */ + public $value; + + /** + * Literal target declaration. + * + * @var array + */ + public $literal; + + /** + * Annotation constructor. + * + * @param array $values + * + * @throws \InvalidArgumentException + */ + public function __construct(array $values) + { + if ( ! isset($values['literal'])) { + $values['literal'] = array(); + } + + foreach ($values['value'] as $var) { + if( ! is_scalar($var)) { + throw new \InvalidArgumentException(sprintf( + '@Enum supports only scalar values "%s" given.', + is_object($var) ? get_class($var) : gettype($var) + )); + } + } + + foreach ($values['literal'] as $key => $var) { + if( ! in_array($key, $values['value'])) { + throw new \InvalidArgumentException(sprintf( + 'Undefined enumerator value "%s" for literal "%s".', + $key , $var + )); + } + } + + $this->value = $values['value']; + $this->literal = $values['literal']; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php new file mode 100644 index 0000000..175226a --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/IgnoreAnnotation.php @@ -0,0 +1,54 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser to ignore specific + * annotations during the parsing process. + * + * @Annotation + * @author Johannes M. Schmitt + */ +final class IgnoreAnnotation +{ + /** + * @var array + */ + public $names; + + /** + * Constructor. + * + * @param array $values + * + * @throws \RuntimeException + */ + public function __construct(array $values) + { + if (is_string($values['value'])) { + $values['value'] = array($values['value']); + } + if (!is_array($values['value'])) { + throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value']))); + } + + $this->names = $values['value']; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php new file mode 100644 index 0000000..d67f960 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Required.php @@ -0,0 +1,33 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check if that attribute is required during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Required +{ +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php new file mode 100644 index 0000000..f6c5445 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Annotation/Target.php @@ -0,0 +1,107 @@ +. + */ + +namespace Doctrine\Common\Annotations\Annotation; + +/** + * Annotation that can be used to signal to the parser + * to check the annotation target during the parsing process. + * + * @author Fabio B. Silva + * + * @Annotation + */ +final class Target +{ + const TARGET_CLASS = 1; + const TARGET_METHOD = 2; + const TARGET_PROPERTY = 4; + const TARGET_ANNOTATION = 8; + const TARGET_ALL = 15; + + /** + * @var array + */ + private static $map = array( + 'ALL' => self::TARGET_ALL, + 'CLASS' => self::TARGET_CLASS, + 'METHOD' => self::TARGET_METHOD, + 'PROPERTY' => self::TARGET_PROPERTY, + 'ANNOTATION' => self::TARGET_ANNOTATION, + ); + + /** + * @var array + */ + public $value; + + /** + * Targets as bitmask. + * + * @var integer + */ + public $targets; + + /** + * Literal target declaration. + * + * @var integer + */ + public $literal; + + /** + * Annotation constructor. + * + * @param array $values + * + * @throws \InvalidArgumentException + */ + public function __construct(array $values) + { + if (!isset($values['value'])){ + $values['value'] = null; + } + if (is_string($values['value'])){ + $values['value'] = array($values['value']); + } + if (!is_array($values['value'])){ + throw new \InvalidArgumentException( + sprintf('@Target expects either a string value, or an array of strings, "%s" given.', + is_object($values['value']) ? get_class($values['value']) : gettype($values['value']) + ) + ); + } + + $bitmask = 0; + foreach ($values['value'] as $literal) { + if(!isset(self::$map[$literal])){ + throw new \InvalidArgumentException( + sprintf('Invalid Target "%s". Available targets: [%s]', + $literal, implode(', ', array_keys(self::$map))) + ); + } + $bitmask |= self::$map[$literal]; + } + + $this->targets = $bitmask; + $this->value = $values['value']; + $this->literal = implode(', ', $this->value); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php new file mode 100644 index 0000000..d06fe66 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationException.php @@ -0,0 +1,197 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Description of AnnotationException + * + * @since 2.0 + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class AnnotationException extends \Exception +{ + /** + * Creates a new AnnotationException describing a Syntax error. + * + * @param string $message Exception message + * + * @return AnnotationException + */ + public static function syntaxError($message) + { + return new self('[Syntax Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a Semantical error. + * + * @param string $message Exception message + * + * @return AnnotationException + */ + public static function semanticalError($message) + { + return new self('[Semantical Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing an error which occurred during + * the creation of the annotation. + * + * @since 2.2 + * + * @param string $message + * + * @return AnnotationException + */ + public static function creationError($message) + { + return new self('[Creation Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a type error. + * + * @since 1.1 + * + * @param string $message + * + * @return AnnotationException + */ + public static function typeError($message) + { + return new self('[Type Error] ' . $message); + } + + /** + * Creates a new AnnotationException describing a constant semantical error. + * + * @since 2.3 + * + * @param string $identifier + * @param string $context + * + * @return AnnotationException + */ + public static function semanticalErrorConstants($identifier, $context = null) + { + return self::semanticalError(sprintf( + "Couldn't find constant %s%s.", + $identifier, + $context ? ', ' . $context : '' + )); + } + + /** + * Creates a new AnnotationException describing an type error of an attribute. + * + * @since 2.2 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param string $expected + * @param mixed $actual + * + * @return AnnotationException + */ + public static function attributeTypeError($attributeName, $annotationName, $context, $expected, $actual) + { + return self::typeError(sprintf( + 'Attribute "%s" of @%s declared on %s expects %s, but got %s.', + $attributeName, + $annotationName, + $context, + $expected, + is_object($actual) ? 'an instance of ' . get_class($actual) : gettype($actual) + )); + } + + /** + * Creates a new AnnotationException describing an required error of an attribute. + * + * @since 2.2 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param string $expected + * + * @return AnnotationException + */ + public static function requiredError($attributeName, $annotationName, $context, $expected) + { + return self::typeError(sprintf( + 'Attribute "%s" of @%s declared on %s expects %s. This value should not be null.', + $attributeName, + $annotationName, + $context, + $expected + )); + } + + /** + * Creates a new AnnotationException describing a invalid enummerator. + * + * @since 2.4 + * + * @param string $attributeName + * @param string $annotationName + * @param string $context + * @param array $available + * @param mixed $given + * + * @return AnnotationException + */ + public static function enumeratorError($attributeName, $annotationName, $context, $available, $given) + { + return new self(sprintf( + '[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.', + $attributeName, + $annotationName, + $context, + implode(', ', $available), + is_object($given) ? get_class($given) : $given + )); + } + + /** + * @return AnnotationException + */ + public static function optimizerPlusSaveComments() + { + return new self( + "You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1." + ); + } + + /** + * @return AnnotationException + */ + public static function optimizerPlusLoadComments() + { + return new self( + "You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1." + ); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php new file mode 100644 index 0000000..0c06049 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationReader.php @@ -0,0 +1,425 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation; +use Doctrine\Common\Annotations\Annotation\Target; +use ReflectionClass; +use ReflectionMethod; +use ReflectionProperty; + +/** + * A reader for docblock annotations. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + */ +class AnnotationReader implements Reader +{ + /** + * Global map for imports. + * + * @var array + */ + private static $globalImports = array( + 'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation', + ); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names are case sensitive. + * + * @var array + */ + private static $globalIgnoredNames = array( + // Annotation tags + 'Annotation' => true, 'Attribute' => true, 'Attributes' => true, + /* Can we enable this? 'Enum' => true, */ + 'Required' => true, + 'Target' => true, + // Widely used tags (but not existent in phpdoc) + 'fix' => true , 'fixme' => true, + 'override' => true, + // PHPDocumentor 1 tags + 'abstract'=> true, 'access'=> true, + 'code' => true, + 'deprec'=> true, + 'endcode' => true, 'exception'=> true, + 'final'=> true, + 'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true, + 'magic' => true, + 'name'=> true, + 'toc' => true, 'tutorial'=> true, + 'private' => true, + 'static'=> true, 'staticvar'=> true, 'staticVar'=> true, + 'throw' => true, + // PHPDocumentor 2 tags. + 'api' => true, 'author'=> true, + 'category'=> true, 'copyright'=> true, + 'deprecated'=> true, + 'example'=> true, + 'filesource'=> true, + 'global'=> true, + 'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true, + 'license'=> true, 'link'=> true, + 'method' => true, + 'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true, + 'return'=> true, + 'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true, + 'throws'=> true, 'todo'=> true, 'TODO'=> true, + 'usedby'=> true, 'uses' => true, + 'var'=> true, 'version'=> true, + // PHPUnit tags + 'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true, + // PHPCheckStyle + 'SuppressWarnings' => true, + // PHPStorm + 'noinspection' => true, + // PEAR + 'package_version' => true, + // PlantUML + 'startuml' => true, 'enduml' => true, + // Symfony 3.3 Cache Adapter + 'experimental' => true + ); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names are case sensitive. + * + * @var array + */ + private static $globalIgnoredNamespaces = array(); + + /** + * Add a new annotation to the globally ignored annotation names with regard to exception handling. + * + * @param string $name + */ + static public function addGlobalIgnoredName($name) + { + self::$globalIgnoredNames[$name] = true; + } + + /** + * Add a new annotation to the globally ignored annotation namespaces with regard to exception handling. + * + * @param string $namespace + */ + static public function addGlobalIgnoredNamespace($namespace) + { + self::$globalIgnoredNamespaces[$namespace] = true; + } + + /** + * Annotations parser. + * + * @var \Doctrine\Common\Annotations\DocParser + */ + private $parser; + + /** + * Annotations parser used to collect parsing metadata. + * + * @var \Doctrine\Common\Annotations\DocParser + */ + private $preParser; + + /** + * PHP parser used to collect imports. + * + * @var \Doctrine\Common\Annotations\PhpParser + */ + private $phpParser; + + /** + * In-memory cache mechanism to store imported annotations per class. + * + * @var array + */ + private $imports = array(); + + /** + * In-memory cache mechanism to store ignored annotations per class. + * + * @var array + */ + private $ignoredAnnotationNames = array(); + + /** + * Constructor. + * + * Initializes a new AnnotationReader. + * + * @param DocParser $parser + * + * @throws AnnotationException + */ + public function __construct(DocParser $parser = null) + { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) { + throw AnnotationException::optimizerPlusSaveComments(); + } + + if (PHP_VERSION_ID < 70000) { + if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.load_comments') === "0" || ini_get('opcache.load_comments') === "0")) { + throw AnnotationException::optimizerPlusLoadComments(); + } + + if (extension_loaded('Zend OPcache') && ini_get('opcache.load_comments') == 0) { + throw AnnotationException::optimizerPlusLoadComments(); + } + } + + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php'); + + $this->parser = $parser ?: new DocParser(); + + $this->preParser = new DocParser; + + $this->preParser->setImports(self::$globalImports); + $this->preParser->setIgnoreNotImportedAnnotations(true); + + $this->phpParser = new PhpParser; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(ReflectionClass $class) + { + $this->parser->setTarget(Target::TARGET_CLASS); + $this->parser->setImports($this->getClassImports($class)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); + + return $this->parser->parse($class->getDocComment(), 'class ' . $class->getName()); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $context = 'property ' . $class->getName() . "::\$" . $property->getName(); + + $this->parser->setTarget(Target::TARGET_PROPERTY); + $this->parser->setImports($this->getPropertyImports($property)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); + + return $this->parser->parse($property->getDocComment(), $context); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; + + $this->parser->setTarget(Target::TARGET_METHOD); + $this->parser->setImports($this->getMethodImports($method)); + $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); + $this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces); + + return $this->parser->parse($method->getDocComment(), $context); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Returns the ignored annotations for the given class. + * + * @param \ReflectionClass $class + * + * @return array + */ + private function getIgnoredAnnotationNames(ReflectionClass $class) + { + $name = $class->getName(); + if (isset($this->ignoredAnnotationNames[$name])) { + return $this->ignoredAnnotationNames[$name]; + } + + $this->collectParsingMetadata($class); + + return $this->ignoredAnnotationNames[$name]; + } + + /** + * Retrieves imports. + * + * @param \ReflectionClass $class + * + * @return array + */ + private function getClassImports(ReflectionClass $class) + { + $name = $class->getName(); + if (isset($this->imports[$name])) { + return $this->imports[$name]; + } + + $this->collectParsingMetadata($class); + + return $this->imports[$name]; + } + + /** + * Retrieves imports for methods. + * + * @param \ReflectionMethod $method + * + * @return array + */ + private function getMethodImports(ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $classImports = $this->getClassImports($class); + if (!method_exists($class, 'getTraits')) { + return $classImports; + } + + $traitImports = array(); + + foreach ($class->getTraits() as $trait) { + if ($trait->hasMethod($method->getName()) + && $trait->getFileName() === $method->getFileName() + ) { + $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); + } + } + + return array_merge($classImports, $traitImports); + } + + /** + * Retrieves imports for properties. + * + * @param \ReflectionProperty $property + * + * @return array + */ + private function getPropertyImports(ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $classImports = $this->getClassImports($class); + if (!method_exists($class, 'getTraits')) { + return $classImports; + } + + $traitImports = array(); + + foreach ($class->getTraits() as $trait) { + if ($trait->hasProperty($property->getName())) { + $traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait)); + } + } + + return array_merge($classImports, $traitImports); + } + + /** + * Collects parsing metadata for a given class. + * + * @param \ReflectionClass $class + */ + private function collectParsingMetadata(ReflectionClass $class) + { + $ignoredAnnotationNames = self::$globalIgnoredNames; + $annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name); + + foreach ($annotations as $annotation) { + if ($annotation instanceof IgnoreAnnotation) { + foreach ($annotation->names AS $annot) { + $ignoredAnnotationNames[$annot] = true; + } + } + } + + $name = $class->getName(); + + $this->imports[$name] = array_merge( + self::$globalImports, + $this->phpParser->parseClass($class), + array('__NAMESPACE__' => $class->getNamespaceName()) + ); + + $this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php new file mode 100644 index 0000000..13abaf5 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/AnnotationRegistry.php @@ -0,0 +1,174 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +final class AnnotationRegistry +{ + /** + * A map of namespaces to use for autoloading purposes based on a PSR-0 convention. + * + * Contains the namespace as key and an array of directories as value. If the value is NULL + * the include path is used for checking for the corresponding file. + * + * This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own. + * + * @var string[][]|string[]|null[] + */ + static private $autoloadNamespaces = []; + + /** + * A map of autoloader callables. + * + * @var callable[] + */ + static private $loaders = []; + + /** + * An array of classes which cannot be found + * + * @var null[] indexed by class name + */ + static private $failedToAutoload = []; + + public static function reset() : void + { + self::$autoloadNamespaces = []; + self::$loaders = []; + self::$failedToAutoload = []; + } + + /** + * Registers file. + * + * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0 + * autoloading should be deferred to the globally registered autoloader by then. For now, + * use @example AnnotationRegistry::registerLoader('class_exists') + */ + public static function registerFile(string $file) : void + { + require_once $file; + } + + /** + * Adds a namespace with one or many directories to look for files or null for the include path. + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param string $namespace + * @param string|array|null $dirs + * + * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0 + * autoloading should be deferred to the globally registered autoloader by then. For now, + * use @example AnnotationRegistry::registerLoader('class_exists') + */ + public static function registerAutoloadNamespace(string $namespace, $dirs = null) : void + { + self::$autoloadNamespaces[$namespace] = $dirs; + } + + /** + * Registers multiple namespaces. + * + * Loading of this namespaces will be done with a PSR-0 namespace loading algorithm. + * + * @param string[][]|string[]|null[] $namespaces indexed by namespace name + * + * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0 + * autoloading should be deferred to the globally registered autoloader by then. For now, + * use @example AnnotationRegistry::registerLoader('class_exists') + */ + public static function registerAutoloadNamespaces(array $namespaces) : void + { + self::$autoloadNamespaces = \array_merge(self::$autoloadNamespaces, $namespaces); + } + + /** + * Registers an autoloading callable for annotations, much like spl_autoload_register(). + * + * NOTE: These class loaders HAVE to be silent when a class was not found! + * IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class. + * + * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0 + * autoloading should be deferred to the globally registered autoloader by then. For now, + * use @example AnnotationRegistry::registerLoader('class_exists') + */ + public static function registerLoader(callable $callable) : void + { + // Reset our static cache now that we have a new loader to work with + self::$failedToAutoload = []; + self::$loaders[] = $callable; + } + + /** + * Registers an autoloading callable for annotations, if it is not already registered + * + * @deprecated this method is deprecated and will be removed in doctrine/annotations 2.0 + */ + public static function registerUniqueLoader(callable $callable) : void + { + if ( ! in_array($callable, self::$loaders, true) ) { + self::registerLoader($callable); + } + } + + /** + * Autoloads an annotation class silently. + */ + public static function loadAnnotationClass(string $class) : bool + { + if (\class_exists($class, false)) { + return true; + } + + if (\array_key_exists($class, self::$failedToAutoload)) { + return false; + } + + foreach (self::$autoloadNamespaces AS $namespace => $dirs) { + if (\strpos($class, $namespace) === 0) { + $file = \str_replace('\\', \DIRECTORY_SEPARATOR, $class) . '.php'; + + if ($dirs === null) { + if ($path = stream_resolve_include_path($file)) { + require $path; + return true; + } + } else { + foreach((array) $dirs AS $dir) { + if (is_file($dir . \DIRECTORY_SEPARATOR . $file)) { + require $dir . \DIRECTORY_SEPARATOR . $file; + return true; + } + } + } + } + } + + foreach (self::$loaders AS $loader) { + if ($loader($class) === true) { + return true; + } + } + + self::$failedToAutoload[$class] = null; + + return false; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php new file mode 100644 index 0000000..751c1b1 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/CachedReader.php @@ -0,0 +1,262 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Cache\Cache; +use ReflectionClass; + +/** + * A cache aware annotation reader. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + */ +final class CachedReader implements Reader +{ + /** + * @var Reader + */ + private $delegate; + + /** + * @var Cache + */ + private $cache; + + /** + * @var boolean + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * Constructor. + * + * @param Reader $reader + * @param Cache $cache + * @param bool $debug + */ + public function __construct(Reader $reader, Cache $cache, $debug = false) + { + $this->delegate = $reader; + $this->cache = $cache; + $this->debug = (boolean) $debug; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(ReflectionClass $class) + { + $cacheKey = $class->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getClassAnnotations($class); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + $cacheKey = $class->getName().'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getPropertyAnnotations($property); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + $cacheKey = $class->getName().'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$cacheKey])) { + return $this->loadedAnnotations[$cacheKey]; + } + + if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) { + $annots = $this->delegate->getMethodAnnotations($method); + $this->saveToCache($cacheKey, $annots); + } + + return $this->loadedAnnotations[$cacheKey] = $annots; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * Clears loaded annotations. + * + * @return void + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } + + /** + * Fetches a value from the cache. + * + * @param string $cacheKey The cache key. + * @param ReflectionClass $class The related class. + * + * @return mixed The cached value or false when the value is not in cache. + */ + private function fetchFromCache($cacheKey, ReflectionClass $class) + { + if (($data = $this->cache->fetch($cacheKey)) !== false) { + if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) { + return $data; + } + } + + return false; + } + + /** + * Saves a value to the cache. + * + * @param string $cacheKey The cache key. + * @param mixed $value The value. + * + * @return void + */ + private function saveToCache($cacheKey, $value) + { + $this->cache->save($cacheKey, $value); + if ($this->debug) { + $this->cache->save('[C]'.$cacheKey, time()); + } + } + + /** + * Checks if the cache is fresh. + * + * @param string $cacheKey + * @param ReflectionClass $class + * + * @return boolean + */ + private function isCacheFresh($cacheKey, ReflectionClass $class) + { + if (null === $lastModification = $this->getLastModification($class)) { + return true; + } + + return $this->cache->fetch('[C]'.$cacheKey) >= $lastModification; + } + + /** + * Returns the time the class was last modified, testing traits and parents + * + * @param ReflectionClass $class + * @return int + */ + private function getLastModification(ReflectionClass $class) + { + $filename = $class->getFileName(); + $parent = $class->getParentClass(); + + return max(array_merge( + [$filename ? filemtime($filename) : 0], + array_map([$this, 'getTraitLastModificationTime'], $class->getTraits()), + array_map([$this, 'getLastModification'], $class->getInterfaces()), + $parent ? [$this->getLastModification($parent)] : [] + )); + } + + /** + * @param ReflectionClass $reflectionTrait + * @return int + */ + private function getTraitLastModificationTime(ReflectionClass $reflectionTrait) + { + $fileName = $reflectionTrait->getFileName(); + + return max(array_merge( + [$fileName ? filemtime($fileName) : 0], + array_map([$this, 'getTraitLastModificationTime'], $reflectionTrait->getTraits()) + )); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php new file mode 100644 index 0000000..d864540 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocLexer.php @@ -0,0 +1,134 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Lexer\AbstractLexer; + +/** + * Simple lexer for docblock annotations. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + */ +final class DocLexer extends AbstractLexer +{ + const T_NONE = 1; + const T_INTEGER = 2; + const T_STRING = 3; + const T_FLOAT = 4; + + // All tokens that are also identifiers should be >= 100 + const T_IDENTIFIER = 100; + const T_AT = 101; + const T_CLOSE_CURLY_BRACES = 102; + const T_CLOSE_PARENTHESIS = 103; + const T_COMMA = 104; + const T_EQUALS = 105; + const T_FALSE = 106; + const T_NAMESPACE_SEPARATOR = 107; + const T_OPEN_CURLY_BRACES = 108; + const T_OPEN_PARENTHESIS = 109; + const T_TRUE = 110; + const T_NULL = 111; + const T_COLON = 112; + + /** + * @var array + */ + protected $noCase = array( + '@' => self::T_AT, + ',' => self::T_COMMA, + '(' => self::T_OPEN_PARENTHESIS, + ')' => self::T_CLOSE_PARENTHESIS, + '{' => self::T_OPEN_CURLY_BRACES, + '}' => self::T_CLOSE_CURLY_BRACES, + '=' => self::T_EQUALS, + ':' => self::T_COLON, + '\\' => self::T_NAMESPACE_SEPARATOR + ); + + /** + * @var array + */ + protected $withCase = array( + 'true' => self::T_TRUE, + 'false' => self::T_FALSE, + 'null' => self::T_NULL + ); + + /** + * {@inheritdoc} + */ + protected function getCatchablePatterns() + { + return array( + '[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*', + '(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?', + '"(?:""|[^"])*+"', + ); + } + + /** + * {@inheritdoc} + */ + protected function getNonCatchablePatterns() + { + return array('\s+', '\*+', '(.)'); + } + + /** + * {@inheritdoc} + */ + protected function getType(&$value) + { + $type = self::T_NONE; + + if ($value[0] === '"') { + $value = str_replace('""', '"', substr($value, 1, strlen($value) - 2)); + + return self::T_STRING; + } + + if (isset($this->noCase[$value])) { + return $this->noCase[$value]; + } + + if ($value[0] === '_' || $value[0] === '\\' || ctype_alpha($value[0])) { + return self::T_IDENTIFIER; + } + + $lowerValue = strtolower($value); + + if (isset($this->withCase[$lowerValue])) { + return $this->withCase[$lowerValue]; + } + + // Checking numeric value + if (is_numeric($value)) { + return (strpos($value, '.') !== false || stripos($value, 'e') !== false) + ? self::T_FLOAT : self::T_INTEGER; + } + + return $type; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php new file mode 100644 index 0000000..eb7a457 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/DocParser.php @@ -0,0 +1,1190 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use Doctrine\Common\Annotations\Annotation\Attribute; +use ReflectionClass; +use Doctrine\Common\Annotations\Annotation\Enum; +use Doctrine\Common\Annotations\Annotation\Target; +use Doctrine\Common\Annotations\Annotation\Attributes; + +/** + * A parser for docblock annotations. + * + * It is strongly discouraged to change the default annotation parsing process. + * + * @author Benjamin Eberlei + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * @author Johannes M. Schmitt + * @author Fabio B. Silva + */ +final class DocParser +{ + /** + * An array of all valid tokens for a class name. + * + * @var array + */ + private static $classIdentifiers = array( + DocLexer::T_IDENTIFIER, + DocLexer::T_TRUE, + DocLexer::T_FALSE, + DocLexer::T_NULL + ); + + /** + * The lexer. + * + * @var \Doctrine\Common\Annotations\DocLexer + */ + private $lexer; + + /** + * Current target context. + * + * @var integer + */ + private $target; + + /** + * Doc parser used to collect annotation target. + * + * @var \Doctrine\Common\Annotations\DocParser + */ + private static $metadataParser; + + /** + * Flag to control if the current annotation is nested or not. + * + * @var boolean + */ + private $isNestedAnnotation = false; + + /** + * Hashmap containing all use-statements that are to be used when parsing + * the given doc block. + * + * @var array + */ + private $imports = array(); + + /** + * This hashmap is used internally to cache results of class_exists() + * look-ups. + * + * @var array + */ + private $classExists = array(); + + /** + * Whether annotations that have not been imported should be ignored. + * + * @var boolean + */ + private $ignoreNotImportedAnnotations = false; + + /** + * An array of default namespaces if operating in simple mode. + * + * @var string[] + */ + private $namespaces = array(); + + /** + * A list with annotations that are not causing exceptions when not resolved to an annotation class. + * + * The names must be the raw names as used in the class, not the fully qualified + * class names. + * + * @var bool[] indexed by annotation name + */ + private $ignoredAnnotationNames = array(); + + /** + * A list with annotations in namespaced format + * that are not causing exceptions when not resolved to an annotation class. + * + * @var bool[] indexed by namespace name + */ + private $ignoredAnnotationNamespaces = array(); + + /** + * @var string + */ + private $context = ''; + + /** + * Hash-map for caching annotation metadata. + * + * @var array + */ + private static $annotationMetadata = array( + 'Doctrine\Common\Annotations\Annotation\Target' => array( + 'is_annotation' => true, + 'has_constructor' => true, + 'properties' => array(), + 'targets_literal' => 'ANNOTATION_CLASS', + 'targets' => Target::TARGET_CLASS, + 'default_property' => 'value', + 'attribute_types' => array( + 'value' => array( + 'required' => false, + 'type' =>'array', + 'array_type'=>'string', + 'value' =>'array' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Attribute' => array( + 'is_annotation' => true, + 'has_constructor' => false, + 'targets_literal' => 'ANNOTATION_ANNOTATION', + 'targets' => Target::TARGET_ANNOTATION, + 'default_property' => 'name', + 'properties' => array( + 'name' => 'name', + 'type' => 'type', + 'required' => 'required' + ), + 'attribute_types' => array( + 'value' => array( + 'required' => true, + 'type' =>'string', + 'value' =>'string' + ), + 'type' => array( + 'required' =>true, + 'type' =>'string', + 'value' =>'string' + ), + 'required' => array( + 'required' =>false, + 'type' =>'boolean', + 'value' =>'boolean' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Attributes' => array( + 'is_annotation' => true, + 'has_constructor' => false, + 'targets_literal' => 'ANNOTATION_CLASS', + 'targets' => Target::TARGET_CLASS, + 'default_property' => 'value', + 'properties' => array( + 'value' => 'value' + ), + 'attribute_types' => array( + 'value' => array( + 'type' =>'array', + 'required' =>true, + 'array_type'=>'Doctrine\Common\Annotations\Annotation\Attribute', + 'value' =>'array' + ) + ), + ), + 'Doctrine\Common\Annotations\Annotation\Enum' => array( + 'is_annotation' => true, + 'has_constructor' => true, + 'targets_literal' => 'ANNOTATION_PROPERTY', + 'targets' => Target::TARGET_PROPERTY, + 'default_property' => 'value', + 'properties' => array( + 'value' => 'value' + ), + 'attribute_types' => array( + 'value' => array( + 'type' => 'array', + 'required' => true, + ), + 'literal' => array( + 'type' => 'array', + 'required' => false, + ), + ), + ), + ); + + /** + * Hash-map for handle types declaration. + * + * @var array + */ + private static $typeMap = array( + 'float' => 'double', + 'bool' => 'boolean', + // allow uppercase Boolean in honor of George Boole + 'Boolean' => 'boolean', + 'int' => 'integer', + ); + + /** + * Constructs a new DocParser. + */ + public function __construct() + { + $this->lexer = new DocLexer; + } + + /** + * Sets the annotation names that are ignored during the parsing process. + * + * The names are supposed to be the raw names as used in the class, not the + * fully qualified class names. + * + * @param bool[] $names indexed by annotation name + * + * @return void + */ + public function setIgnoredAnnotationNames(array $names) + { + $this->ignoredAnnotationNames = $names; + } + + /** + * Sets the annotation namespaces that are ignored during the parsing process. + * + * @param bool[] $ignoredAnnotationNamespaces indexed by annotation namespace name + * + * @return void + */ + public function setIgnoredAnnotationNamespaces($ignoredAnnotationNamespaces) + { + $this->ignoredAnnotationNamespaces = $ignoredAnnotationNamespaces; + } + + /** + * Sets ignore on not-imported annotations. + * + * @param boolean $bool + * + * @return void + */ + public function setIgnoreNotImportedAnnotations($bool) + { + $this->ignoreNotImportedAnnotations = (boolean) $bool; + } + + /** + * Sets the default namespaces. + * + * @param string $namespace + * + * @return void + * + * @throws \RuntimeException + */ + public function addNamespace($namespace) + { + if ($this->imports) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + + $this->namespaces[] = $namespace; + } + + /** + * Sets the imports. + * + * @param array $imports + * + * @return void + * + * @throws \RuntimeException + */ + public function setImports(array $imports) + { + if ($this->namespaces) { + throw new \RuntimeException('You must either use addNamespace(), or setImports(), but not both.'); + } + + $this->imports = $imports; + } + + /** + * Sets current target context as bitmask. + * + * @param integer $target + * + * @return void + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Parses the given docblock string for annotations. + * + * @param string $input The docblock string to parse. + * @param string $context The parsing context. + * + * @return array Array of annotations. If no annotations are found, an empty array is returned. + */ + public function parse($input, $context = '') + { + $pos = $this->findInitialTokenPosition($input); + if ($pos === null) { + return array(); + } + + $this->context = $context; + + $this->lexer->setInput(trim(substr($input, $pos), '* /')); + $this->lexer->moveNext(); + + return $this->Annotations(); + } + + /** + * Finds the first valid annotation + * + * @param string $input The docblock string to parse + * + * @return int|null + */ + private function findInitialTokenPosition($input) + { + $pos = 0; + + // search for first valid annotation + while (($pos = strpos($input, '@', $pos)) !== false) { + $preceding = substr($input, $pos - 1, 1); + + // if the @ is preceded by a space, a tab or * it is valid + if ($pos === 0 || $preceding === ' ' || $preceding === '*' || $preceding === "\t") { + return $pos; + } + + $pos++; + } + + return null; + } + + /** + * Attempts to match the given token with the current lookahead token. + * If they match, updates the lookahead token; otherwise raises a syntax error. + * + * @param integer $token Type of token. + * + * @return boolean True if tokens match; false otherwise. + */ + private function match($token) + { + if ( ! $this->lexer->isNextToken($token) ) { + $this->syntaxError($this->lexer->getLiteral($token)); + } + + return $this->lexer->moveNext(); + } + + /** + * Attempts to match the current lookahead token with any of the given tokens. + * + * If any of them matches, this method updates the lookahead token; otherwise + * a syntax error is raised. + * + * @param array $tokens + * + * @return boolean + */ + private function matchAny(array $tokens) + { + if ( ! $this->lexer->isNextTokenAny($tokens)) { + $this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens))); + } + + return $this->lexer->moveNext(); + } + + /** + * Generates a new syntax error. + * + * @param string $expected Expected string. + * @param array|null $token Optional token. + * + * @return void + * + * @throws AnnotationException + */ + private function syntaxError($expected, $token = null) + { + if ($token === null) { + $token = $this->lexer->lookahead; + } + + $message = sprintf('Expected %s, got ', $expected); + $message .= ($this->lexer->lookahead === null) + ? 'end of string' + : sprintf("'%s' at position %s", $token['value'], $token['position']); + + if (strlen($this->context)) { + $message .= ' in ' . $this->context; + } + + $message .= '.'; + + throw AnnotationException::syntaxError($message); + } + + /** + * Attempts to check if a class exists or not. This never goes through the PHP autoloading mechanism + * but uses the {@link AnnotationRegistry} to load classes. + * + * @param string $fqcn + * + * @return boolean + */ + private function classExists($fqcn) + { + if (isset($this->classExists[$fqcn])) { + return $this->classExists[$fqcn]; + } + + // first check if the class already exists, maybe loaded through another AnnotationReader + if (class_exists($fqcn, false)) { + return $this->classExists[$fqcn] = true; + } + + // final check, does this class exist? + return $this->classExists[$fqcn] = AnnotationRegistry::loadAnnotationClass($fqcn); + } + + /** + * Collects parsing metadata for a given annotation class + * + * @param string $name The annotation name + * + * @return void + */ + private function collectAnnotationMetadata($name) + { + if (self::$metadataParser === null) { + self::$metadataParser = new self(); + + self::$metadataParser->setIgnoreNotImportedAnnotations(true); + self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames); + self::$metadataParser->setImports(array( + 'enum' => 'Doctrine\Common\Annotations\Annotation\Enum', + 'target' => 'Doctrine\Common\Annotations\Annotation\Target', + 'attribute' => 'Doctrine\Common\Annotations\Annotation\Attribute', + 'attributes' => 'Doctrine\Common\Annotations\Annotation\Attributes' + )); + + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Enum.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php'); + AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php'); + } + + $class = new \ReflectionClass($name); + $docComment = $class->getDocComment(); + + // Sets default values for annotation metadata + $metadata = array( + 'default_property' => null, + 'has_constructor' => (null !== $constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0, + 'properties' => array(), + 'property_types' => array(), + 'attribute_types' => array(), + 'targets_literal' => null, + 'targets' => Target::TARGET_ALL, + 'is_annotation' => false !== strpos($docComment, '@Annotation'), + ); + + // verify that the class is really meant to be an annotation + if ($metadata['is_annotation']) { + self::$metadataParser->setTarget(Target::TARGET_CLASS); + + foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { + if ($annotation instanceof Target) { + $metadata['targets'] = $annotation->targets; + $metadata['targets_literal'] = $annotation->literal; + + continue; + } + + if ($annotation instanceof Attributes) { + foreach ($annotation->value as $attribute) { + $this->collectAttributeTypeMetadata($metadata, $attribute); + } + } + } + + // if not has a constructor will inject values into public properties + if (false === $metadata['has_constructor']) { + // collect all public properties + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $metadata['properties'][$property->name] = $property->name; + + if (false === ($propertyComment = $property->getDocComment())) { + continue; + } + + $attribute = new Attribute(); + + $attribute->required = (false !== strpos($propertyComment, '@Required')); + $attribute->name = $property->name; + $attribute->type = (false !== strpos($propertyComment, '@var') && preg_match('/@var\s+([^\s]+)/',$propertyComment, $matches)) + ? $matches[1] + : 'mixed'; + + $this->collectAttributeTypeMetadata($metadata, $attribute); + + // checks if the property has @Enum + if (false !== strpos($propertyComment, '@Enum')) { + $context = 'property ' . $class->name . "::\$" . $property->name; + + self::$metadataParser->setTarget(Target::TARGET_PROPERTY); + + foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) { + if ( ! $annotation instanceof Enum) { + continue; + } + + $metadata['enum'][$property->name]['value'] = $annotation->value; + $metadata['enum'][$property->name]['literal'] = ( ! empty($annotation->literal)) + ? $annotation->literal + : $annotation->value; + } + } + } + + // choose the first property as default property + $metadata['default_property'] = reset($metadata['properties']); + } + } + + self::$annotationMetadata[$name] = $metadata; + } + + /** + * Collects parsing metadata for a given attribute. + * + * @param array $metadata + * @param Attribute $attribute + * + * @return void + */ + private function collectAttributeTypeMetadata(&$metadata, Attribute $attribute) + { + // handle internal type declaration + $type = isset(self::$typeMap[$attribute->type]) + ? self::$typeMap[$attribute->type] + : $attribute->type; + + // handle the case if the property type is mixed + if ('mixed' === $type) { + return; + } + + // Evaluate type + switch (true) { + // Checks if the property has array + case (false !== $pos = strpos($type, '<')): + $arrayType = substr($type, $pos + 1, -1); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; + break; + + // Checks if the property has type[] + case (false !== $pos = strrpos($type, '[')): + $arrayType = substr($type, 0, $pos); + $type = 'array'; + + if (isset(self::$typeMap[$arrayType])) { + $arrayType = self::$typeMap[$arrayType]; + } + + $metadata['attribute_types'][$attribute->name]['array_type'] = $arrayType; + break; + } + + $metadata['attribute_types'][$attribute->name]['type'] = $type; + $metadata['attribute_types'][$attribute->name]['value'] = $attribute->type; + $metadata['attribute_types'][$attribute->name]['required'] = $attribute->required; + } + + /** + * Annotations ::= Annotation {[ "*" ]* [Annotation]}* + * + * @return array + */ + private function Annotations() + { + $annotations = array(); + + while (null !== $this->lexer->lookahead) { + if (DocLexer::T_AT !== $this->lexer->lookahead['type']) { + $this->lexer->moveNext(); + continue; + } + + // make sure the @ is preceded by non-catchable pattern + if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) { + $this->lexer->moveNext(); + continue; + } + + // make sure the @ is followed by either a namespace separator, or + // an identifier token + if ((null === $peek = $this->lexer->glimpse()) + || (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true)) + || $peek['position'] !== $this->lexer->lookahead['position'] + 1) { + $this->lexer->moveNext(); + continue; + } + + $this->isNestedAnnotation = false; + if (false !== $annot = $this->Annotation()) { + $annotations[] = $annot; + } + } + + return $annotations; + } + + /** + * Annotation ::= "@" AnnotationName MethodCall + * AnnotationName ::= QualifiedName | SimpleName + * QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName + * NameSpacePart ::= identifier | null | false | true + * SimpleName ::= identifier | null | false | true + * + * @return mixed False if it is not a valid annotation. + * + * @throws AnnotationException + */ + private function Annotation() + { + $this->match(DocLexer::T_AT); + + // check if we have an annotation + $name = $this->Identifier(); + + // only process names which are not fully qualified, yet + // fully qualified names must start with a \ + $originalName = $name; + + if ('\\' !== $name[0]) { + $pos = strpos($name, '\\'); + $alias = (false === $pos)? $name : substr($name, 0, $pos); + $found = false; + $loweredAlias = strtolower($alias); + + if ($this->namespaces) { + foreach ($this->namespaces as $namespace) { + if ($this->classExists($namespace.'\\'.$name)) { + $name = $namespace.'\\'.$name; + $found = true; + break; + } + } + } elseif (isset($this->imports[$loweredAlias])) { + $found = true; + $name = (false !== $pos) + ? $this->imports[$loweredAlias] . substr($name, $pos) + : $this->imports[$loweredAlias]; + } elseif ( ! isset($this->ignoredAnnotationNames[$name]) + && isset($this->imports['__NAMESPACE__']) + && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name) + ) { + $name = $this->imports['__NAMESPACE__'].'\\'.$name; + $found = true; + } elseif (! isset($this->ignoredAnnotationNames[$name]) && $this->classExists($name)) { + $found = true; + } + + if ( ! $found) { + if ($this->isIgnoredAnnotation($name)) { + return false; + } + + throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context)); + } + } + + $name = ltrim($name,'\\'); + + if ( ! $this->classExists($name)) { + throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context)); + } + + // at this point, $name contains the fully qualified class name of the + // annotation, and it is also guaranteed that this class exists, and + // that it is loaded + + + // collects the metadata annotation only if there is not yet + if ( ! isset(self::$annotationMetadata[$name])) { + $this->collectAnnotationMetadata($name); + } + + // verify that the class is really meant to be an annotation and not just any ordinary class + if (self::$annotationMetadata[$name]['is_annotation'] === false) { + if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$originalName])) { + return false; + } + + throw AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context)); + } + + //if target is nested annotation + $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target; + + // Next will be nested + $this->isNestedAnnotation = true; + + //if annotation does not support current target + if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) { + throw AnnotationException::semanticalError( + sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.', + $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal']) + ); + } + + $values = $this->MethodCall(); + + if (isset(self::$annotationMetadata[$name]['enum'])) { + // checks all declared attributes + foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) { + // checks if the attribute is a valid enumerator + if (isset($values[$property]) && ! in_array($values[$property], $enum['value'])) { + throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]); + } + } + } + + // checks all declared attributes + foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) { + if ($property === self::$annotationMetadata[$name]['default_property'] + && !isset($values[$property]) && isset($values['value'])) { + $property = 'value'; + } + + // handle a not given attribute or null value + if (!isset($values[$property])) { + if ($type['required']) { + throw AnnotationException::requiredError($property, $originalName, $this->context, 'a(n) '.$type['value']); + } + + continue; + } + + if ($type['type'] === 'array') { + // handle the case of a single value + if ( ! is_array($values[$property])) { + $values[$property] = array($values[$property]); + } + + // checks if the attribute has array type declaration, such as "array" + if (isset($type['array_type'])) { + foreach ($values[$property] as $item) { + if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) { + throw AnnotationException::attributeTypeError($property, $originalName, $this->context, 'either a(n) '.$type['array_type'].', or an array of '.$type['array_type'].'s', $item); + } + } + } + } elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) { + throw AnnotationException::attributeTypeError($property, $originalName, $this->context, 'a(n) '.$type['value'], $values[$property]); + } + } + + // check if the annotation expects values via the constructor, + // or directly injected into public properties + if (self::$annotationMetadata[$name]['has_constructor'] === true) { + return new $name($values); + } + + $instance = new $name(); + + foreach ($values as $property => $value) { + if (!isset(self::$annotationMetadata[$name]['properties'][$property])) { + if ('value' !== $property) { + throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties']))); + } + + // handle the case if the property has no annotations + if ( ! $property = self::$annotationMetadata[$name]['default_property']) { + throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values))); + } + } + + $instance->{$property} = $value; + } + + return $instance; + } + + /** + * MethodCall ::= ["(" [Values] ")"] + * + * @return array + */ + private function MethodCall() + { + $values = array(); + + if ( ! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) { + return $values; + } + + $this->match(DocLexer::T_OPEN_PARENTHESIS); + + if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + $values = $this->Values(); + } + + $this->match(DocLexer::T_CLOSE_PARENTHESIS); + + return $values; + } + + /** + * Values ::= Array | Value {"," Value}* [","] + * + * @return array + */ + private function Values() + { + $values = array($this->Value()); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) { + break; + } + + $token = $this->lexer->lookahead; + $value = $this->Value(); + + if ( ! is_object($value) && ! is_array($value)) { + $this->syntaxError('Value', $token); + } + + $values[] = $value; + } + + foreach ($values as $k => $value) { + if (is_object($value) && $value instanceof \stdClass) { + $values[$value->name] = $value->value; + } else if ( ! isset($values['value'])){ + $values['value'] = $value; + } else { + if ( ! is_array($values['value'])) { + $values['value'] = array($values['value']); + } + + $values['value'][] = $value; + } + + unset($values[$k]); + } + + return $values; + } + + /** + * Constant ::= integer | string | float | boolean + * + * @return mixed + * + * @throws AnnotationException + */ + private function Constant() + { + $identifier = $this->Identifier(); + + if ( ! defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) { + list($className, $const) = explode('::', $identifier); + + $pos = strpos($className, '\\'); + $alias = (false === $pos) ? $className : substr($className, 0, $pos); + $found = false; + $loweredAlias = strtolower($alias); + + switch (true) { + case !empty ($this->namespaces): + foreach ($this->namespaces as $ns) { + if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) { + $className = $ns.'\\'.$className; + $found = true; + break; + } + } + break; + + case isset($this->imports[$loweredAlias]): + $found = true; + $className = (false !== $pos) + ? $this->imports[$loweredAlias] . substr($className, $pos) + : $this->imports[$loweredAlias]; + break; + + default: + if(isset($this->imports['__NAMESPACE__'])) { + $ns = $this->imports['__NAMESPACE__']; + + if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) { + $className = $ns.'\\'.$className; + $found = true; + } + } + break; + } + + if ($found) { + $identifier = $className . '::' . $const; + } + } + + // checks if identifier ends with ::class, \strlen('::class') === 7 + $classPos = stripos($identifier, '::class'); + if ($classPos === strlen($identifier) - 7) { + return substr($identifier, 0, $classPos); + } + + if (!defined($identifier)) { + throw AnnotationException::semanticalErrorConstants($identifier, $this->context); + } + + return constant($identifier); + } + + /** + * Identifier ::= string + * + * @return string + */ + private function Identifier() + { + // check if we have an annotation + if ( ! $this->lexer->isNextTokenAny(self::$classIdentifiers)) { + $this->syntaxError('namespace separator or identifier'); + } + + $this->lexer->moveNext(); + + $className = $this->lexer->token['value']; + + while ($this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value'])) + && $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) { + + $this->match(DocLexer::T_NAMESPACE_SEPARATOR); + $this->matchAny(self::$classIdentifiers); + + $className .= '\\' . $this->lexer->token['value']; + } + + return $className; + } + + /** + * Value ::= PlainValue | FieldAssignment + * + * @return mixed + */ + private function Value() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type']) { + return $this->FieldAssignment(); + } + + return $this->PlainValue(); + } + + /** + * PlainValue ::= integer | string | float | boolean | Array | Annotation + * + * @return mixed + */ + private function PlainValue() + { + if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) { + return $this->Arrayx(); + } + + if ($this->lexer->isNextToken(DocLexer::T_AT)) { + return $this->Annotation(); + } + + if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { + return $this->Constant(); + } + + switch ($this->lexer->lookahead['type']) { + case DocLexer::T_STRING: + $this->match(DocLexer::T_STRING); + return $this->lexer->token['value']; + + case DocLexer::T_INTEGER: + $this->match(DocLexer::T_INTEGER); + return (int)$this->lexer->token['value']; + + case DocLexer::T_FLOAT: + $this->match(DocLexer::T_FLOAT); + return (float)$this->lexer->token['value']; + + case DocLexer::T_TRUE: + $this->match(DocLexer::T_TRUE); + return true; + + case DocLexer::T_FALSE: + $this->match(DocLexer::T_FALSE); + return false; + + case DocLexer::T_NULL: + $this->match(DocLexer::T_NULL); + return null; + + default: + $this->syntaxError('PlainValue'); + } + } + + /** + * FieldAssignment ::= FieldName "=" PlainValue + * FieldName ::= identifier + * + * @return \stdClass + */ + private function FieldAssignment() + { + $this->match(DocLexer::T_IDENTIFIER); + $fieldName = $this->lexer->token['value']; + + $this->match(DocLexer::T_EQUALS); + + $item = new \stdClass(); + $item->name = $fieldName; + $item->value = $this->PlainValue(); + + return $item; + } + + /** + * Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}" + * + * @return array + */ + private function Arrayx() + { + $array = $values = array(); + + $this->match(DocLexer::T_OPEN_CURLY_BRACES); + + // If the array is empty, stop parsing and return. + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { + $this->match(DocLexer::T_CLOSE_CURLY_BRACES); + + return $array; + } + + $values[] = $this->ArrayEntry(); + + while ($this->lexer->isNextToken(DocLexer::T_COMMA)) { + $this->match(DocLexer::T_COMMA); + + // optional trailing comma + if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) { + break; + } + + $values[] = $this->ArrayEntry(); + } + + $this->match(DocLexer::T_CLOSE_CURLY_BRACES); + + foreach ($values as $value) { + list ($key, $val) = $value; + + if ($key !== null) { + $array[$key] = $val; + } else { + $array[] = $val; + } + } + + return $array; + } + + /** + * ArrayEntry ::= Value | KeyValuePair + * KeyValuePair ::= Key ("=" | ":") PlainValue | Constant + * Key ::= string | integer | Constant + * + * @return array + */ + private function ArrayEntry() + { + $peek = $this->lexer->glimpse(); + + if (DocLexer::T_EQUALS === $peek['type'] + || DocLexer::T_COLON === $peek['type']) { + + if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) { + $key = $this->Constant(); + } else { + $this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING)); + $key = $this->lexer->token['value']; + } + + $this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON)); + + return array($key, $this->PlainValue()); + } + + return array(null, $this->Value()); + } + + /** + * Checks whether the given $name matches any ignored annotation name or namespace + * + * @param string $name + * + * @return bool + */ + private function isIgnoredAnnotation($name) + { + if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) { + return true; + } + + foreach (array_keys($this->ignoredAnnotationNamespaces) as $ignoredAnnotationNamespace) { + $ignoredAnnotationNamespace = rtrim($ignoredAnnotationNamespace, '\\') . '\\'; + + if (0 === stripos(rtrim($name, '\\') . '\\', $ignoredAnnotationNamespace)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php new file mode 100644 index 0000000..fd2fede --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/FileCacheReader.php @@ -0,0 +1,290 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * File cache reader for annotations. + * + * @author Johannes M. Schmitt + * @author Benjamin Eberlei + * + * @deprecated the FileCacheReader is deprecated and will be removed + * in version 2.0.0 of doctrine/annotations. Please use the + * {@see \Doctrine\Common\Annotations\CachedReader} instead. + */ +class FileCacheReader implements Reader +{ + /** + * @var Reader + */ + private $reader; + + /** + * @var string + */ + private $dir; + + /** + * @var bool + */ + private $debug; + + /** + * @var array + */ + private $loadedAnnotations = array(); + + /** + * @var array + */ + private $classNameHashes = array(); + + /** + * @var int + */ + private $umask; + + /** + * Constructor. + * + * @param Reader $reader + * @param string $cacheDir + * @param boolean $debug + * + * @throws \InvalidArgumentException + */ + public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002) + { + if ( ! is_int($umask)) { + throw new \InvalidArgumentException(sprintf( + 'The parameter umask must be an integer, was: %s', + gettype($umask) + )); + } + + $this->reader = $reader; + $this->umask = $umask; + + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777 & (~$this->umask), true)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir)); + } + + $this->dir = rtrim($cacheDir, '\\/'); + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name]; + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($path)) { + $annot = $this->reader->getClassAnnotations($class); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFileName()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getClassAnnotations($class); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $class = $property->getDeclaringClass(); + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name].'$'.$property->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($path)) { + $annot = $this->reader->getPropertyAnnotations($property); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getPropertyAnnotations($property); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $class = $method->getDeclaringClass(); + if ( ! isset($this->classNameHashes[$class->name])) { + $this->classNameHashes[$class->name] = sha1($class->name); + } + $key = $this->classNameHashes[$class->name].'#'.$method->getName(); + + if (isset($this->loadedAnnotations[$key])) { + return $this->loadedAnnotations[$key]; + } + + $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php'; + if (!is_file($path)) { + $annot = $this->reader->getMethodAnnotations($method); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + if ($this->debug + && (false !== $filename = $class->getFilename()) + && filemtime($path) < filemtime($filename)) { + @unlink($path); + + $annot = $this->reader->getMethodAnnotations($method); + $this->saveCacheFile($path, $annot); + return $this->loadedAnnotations[$key] = $annot; + } + + return $this->loadedAnnotations[$key] = include $path; + } + + /** + * Saves the cache file. + * + * @param string $path + * @param mixed $data + * + * @return void + */ + private function saveCacheFile($path, $data) + { + if (!is_writable($this->dir)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $this->dir)); + } + + $tempfile = tempnam($this->dir, uniqid('', true)); + + if (false === $tempfile) { + throw new \RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir)); + } + + @chmod($tempfile, 0666 & (~$this->umask)); + + $written = file_put_contents($tempfile, 'umask)); + + if (false === rename($tempfile, $path)) { + @unlink($tempfile); + throw new \RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path)); + } + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + $annotations = $this->getClassAnnotations($class); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + $annotations = $this->getMethodAnnotations($method); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + $annotations = $this->getPropertyAnnotations($property); + + foreach ($annotations as $annotation) { + if ($annotation instanceof $annotationName) { + return $annotation; + } + } + + return null; + } + + /** + * Clears loaded annotations. + * + * @return void + */ + public function clearLoadedAnnotations() + { + $this->loadedAnnotations = array(); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php new file mode 100644 index 0000000..bf7fbdc --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/IndexedReader.php @@ -0,0 +1,119 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Allows the reader to be used in-place of Doctrine's reader. + * + * @author Johannes M. Schmitt + */ +class IndexedReader implements Reader +{ + /** + * @var Reader + */ + private $delegate; + + /** + * Constructor. + * + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->delegate = $reader; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + $annotations = array(); + foreach ($this->delegate->getClassAnnotations($class) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotation) + { + return $this->delegate->getClassAnnotation($class, $annotation); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + $annotations = array(); + foreach ($this->delegate->getMethodAnnotations($method) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotation) + { + return $this->delegate->getMethodAnnotation($method, $annotation); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + $annotations = array(); + foreach ($this->delegate->getPropertyAnnotations($property) as $annot) { + $annotations[get_class($annot)] = $annot; + } + + return $annotations; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotation) + { + return $this->delegate->getPropertyAnnotation($property, $annotation); + } + + /** + * Proxies all methods to the delegate. + * + * @param string $method + * @param array $args + * + * @return mixed + */ + public function __call($method, $args) + { + return call_user_func_array(array($this->delegate, $method), $args); + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php new file mode 100644 index 0000000..c2d4770 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/PhpParser.php @@ -0,0 +1,91 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +use SplFileObject; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +final class PhpParser +{ + /** + * Parses a class. + * + * @param \ReflectionClass $class A ReflectionClass object. + * + * @return array A list with use statements in the form (Alias => FQN). + */ + public function parseClass(\ReflectionClass $class) + { + if (method_exists($class, 'getUseStatements')) { + return $class->getUseStatements(); + } + + if (false === $filename = $class->getFileName()) { + return array(); + } + + $content = $this->getFileContent($filename, $class->getStartLine()); + + if (null === $content) { + return array(); + } + + $namespace = preg_quote($class->getNamespaceName()); + $content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content); + $tokenizer = new TokenParser('parseUseStatements($class->getNamespaceName()); + + return $statements; + } + + /** + * Gets the content of the file right up to the given line number. + * + * @param string $filename The name of the file to load. + * @param integer $lineNumber The number of lines to read from file. + * + * @return string|null The content of the file or null if the file does not exist. + */ + private function getFileContent($filename, $lineNumber) + { + if ( ! is_file($filename)) { + return null; + } + + $content = ''; + $lineCnt = 0; + $file = new SplFileObject($filename); + while (!$file->eof()) { + if ($lineCnt++ == $lineNumber) { + break; + } + + $content .= $file->fgets(); + } + + return $content; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php new file mode 100644 index 0000000..4774f87 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/Reader.php @@ -0,0 +1,89 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Interface for annotation readers. + * + * @author Johannes M. Schmitt + */ +interface Reader +{ + /** + * Gets the annotations applied to a class. + * + * @param \ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * + * @return array An array of Annotations. + */ + function getClassAnnotations(\ReflectionClass $class); + + /** + * Gets a class annotation. + * + * @param \ReflectionClass $class The ReflectionClass of the class from which + * the class annotations should be read. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getClassAnnotation(\ReflectionClass $class, $annotationName); + + /** + * Gets the annotations applied to a method. + * + * @param \ReflectionMethod $method The ReflectionMethod of the method from which + * the annotations should be read. + * + * @return array An array of Annotations. + */ + function getMethodAnnotations(\ReflectionMethod $method); + + /** + * Gets a method annotation. + * + * @param \ReflectionMethod $method The ReflectionMethod to read the annotations from. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getMethodAnnotation(\ReflectionMethod $method, $annotationName); + + /** + * Gets the annotations applied to a property. + * + * @param \ReflectionProperty $property The ReflectionProperty of the property + * from which the annotations should be read. + * + * @return array An array of Annotations. + */ + function getPropertyAnnotations(\ReflectionProperty $property); + + /** + * Gets a property annotation. + * + * @param \ReflectionProperty $property The ReflectionProperty to read the annotations from. + * @param string $annotationName The name of the annotation. + * + * @return object|null The Annotation or NULL, if the requested annotation does not exist. + */ + function getPropertyAnnotation(\ReflectionProperty $property, $annotationName); +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php new file mode 100644 index 0000000..d4757ee --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/SimpleAnnotationReader.php @@ -0,0 +1,127 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Simple Annotation Reader. + * + * This annotation reader is intended to be used in projects where you have + * full-control over all annotations that are available. + * + * @since 2.2 + * @author Johannes M. Schmitt + * @author Fabio B. Silva + */ +class SimpleAnnotationReader implements Reader +{ + /** + * @var DocParser + */ + private $parser; + + /** + * Constructor. + * + * Initializes a new SimpleAnnotationReader. + */ + public function __construct() + { + $this->parser = new DocParser(); + $this->parser->setIgnoreNotImportedAnnotations(true); + } + + /** + * Adds a namespace in which we will look for annotations. + * + * @param string $namespace + * + * @return void + */ + public function addNamespace($namespace) + { + $this->parser->addNamespace($namespace); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotations(\ReflectionClass $class) + { + return $this->parser->parse($class->getDocComment(), 'class '.$class->getName()); + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotations(\ReflectionMethod $method) + { + return $this->parser->parse($method->getDocComment(), 'method '.$method->getDeclaringClass()->name.'::'.$method->getName().'()'); + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotations(\ReflectionProperty $property) + { + return $this->parser->parse($property->getDocComment(), 'property '.$property->getDeclaringClass()->name.'::$'.$property->getName()); + } + + /** + * {@inheritDoc} + */ + public function getClassAnnotation(\ReflectionClass $class, $annotationName) + { + foreach ($this->getClassAnnotations($class) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getMethodAnnotation(\ReflectionMethod $method, $annotationName) + { + foreach ($this->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) + { + foreach ($this->getPropertyAnnotations($property) as $annot) { + if ($annot instanceof $annotationName) { + return $annot; + } + } + + return null; + } +} diff --git a/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php new file mode 100644 index 0000000..bf1b713 --- /dev/null +++ b/vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/TokenParser.php @@ -0,0 +1,194 @@ +. + */ + +namespace Doctrine\Common\Annotations; + +/** + * Parses a file for namespaces/use/class declarations. + * + * @author Fabien Potencier + * @author Christian Kaps + */ +class TokenParser +{ + /** + * The token list. + * + * @var array + */ + private $tokens; + + /** + * The number of tokens. + * + * @var int + */ + private $numTokens; + + /** + * The current array pointer. + * + * @var int + */ + private $pointer = 0; + + /** + * @param string $contents + */ + public function __construct($contents) + { + $this->tokens = token_get_all($contents); + + // The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it + // saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored + // doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a + // docblock. If the first thing in the file is a class without a doc block this would cause calls to + // getDocBlock() on said class to return our long lost doc_comment. Argh. + // To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least + // it's harmless to us. + token_get_all("numTokens = count($this->tokens); + } + + /** + * Gets the next non whitespace and non comment token. + * + * @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped. + * If FALSE then only whitespace and normal comments are skipped. + * + * @return array|null The token if exists, null otherwise. + */ + public function next($docCommentIsComment = TRUE) + { + for ($i = $this->pointer; $i < $this->numTokens; $i++) { + $this->pointer++; + if ($this->tokens[$i][0] === T_WHITESPACE || + $this->tokens[$i][0] === T_COMMENT || + ($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) { + + continue; + } + + return $this->tokens[$i]; + } + + return null; + } + + /** + * Parses a single use statement. + * + * @return array A list with all found class names for a use statement. + */ + public function parseUseStatement() + { + + $groupRoot = ''; + $class = ''; + $alias = ''; + $statements = array(); + $explicitAlias = false; + while (($token = $this->next())) { + $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR; + if (!$explicitAlias && $isNameToken) { + $class .= $token[1]; + $alias = $token[1]; + } else if ($explicitAlias && $isNameToken) { + $alias .= $token[1]; + } else if ($token[0] === T_AS) { + $explicitAlias = true; + $alias = ''; + } else if ($token === ',') { + $statements[strtolower($alias)] = $groupRoot . $class; + $class = ''; + $alias = ''; + $explicitAlias = false; + } else if ($token === ';') { + $statements[strtolower($alias)] = $groupRoot . $class; + break; + } else if ($token === '{' ) { + $groupRoot = $class; + $class = ''; + } else if ($token === '}' ) { + continue; + } else { + break; + } + } + + return $statements; + } + + /** + * Gets all use statements. + * + * @param string $namespaceName The namespace name of the reflected class. + * + * @return array A list with all found use statements. + */ + public function parseUseStatements($namespaceName) + { + $statements = array(); + while (($token = $this->next())) { + if ($token[0] === T_USE) { + $statements = array_merge($statements, $this->parseUseStatement()); + continue; + } + if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) { + continue; + } + + // Get fresh array for new namespace. This is to prevent the parser to collect the use statements + // for a previous namespace with the same name. This is the case if a namespace is defined twice + // or if a namespace with the same name is commented out. + $statements = array(); + } + + return $statements; + } + + /** + * Gets the namespace. + * + * @return string The found namespace. + */ + public function parseNamespace() + { + $name = ''; + while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) { + $name .= $token[1]; + } + + return $name; + } + + /** + * Gets the class name. + * + * @return string The found class name. + */ + public function parseClass() + { + // Namespaces and class names are tokenized the same: T_STRINGs + // separated by T_NS_SEPARATOR so we can use one function to provide + // both. + return $this->parseNamespace(); + } +} diff --git a/vendor/doctrine/annotations/phpstan.neon b/vendor/doctrine/annotations/phpstan.neon new file mode 100644 index 0000000..be267e6 --- /dev/null +++ b/vendor/doctrine/annotations/phpstan.neon @@ -0,0 +1,17 @@ +parameters: + autoload_files: + - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/DocParserTest.php + excludes_analyse: + - %currentWorkingDirectory%/tests/*/Fixtures/* + - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/ReservedKeywordsClasses.php + - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Annotations/Ticket/DCOM58Entity.php + - %currentWorkingDirectory%/tests/Doctrine/Tests/DoctrineTestCase.php + polluteScopeWithLoopInitialAssignments: true + ignoreErrors: + - '#Class Doctrine_Tests_Common_Annotations_Fixtures_ClassNoNamespaceNoComment not found#' + - '#Instantiated class Doctrine_Tests_Common_Annotations_Fixtures_ClassNoNamespaceNoComment not found#' + - '#Property Doctrine\\Tests\\Common\\Annotations\\DummyClassNonAnnotationProblem::\$foo has unknown class#' + - '#Class Doctrine\\Tests\\Common\\Annotations\\True not found#' + - '#Class Doctrine\\Tests\\Common\\Annotations\\False not found#' + - '#Class Doctrine\\Tests\\Common\\Annotations\\Null not found#' + - '#Call to an undefined method ReflectionClass::getUseStatements\(\)#' diff --git a/vendor/doctrine/cache/LICENSE b/vendor/doctrine/cache/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/cache/README.md b/vendor/doctrine/cache/README.md new file mode 100644 index 0000000..9d35e93 --- /dev/null +++ b/vendor/doctrine/cache/README.md @@ -0,0 +1,10 @@ +# Doctrine Cache + +[![Build Status](https://img.shields.io/travis/doctrine/cache/master.svg?style=flat-square)](http://travis-ci.org/doctrine/cache) +[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/doctrine/cache/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/doctrine/cache/?branch=master) +[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/doctrine/cache/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/doctrine/cache/?branch=master) + +[![Latest Stable Version](https://img.shields.io/packagist/v/doctrine/cache.svg?style=flat-square)](https://packagist.org/packages/doctrine/cache) +[![Total Downloads](https://img.shields.io/packagist/dt/doctrine/cache.svg?style=flat-square)](https://packagist.org/packages/doctrine/cache) + +Cache component extracted from the Doctrine Common project. [Documentation](http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/caching.html) diff --git a/vendor/doctrine/cache/UPGRADE.md b/vendor/doctrine/cache/UPGRADE.md new file mode 100644 index 0000000..e1f8a50 --- /dev/null +++ b/vendor/doctrine/cache/UPGRADE.md @@ -0,0 +1,16 @@ +# Upgrade to 1.4 + +## Minor BC Break: `Doctrine\Common\Cache\FileCache#$extension` is now `private`. + +If you need to override the value of `Doctrine\Common\Cache\FileCache#$extension`, then use the +second parameter of `Doctrine\Common\Cache\FileCache#__construct()` instead of overriding +the property in your own implementation. + +## Minor BC Break: file based caches paths changed + +`Doctrine\Common\Cache\FileCache`, `Doctrine\Common\Cache\PhpFileCache` and +`Doctrine\Common\Cache\FilesystemCache` are using a different cache paths structure. + +If you rely on warmed up caches for deployments, consider that caches generated +with `doctrine/cache` `<1.4` are not compatible with the new directory structure, +and will be ignored. diff --git a/vendor/doctrine/cache/composer.json b/vendor/doctrine/cache/composer.json new file mode 100644 index 0000000..2ee5d25 --- /dev/null +++ b/vendor/doctrine/cache/composer.json @@ -0,0 +1,42 @@ +{ + "name": "doctrine/cache", + "type": "library", + "description": "Caching library offering an object-oriented API for many cache backends", + "keywords": ["cache", "caching"], + "homepage": "https://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": "~7.1" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "^1.1", + "mongodb/mongodb": "^1.1", + "phpunit/phpunit": "^7.0", + "predis/predis": "~1.0", + "doctrine/coding-standard": "^4.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "autoload": { + "psr-4": { "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" } + }, + "autoload-dev": { + "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.8.x-dev" + } + } +} diff --git a/vendor/doctrine/cache/docs/en/index.rst b/vendor/doctrine/cache/docs/en/index.rst new file mode 100644 index 0000000..f08ab41 --- /dev/null +++ b/vendor/doctrine/cache/docs/en/index.rst @@ -0,0 +1,274 @@ +Introduction +============ + +Doctrine Cache is a library that provides an interface for caching data. +It comes with implementations for some of the most popular caching data +stores. Here is what the ``Cache`` interface looks like. + +.. code-block:: php + namespace Doctrine\Common\Cache; + + interface Cache + { + public function fetch($id); + public function contains($id); + public function save($id, $data, $lifeTime = 0); + public function delete($id); + public function getStats(); + } + +Here is an example that uses Memcache. + +.. code-block:: php + use Doctrine\Common\Cache\MemcacheCache; + + $memcache = new Memcache(); + $cache = new MemcacheCache(); + $cache->setMemcache($memcache); + + $cache->set('key', 'value'); + + echo $cache->get('key') // prints "value" + +Drivers +======= + +Doctrine ships with several common drivers that you can easily use. +Below you can find information about all the available drivers. + +ApcCache +-------- + +The ``ApcCache`` driver uses the ``apc_fetch``, ``apc_exists``, etc. functions that come +with PHP so no additional setup is required in order to use it. + +.. code-block:: php + $cache = new ApcCache(); + +ApcuCache +--------- + +The ``ApcuCache`` driver uses the ``apcu_fetch``, ``apcu_exists``, etc. functions that come +with PHP so no additional setup is required in order to use it. + +.. code-block:: php + $cache = new ApcuCache(); + +ArrayCache +---------- + +The ``ArrayCache`` driver stores the cache data in PHPs memory and is not persisted anywhere. +This can be useful for caching things in memory for a single process when you don't need +the cache to be persistent across processes. + +.. code-block:: php + $cache = new ArrayCache(); + +ChainCache +---------- + +The ``ChainCache`` driver lets you chain multiple other drivers together easily. + +.. code-block:: php + $arrayCache = new ArrayCache(); + $apcuCache = new ApcuCache(); + + $cache = new ChainCache([$arrayCache, $apcuCache]); + +CouchbaseBucketCache +-------------------- + +The ``CouchbaseBucketCache`` driver uses Couchbase to store the cache data. + +.. code-block:: php + $bucketName = 'bucket-name'; + + $authenticator = new Couchbase\PasswordAuthenticator(); + $authenticator->username('username')->password('password'); + + $cluster = new CouchbaseCluster('couchbase://127.0.0.1'); + + $cluster->authenticate($authenticator); + $bucket = $cluster->openBucket($bucketName); + + $cache = new CouchbaseBucketCache($bucket); + +FilesystemCache +--------------- + +The ``FilesystemCache`` driver stores the cache data on the local filesystem. + +.. code-block:: php + $cache = new FilesystemCache('/path/to/cache/directory'); + +MemecacheCache +-------------- + +The ``MemcacheCache`` drivers stores the cache data in Memcache. + +.. code-block:: php + $memcache = new Memcache(); + $memcache->connect('localhost', 11211); + + $cache = new MemcacheCache(); + $cache->setMemcache($memcache); + +MemcachedCache +-------------- + +The ``MemcachedCache`` drivers stores the cache data in Memcached. + +.. code-block:: php + $memcached = new Memcached(); + + $cache = new MemcachedCache(); + $cache->setMemcached($memcached); + +MongoDBCache +------------ + +The ``MongoDBCache`` drivers stores the cache data in a MongoDB collection. + +.. code-block:: php + $manager = new MongoDB\Driver\Manager("mongodb://localhost:27017"); + + $collection = new MongoDB\Collection($manager, 'database_name', 'collection_name'); + + $cache = new MongoDBCache($collection); + +PhpFileCache +------------ + +The ``PhpFileCache`` driver stores the cache data on the local filesystem like the +``FilesystemCache`` driver except the data is serialized using the ``serialize()`` +and ``unserialize()`` functions available in PHP. The files are included so this means +that the data can be cached in PHPs opcache. + +.. code-block:: php + $cache = new PhpFileCache('/path/to/cache/directory'); + +PredisCache +----------- + +The ``PredisCache`` driver stores the cache data in Redis +and depends on the ``predis/predis`` package which can be installed with composer. + +.. code-block:: bash + $ composer require predis/predis + +Then you can use the ``Predis\Client`` class to pass to the ``PredisCache`` class. + +.. code-block:: php + $client = new Predis\Client(); + + $cache = new PredisCache($client); + +RedisCache +---------- + +The ``RedisCache`` driver stores the cache data in Redis and depends on the +``phpredis`` extension which can be found `here `_. + +.. code-block:: php + $redis = new Redis(); + + $cache = new RedisCache($redis); + +RiakCache +--------- + +The ``RiakCache`` driver stores the cache data in Riak and depends on the +``riak`` extension which can be found `here `_. + +.. code-block:: php + $connection = new Riak\Connection('localhost', 8087); + + $bucket = new Riak\Bucket($connection, 'bucket_name'); + + $cache = new RiakCache($bucket); + +SQLite3Cache +------------ + +The ``SQLite3Cache`` driver stores the cache data in a SQLite database and depends on the +``sqlite3`` extension which can be found `here `_. + +.. code-block:: php + $db = new SQLite3('mydatabase.db'); + + $cache = new SQLite3Cache($db, 'table_name'); + +VoidCache +--------- + +The ``VoidCache`` driver does not store the cache data anywhere. This can +be useful for test environments where you don't want to cache the data +anywhere but need to satisfy the dependency for the ``Doctrine\Common\Cache\Cache`` +interface. + +.. code-block:: php + $cache = new VoidCache(); + +WinCacheCache +------------- + +The ``WinCacheCache`` driver uses the ``wincache_ucache_get``, ``wincache_ucache_exists``, etc. functions that come +with the ``wincache`` extension which can be found `here `_. + +.. code-block:: php + $cache = new WinCacheCache(); + +XcacheCache +----------- + +The ``XcacheCache`` driver uses functions that come with the ``xcache`` +extension which can be found `here `_. + +.. code-block:: php + $cache = new XcacheCache(); + +ZendDataCache +------------- + +The ``ZendDataCache`` driver uses the Zend Data Cache API available in the Zend Platform. + +.. code-block:: php + $cache = new ZendDataCache(); + +Custom Drivers +============== + +If you want to implement your own cache driver, you just need to implement +the ``Doctrine\Common\Cache\Cache`` interface. Here is an example implementation +skeleton. + +.. code-block:: php + use Doctrine\Common\Cache\Cache; + + class MyCacheDriver implements Cache + { + public function fetch($id) + { + // fetch $id from the cache + } + + public function contains($id) + { + // check if $id exists in the cache + } + + public function save($id, $data, $lifeTime = 0) + { + // save $data under $id in the cache for $lifetime + } + + public function delete($id) + { + // delete $id from the cache + } + + public function getStats() + { + // get cache stats + } + } diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php new file mode 100644 index 0000000..451865e --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcCache.php @@ -0,0 +1,104 @@ += 50500) { + $info['num_hits'] = $info['num_hits'] ?? $info['nhits']; + $info['num_misses'] = $info['num_misses'] ?? $info['nmisses']; + $info['start_time'] = $info['start_time'] ?? $info['stime']; + } + + return [ + Cache::STATS_HITS => $info['num_hits'], + Cache::STATS_MISSES => $info['num_misses'], + Cache::STATS_UPTIME => $info['start_time'], + Cache::STATS_MEMORY_USAGE => $info['mem_size'], + Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php new file mode 100644 index 0000000..a725213 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ApcuCache.php @@ -0,0 +1,106 @@ + $info['num_hits'], + Cache::STATS_MISSES => $info['num_misses'], + Cache::STATS_UPTIME => $info['start_time'], + Cache::STATS_MEMORY_USAGE => $info['mem_size'], + Cache::STATS_MEMORY_AVAILABLE => $sma['avail_mem'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php new file mode 100644 index 0000000..1beb709 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ArrayCache.php @@ -0,0 +1,113 @@ +upTime = time(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + if (! $this->doContains($id)) { + $this->missesCount += 1; + + return false; + } + + $this->hitsCount += 1; + + return $this->data[$id][0]; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + if (! isset($this->data[$id])) { + return false; + } + + $expiration = $this->data[$id][1]; + + if ($expiration && $expiration < time()) { + $this->doDelete($id); + + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = [$data, $lifeTime ? time() + $lifeTime : false]; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->data = []; + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return [ + Cache::STATS_HITS => $this->hitsCount, + Cache::STATS_MISSES => $this->missesCount, + Cache::STATS_UPTIME => $this->upTime, + Cache::STATS_MEMORY_USAGE => null, + Cache::STATS_MEMORY_AVAILABLE => null, + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php new file mode 100644 index 0000000..4569744 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Cache.php @@ -0,0 +1,90 @@ +hits + * Number of keys that have been requested and found present. + * + * - misses + * Number of items that have been requested and not found. + * + * - uptime + * Time that the server is running. + * + * - memory_usage + * Memory used by this server to store items. + * + * - memory_available + * Memory allowed to use for storage. + * + * @return array|null An associative array with server's statistics if available, NULL otherwise. + */ + public function getStats(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php new file mode 100644 index 0000000..3be5454 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php @@ -0,0 +1,325 @@ +namespace = (string) $namespace; + $this->namespaceVersion = null; + } + + /** + * Retrieves the namespace that prefixes all cache ids. + * + * @return string + */ + public function getNamespace() + { + return $this->namespace; + } + + /** + * {@inheritdoc} + */ + public function fetch($id) + { + return $this->doFetch($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function fetchMultiple(array $keys) + { + if (empty($keys)) { + return []; + } + + // note: the array_combine() is in place to keep an association between our $keys and the $namespacedKeys + $namespacedKeys = array_combine($keys, array_map([$this, 'getNamespacedId'], $keys)); + $items = $this->doFetchMultiple($namespacedKeys); + $foundItems = []; + + // no internal array function supports this sort of mapping: needs to be iterative + // this filters and combines keys in one pass + foreach ($namespacedKeys as $requestedKey => $namespacedKey) { + if (! isset($items[$namespacedKey]) && ! array_key_exists($namespacedKey, $items)) { + continue; + } + + $foundItems[$requestedKey] = $items[$namespacedKey]; + } + + return $foundItems; + } + + /** + * {@inheritdoc} + */ + public function saveMultiple(array $keysAndValues, $lifetime = 0) + { + $namespacedKeysAndValues = []; + foreach ($keysAndValues as $key => $value) { + $namespacedKeysAndValues[$this->getNamespacedId($key)] = $value; + } + + return $this->doSaveMultiple($namespacedKeysAndValues, $lifetime); + } + + /** + * {@inheritdoc} + */ + public function contains($id) + { + return $this->doContains($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function save($id, $data, $lifeTime = 0) + { + return $this->doSave($this->getNamespacedId($id), $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple(array $keys) + { + return $this->doDeleteMultiple(array_map([$this, 'getNamespacedId'], $keys)); + } + + /** + * {@inheritdoc} + */ + public function delete($id) + { + return $this->doDelete($this->getNamespacedId($id)); + } + + /** + * {@inheritdoc} + */ + public function getStats() + { + return $this->doGetStats(); + } + + /** + * {@inheritDoc} + */ + public function flushAll() + { + return $this->doFlush(); + } + + /** + * {@inheritDoc} + */ + public function deleteAll() + { + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $namespaceVersion = $this->getNamespaceVersion() + 1; + + if ($this->doSave($namespaceCacheKey, $namespaceVersion)) { + $this->namespaceVersion = $namespaceVersion; + + return true; + } + + return false; + } + + /** + * Prefixes the passed id with the configured namespace value. + * + * @param string $id The id to namespace. + * + * @return string The namespaced id. + */ + private function getNamespacedId(string $id) : string + { + $namespaceVersion = $this->getNamespaceVersion(); + + return sprintf('%s[%s][%s]', $this->namespace, $id, $namespaceVersion); + } + + /** + * Returns the namespace cache key. + */ + private function getNamespaceCacheKey() : string + { + return sprintf(self::DOCTRINE_NAMESPACE_CACHEKEY, $this->namespace); + } + + /** + * Returns the namespace version. + */ + private function getNamespaceVersion() : int + { + if ($this->namespaceVersion !== null) { + return $this->namespaceVersion; + } + + $namespaceCacheKey = $this->getNamespaceCacheKey(); + $this->namespaceVersion = (int) $this->doFetch($namespaceCacheKey) ?: 1; + + return $this->namespaceVersion; + } + + /** + * Default implementation of doFetchMultiple. Each driver that supports multi-get should owerwrite it. + * + * @param array $keys Array of keys to retrieve from cache + * @return array Array of values retrieved for the given keys. + */ + protected function doFetchMultiple(array $keys) + { + $returnValues = []; + + foreach ($keys as $key) { + $item = $this->doFetch($key); + if ($item === false && ! $this->doContains($key)) { + continue; + } + + $returnValues[$key] = $item; + } + + return $returnValues; + } + + /** + * Fetches an entry from the cache. + * + * @param string $id The id of the cache entry to fetch. + * + * @return mixed|false The cached data or FALSE, if no cache entry exists for the given id. + */ + abstract protected function doFetch($id); + + /** + * Tests if an entry exists in the cache. + * + * @param string $id The cache id of the entry to check for. + * + * @return bool TRUE if a cache entry exists for the given cache id, FALSE otherwise. + */ + abstract protected function doContains($id); + + /** + * Default implementation of doSaveMultiple. Each driver that supports multi-put should override it. + * + * @param array $keysAndValues Array of keys and values to save in cache + * @param int $lifetime The lifetime. If != 0, sets a specific lifetime for these + * cache entries (0 => infinite lifeTime). + * + * @return bool TRUE if the operation was successful, FALSE if it wasn't. + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + $success = true; + + foreach ($keysAndValues as $key => $value) { + if ($this->doSave($key, $value, $lifetime)) { + continue; + } + + $success = false; + } + + return $success; + } + + /** + * Puts data into the cache. + * + * @param string $id The cache id. + * @param string $data The cache entry/data. + * @param int $lifeTime The lifetime. If != 0, sets a specific lifetime for this + * cache entry (0 => infinite lifeTime). + * + * @return bool TRUE if the entry was successfully stored in the cache, FALSE otherwise. + */ + abstract protected function doSave($id, $data, $lifeTime = 0); + + /** + * Default implementation of doDeleteMultiple. Each driver that supports multi-delete should override it. + * + * @param array $keys Array of keys to delete from cache + * + * @return bool TRUE if the operation was successful, FALSE if it wasn't + */ + protected function doDeleteMultiple(array $keys) + { + $success = true; + + foreach ($keys as $key) { + if ($this->doDelete($key)) { + continue; + } + + $success = false; + } + + return $success; + } + + /** + * Deletes a cache entry. + * + * @param string $id The cache id. + * + * @return bool TRUE if the cache entry was successfully deleted, FALSE otherwise. + */ + abstract protected function doDelete($id); + + /** + * Flushes all cache entries. + * + * @return bool TRUE if the cache entries were successfully flushed, FALSE otherwise. + */ + abstract protected function doFlush(); + + /** + * Retrieves cached information from the data store. + * + * @return array|null An associative array with server's statistics if available, NULL otherwise. + */ + abstract protected function doGetStats(); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php new file mode 100644 index 0000000..1307fae --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ChainCache.php @@ -0,0 +1,186 @@ +cacheProviders = $cacheProviders instanceof \Traversable + ? iterator_to_array($cacheProviders, false) + : array_values($cacheProviders); + } + + /** + * {@inheritDoc} + */ + public function setNamespace($namespace) + { + parent::setNamespace($namespace); + + foreach ($this->cacheProviders as $cacheProvider) { + $cacheProvider->setNamespace($namespace); + } + } + + /** + * {@inheritDoc} + */ + protected function doFetch($id) + { + foreach ($this->cacheProviders as $key => $cacheProvider) { + if ($cacheProvider->doContains($id)) { + $value = $cacheProvider->doFetch($id); + + // We populate all the previous cache layers (that are assumed to be faster) + for ($subKey = $key - 1; $subKey >= 0; $subKey--) { + $this->cacheProviders[$subKey]->doSave($id, $value); + } + + return $value; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + /** @var CacheProvider[] $traversedProviders */ + $traversedProviders = []; + $keysCount = count($keys); + $fetchedValues = []; + + foreach ($this->cacheProviders as $key => $cacheProvider) { + $fetchedValues = $cacheProvider->doFetchMultiple($keys); + + // We populate all the previous cache layers (that are assumed to be faster) + if (count($fetchedValues) === $keysCount) { + foreach ($traversedProviders as $previousCacheProvider) { + $previousCacheProvider->doSaveMultiple($fetchedValues); + } + + return $fetchedValues; + } + + $traversedProviders[] = $cacheProvider; + } + + return $fetchedValues; + } + + /** + * {@inheritDoc} + */ + protected function doContains($id) + { + foreach ($this->cacheProviders as $cacheProvider) { + if ($cacheProvider->doContains($id)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $stored = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $stored = $cacheProvider->doSave($id, $data, $lifeTime) && $stored; + } + + return $stored; + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + $stored = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $stored = $cacheProvider->doSaveMultiple($keysAndValues, $lifetime) && $stored; + } + + return $stored; + } + + /** + * {@inheritDoc} + */ + protected function doDelete($id) + { + $deleted = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $deleted = $cacheProvider->doDelete($id) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + protected function doDeleteMultiple(array $keys) + { + $deleted = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $deleted = $cacheProvider->doDeleteMultiple($keys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritDoc} + */ + protected function doFlush() + { + $flushed = true; + + foreach ($this->cacheProviders as $cacheProvider) { + $flushed = $cacheProvider->doFlush() && $flushed; + } + + return $flushed; + } + + /** + * {@inheritDoc} + */ + protected function doGetStats() + { + // We return all the stats from all adapters + $stats = []; + + foreach ($this->cacheProviders as $cacheProvider) { + $stats[] = $cacheProvider->doGetStats(); + } + + return $stats; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php new file mode 100644 index 0000000..b94618e --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ClearableCache.php @@ -0,0 +1,21 @@ +bucket = $bucket; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $id = $this->normalizeKey($id); + + try { + $document = $this->bucket->get($id); + } catch (Exception $e) { + return false; + } + + if ($document instanceof Document && $document->value !== false) { + return unserialize($document->value); + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $id = $this->normalizeKey($id); + + try { + $document = $this->bucket->get($id); + } catch (Exception $e) { + return false; + } + + if ($document instanceof Document) { + return ! $document->error; + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $id = $this->normalizeKey($id); + + $lifeTime = $this->normalizeExpiry($lifeTime); + + try { + $encoded = serialize($data); + + $document = $this->bucket->upsert($id, $encoded, [ + 'expiry' => (int) $lifeTime, + ]); + } catch (Exception $e) { + return false; + } + + if ($document instanceof Document) { + return ! $document->error; + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $id = $this->normalizeKey($id); + + try { + $document = $this->bucket->remove($id); + } catch (Exception $e) { + return $e->getCode() === self::KEY_NOT_FOUND; + } + + if ($document instanceof Document) { + return ! $document->error; + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $manager = $this->bucket->manager(); + + // Flush does not return with success or failure, and must be enabled per bucket on the server. + // Store a marker item so that we will know if it was successful. + $this->doSave(__METHOD__, true, 60); + + $manager->flush(); + + if ($this->doContains(__METHOD__)) { + $this->doDelete(__METHOD__); + + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $manager = $this->bucket->manager(); + $stats = $manager->info(); + $nodes = $stats['nodes']; + $node = $nodes[0]; + $interestingStats = $node['interestingStats']; + + return [ + Cache::STATS_HITS => $interestingStats['get_hits'], + Cache::STATS_MISSES => $interestingStats['cmd_get'] - $interestingStats['get_hits'], + Cache::STATS_UPTIME => $node['uptime'], + Cache::STATS_MEMORY_USAGE => $interestingStats['mem_used'], + Cache::STATS_MEMORY_AVAILABLE => $node['memoryFree'], + ]; + } + + private function normalizeKey(string $id) : string + { + $normalized = substr($id, 0, self::MAX_KEY_LENGTH); + + if ($normalized === false) { + return $id; + } + + return $normalized; + } + + /** + * Expiry treated as a unix timestamp instead of an offset if expiry is greater than 30 days. + * @src https://developer.couchbase.com/documentation/server/4.1/developer-guide/expiry.html + */ + private function normalizeExpiry(int $expiry) : int + { + if ($expiry > self::THIRTY_DAYS_IN_SECONDS) { + return time() + $expiry; + } + + return $expiry; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php new file mode 100644 index 0000000..4c2b9bd --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CouchbaseCache.php @@ -0,0 +1,102 @@ +couchbase = $couchbase; + } + + /** + * Gets the Couchbase instance used by the cache. + * + * @return Couchbase|null + */ + public function getCouchbase() + { + return $this->couchbase; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->couchbase->get($id) ?: false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->couchbase->get($id) !== null; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->couchbase->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->couchbase->delete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->couchbase->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->couchbase->getStats(); + $servers = $this->couchbase->getServers(); + $server = explode(':', $servers[0]); + $key = $server[0] . ':11210'; + $stats = $stats[$key]; + return [ + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php new file mode 100644 index 0000000..95ab5be --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ExtMongoDBCache.php @@ -0,0 +1,196 @@ +collection = $collection->withOptions(['typeMap' => null]); + $this->database = new Database($collection->getManager(), $collection->getDatabaseName()); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->createExpirationIndex(); + $this->doDelete($id); + return false; + } + + return unserialize($document[MongoDBCache::DATA_FIELD]->getData()); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->createExpirationIndex(); + $this->doDelete($id); + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $this->collection->updateOne( + ['_id' => $id], + [ + '$set' => [ + MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new UTCDateTime((time() + $lifeTime) * 1000): null), + MongoDBCache::DATA_FIELD => new Binary(serialize($data), Binary::TYPE_GENERIC), + ], + ], + ['upsert' => true] + ); + } catch (Exception $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + try { + $this->collection->deleteOne(['_id' => $id]); + } catch (Exception $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + try { + // Use remove() in lieu of drop() to maintain any collection indexes + $this->collection->deleteMany([]); + } catch (Exception $e) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $uptime = null; + $memoryUsage = null; + + try { + $serverStatus = $this->database->command([ + 'serverStatus' => 1, + 'locks' => 0, + 'metrics' => 0, + 'recordStats' => 0, + 'repl' => 0, + ])->toArray()[0]; + $uptime = $serverStatus['uptime'] ?? null; + } catch (Exception $e) { + } + + try { + $collStats = $this->database->command(['collStats' => $this->collection->getCollectionName()])->toArray()[0]; + $memoryUsage = $collStats['size'] ?? null; + } catch (Exception $e) { + } + + return [ + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => $uptime, + Cache::STATS_MEMORY_USAGE => $memoryUsage, + Cache::STATS_MEMORY_AVAILABLE => null, + ]; + } + + /** + * Check if the document is expired. + */ + private function isExpired(BSONDocument $document) : bool + { + return isset($document[MongoDBCache::EXPIRATION_FIELD]) && + $document[MongoDBCache::EXPIRATION_FIELD] instanceof UTCDateTime && + $document[MongoDBCache::EXPIRATION_FIELD]->toDateTime() < new \DateTime(); + } + + private function createExpirationIndex() : void + { + if ($this->expirationIndexCreated) { + return; + } + + $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php new file mode 100644 index 0000000..184680c --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FileCache.php @@ -0,0 +1,276 @@ +umask = $umask; + + if (! $this->createPathIfNeeded($directory)) { + throw new \InvalidArgumentException(sprintf( + 'The directory "%s" does not exist and could not be created.', + $directory + )); + } + + if (! is_writable($directory)) { + throw new \InvalidArgumentException(sprintf( + 'The directory "%s" is not writable.', + $directory + )); + } + + // YES, this needs to be *after* createPathIfNeeded() + $this->directory = realpath($directory); + $this->extension = (string) $extension; + + $this->directoryStringLength = strlen($this->directory); + $this->extensionStringLength = strlen($this->extension); + $this->isRunningOnWindows = defined('PHP_WINDOWS_VERSION_BUILD'); + } + + /** + * Gets the cache directory. + * + * @return string + */ + public function getDirectory() + { + return $this->directory; + } + + /** + * Gets the cache file extension. + * + * @return string + */ + public function getExtension() + { + return $this->extension; + } + + /** + * @param string $id + * + * @return string + */ + protected function getFilename($id) + { + $hash = hash('sha256', $id); + + // This ensures that the filename is unique and that there are no invalid chars in it. + if ($id === '' + || ((strlen($id) * 2 + $this->extensionStringLength) > 255) + || ($this->isRunningOnWindows && ($this->directoryStringLength + 4 + strlen($id) * 2 + $this->extensionStringLength) > 258) + ) { + // Most filesystems have a limit of 255 chars for each path component. On Windows the the whole path is limited + // to 260 chars (including terminating null char). Using long UNC ("\\?\" prefix) does not work with the PHP API. + // And there is a bug in PHP (https://bugs.php.net/bug.php?id=70943) with path lengths of 259. + // So if the id in hex representation would surpass the limit, we use the hash instead. The prefix prevents + // collisions between the hash and bin2hex. + $filename = '_' . $hash; + } else { + $filename = bin2hex($id); + } + + return $this->directory + . DIRECTORY_SEPARATOR + . substr($hash, 0, 2) + . DIRECTORY_SEPARATOR + . $filename + . $this->extension; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $filename = $this->getFilename($id); + + return @unlink($filename) || ! file_exists($filename); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + foreach ($this->getIterator() as $name => $file) { + if ($file->isDir()) { + // Remove the intermediate directories which have been created to balance the tree. It only takes effect + // if the directory is empty. If several caches share the same directory but with different file extensions, + // the other ones are not removed. + @rmdir($name); + } elseif ($this->isFilenameEndingWithExtension($name)) { + // If an extension is set, only remove files which end with the given extension. + // If no extension is set, we have no other choice than removing everything. + @unlink($name); + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $usage = 0; + foreach ($this->getIterator() as $name => $file) { + if ($file->isDir() || ! $this->isFilenameEndingWithExtension($name)) { + continue; + } + + $usage += $file->getSize(); + } + + $free = disk_free_space($this->directory); + + return [ + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $usage, + Cache::STATS_MEMORY_AVAILABLE => $free, + ]; + } + + /** + * Create path if needed. + * + * @return bool TRUE on success or if path already exists, FALSE if path cannot be created. + */ + private function createPathIfNeeded(string $path) : bool + { + if (! is_dir($path)) { + if (@mkdir($path, 0777 & (~$this->umask), true) === false && ! is_dir($path)) { + return false; + } + } + + return true; + } + + /** + * Writes a string content to file in an atomic way. + * + * @param string $filename Path to the file where to write the data. + * @param string $content The content to write + * + * @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error. + */ + protected function writeFile(string $filename, string $content) : bool + { + $filepath = pathinfo($filename, PATHINFO_DIRNAME); + + if (! $this->createPathIfNeeded($filepath)) { + return false; + } + + if (! is_writable($filepath)) { + return false; + } + + $tmpFile = tempnam($filepath, 'swap'); + @chmod($tmpFile, 0666 & (~$this->umask)); + + if (file_put_contents($tmpFile, $content) !== false) { + @chmod($tmpFile, 0666 & (~$this->umask)); + if (@rename($tmpFile, $filename)) { + return true; + } + + @unlink($tmpFile); + } + + return false; + } + + private function getIterator() : \Iterator + { + return new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + } + + /** + * @param string $name The filename + */ + private function isFilenameEndingWithExtension(string $name) : bool + { + return $this->extension === '' + || strrpos($name, $this->extension) === (strlen($name) - $this->extensionStringLength); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php new file mode 100644 index 0000000..8f34c9c --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FilesystemCache.php @@ -0,0 +1,102 @@ +getFilename($id); + + if (! is_file($filename)) { + return false; + } + + $resource = fopen($filename, 'r'); + $line = fgets($resource); + + if ($line !== false) { + $lifetime = (int) $line; + } + + if ($lifetime !== 0 && $lifetime < time()) { + fclose($resource); + + return false; + } + + while (($line = fgets($resource)) !== false) { + $data .= $line; + } + + fclose($resource); + + return unserialize($data); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $lifetime = -1; + $filename = $this->getFilename($id); + + if (! is_file($filename)) { + return false; + } + + $resource = fopen($filename, 'r'); + $line = fgets($resource); + + if ($line !== false) { + $lifetime = (int) $line; + } + + fclose($resource); + + return $lifetime === 0 || $lifetime > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + $data = serialize($data); + $filename = $this->getFilename($id); + + return $this->writeFile($filename, $lifeTime . PHP_EOL . $data); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php new file mode 100644 index 0000000..ee7ce98 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/FlushableCache.php @@ -0,0 +1,18 @@ +collection = $collection; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::DATA_FIELD, MongoDBCache::EXPIRATION_FIELD]); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->createExpirationIndex(); + $this->doDelete($id); + return false; + } + + return unserialize($document[MongoDBCache::DATA_FIELD]->bin); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $document = $this->collection->findOne(['_id' => $id], [MongoDBCache::EXPIRATION_FIELD]); + + if ($document === null) { + return false; + } + + if ($this->isExpired($document)) { + $this->createExpirationIndex(); + $this->doDelete($id); + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $result = $this->collection->update( + ['_id' => $id], + [ + '$set' => [ + MongoDBCache::EXPIRATION_FIELD => ($lifeTime > 0 ? new MongoDate(time() + $lifeTime) : null), + MongoDBCache::DATA_FIELD => new MongoBinData(serialize($data), MongoBinData::BYTE_ARRAY), + ], + ], + ['upsert' => true, 'multiple' => false] + ); + } catch (MongoCursorException $e) { + return false; + } + + return ($result['ok'] ?? 1) == 1; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + $result = $this->collection->remove(['_id' => $id]); + + return ($result['ok'] ?? 1) == 1; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + // Use remove() in lieu of drop() to maintain any collection indexes + $result = $this->collection->remove(); + + return ($result['ok'] ?? 1) == 1; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $serverStatus = $this->collection->db->command([ + 'serverStatus' => 1, + 'locks' => 0, + 'metrics' => 0, + 'recordStats' => 0, + 'repl' => 0, + ]); + + $collStats = $this->collection->db->command(['collStats' => 1]); + + return [ + Cache::STATS_HITS => null, + Cache::STATS_MISSES => null, + Cache::STATS_UPTIME => $serverStatus['uptime'] ?? null, + Cache::STATS_MEMORY_USAGE => $collStats['size'] ?? null, + Cache::STATS_MEMORY_AVAILABLE => null, + ]; + } + + /** + * Check if the document is expired. + * + * @param array $document + */ + private function isExpired(array $document) : bool + { + return isset($document[MongoDBCache::EXPIRATION_FIELD]) && + $document[MongoDBCache::EXPIRATION_FIELD] instanceof MongoDate && + $document[MongoDBCache::EXPIRATION_FIELD]->sec < time(); + } + + + private function createExpirationIndex() : void + { + if ($this->expirationIndexCreated) { + return; + } + + $this->expirationIndexCreated = true; + $this->collection->createIndex([MongoDBCache::EXPIRATION_FIELD => 1], ['background' => true, 'expireAfterSeconds' => 0]); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php new file mode 100644 index 0000000..1597ff7 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcacheCache.php @@ -0,0 +1,102 @@ +memcache = $memcache; + } + + /** + * Gets the memcache instance used by the cache. + * + * @return Memcache|null + */ + public function getMemcache() + { + return $this->memcache; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcache->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $flags = null; + $this->memcache->get($id, $flags); + + //if memcache has changed the value of "flags", it means the value exists + return $flags !== null; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->memcache->set($id, $data, 0, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + // Memcache::delete() returns false if entry does not exist + return $this->memcache->delete($id) || ! $this->doContains($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcache->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcache->getStats(); + return [ + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php new file mode 100644 index 0000000..6591b92 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MemcachedCache.php @@ -0,0 +1,130 @@ +memcached = $memcached; + } + + /** + * Gets the memcached instance used by the cache. + * + * @return Memcached|null + */ + public function getMemcached() + { + return $this->memcached; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->memcached->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + return $this->memcached->getMulti($keys) ?: []; + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($lifetime > 30 * 24 * 3600) { + $lifetime = time() + $lifetime; + } + + return $this->memcached->setMulti($keysAndValues, $lifetime); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $this->memcached->get($id); + + return $this->memcached->getResultCode() === Memcached::RES_SUCCESS; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 30 * 24 * 3600) { + $lifeTime = time() + $lifeTime; + } + return $this->memcached->set($id, $data, (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDeleteMultiple(array $keys) + { + return $this->memcached->deleteMulti($keys) + || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->memcached->delete($id) + || $this->memcached->getResultCode() === Memcached::RES_NOTFOUND; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->memcached->flush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $stats = $this->memcached->getStats(); + $servers = $this->memcached->getServerList(); + $key = $servers[0]['host'] . ':' . $servers[0]['port']; + $stats = $stats[$key]; + return [ + Cache::STATS_HITS => $stats['get_hits'], + Cache::STATS_MISSES => $stats['get_misses'], + Cache::STATS_UPTIME => $stats['uptime'], + Cache::STATS_MEMORY_USAGE => $stats['bytes'], + Cache::STATS_MEMORY_AVAILABLE => $stats['limit_maxbytes'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php new file mode 100644 index 0000000..05ac4f8 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MongoDBCache.php @@ -0,0 +1,110 @@ +provider = new LegacyMongoDBCache($collection); + } elseif ($collection instanceof Collection) { + $this->provider = new ExtMongoDBCache($collection); + } else { + throw new \InvalidArgumentException('Invalid collection given - expected a MongoCollection or MongoDB\Collection instance'); + } + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->provider->doFetch($id); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->provider->doContains($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return $this->provider->doSave($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->provider->doDelete($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->provider->doFlush(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return $this->provider->doGetStats(); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php new file mode 100644 index 0000000..b4faa41 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/MultiDeleteCache.php @@ -0,0 +1,21 @@ + infinite lifeTime). + * + * @return bool TRUE if the operation was successful, FALSE if it wasn't. + */ + public function saveMultiple(array $keysAndValues, $lifetime = 0); +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php new file mode 100644 index 0000000..132b154 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PhpFileCache.php @@ -0,0 +1,118 @@ +includeFileForId($id); + + if ($value === null) { + return false; + } + + if ($value['lifetime'] !== 0 && $value['lifetime'] < time()) { + return false; + } + + return $value['data']; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $value = $this->includeFileForId($id); + + if ($value === null) { + return false; + } + + return $value['lifetime'] === 0 || $value['lifetime'] > time(); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + $lifeTime = time() + $lifeTime; + } + + $filename = $this->getFilename($id); + + $value = [ + 'lifetime' => $lifeTime, + 'data' => $data, + ]; + + if (is_object($data) && method_exists($data, '__set_state')) { + $value = var_export($value, true); + $code = sprintf('writeFile($filename, $code); + } + + /** + * @return array|null + */ + private function includeFileForId(string $id) : ?array + { + $fileName = $this->getFilename($id); + + // note: error suppression is still faster than `file_exists`, `is_file` and `is_readable` + set_error_handler(self::$emptyErrorHandler); + + $value = include $fileName; + + restore_error_handler(); + + if (! isset($value['lifetime'])) { + return null; + } + + return $value; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php new file mode 100644 index 0000000..40803de --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php @@ -0,0 +1,143 @@ +client = $client; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $result = $this->client->get($id); + if ($result === null) { + return false; + } + + return unserialize($result); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + $fetchedItems = call_user_func_array([$this->client, 'mget'], $keys); + + return array_map('unserialize', array_filter(array_combine($keys, $fetchedItems))); + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($lifetime) { + $success = true; + + // Keys have lifetime, use SETEX for each of them + foreach ($keysAndValues as $key => $value) { + $response = (string) $this->client->setex($key, $lifetime, serialize($value)); + + if ($response == 'OK') { + continue; + } + + $success = false; + } + + return $success; + } + + // No lifetime, use MSET + $response = $this->client->mset(array_map(function ($value) { + return serialize($value); + }, $keysAndValues)); + + return (string) $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return (bool) $this->client->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $data = serialize($data); + if ($lifeTime > 0) { + $response = $this->client->setex($id, $lifeTime, $data); + } else { + $response = $this->client->set($id, $data); + } + + return $response === true || $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->client->del($id) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doDeleteMultiple(array $keys) + { + return $this->client->del($keys) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $response = $this->client->flushdb(); + + return $response === true || $response == 'OK'; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->client->info(); + + return [ + Cache::STATS_HITS => $info['Stats']['keyspace_hits'], + Cache::STATS_MISSES => $info['Stats']['keyspace_misses'], + Cache::STATS_UPTIME => $info['Server']['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['Memory']['used_memory'], + Cache::STATS_MEMORY_AVAILABLE => false, + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php new file mode 100644 index 0000000..4638860 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RedisCache.php @@ -0,0 +1,175 @@ +setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue()); + $this->redis = $redis; + } + + /** + * Gets the redis instance used by the cache. + * + * @return Redis|null + */ + public function getRedis() + { + return $this->redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + return $this->redis->get($id); + } + + /** + * {@inheritdoc} + */ + protected function doFetchMultiple(array $keys) + { + $fetchedItems = array_combine($keys, $this->redis->mget($keys)); + + // Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data. + $foundItems = []; + + foreach ($fetchedItems as $key => $value) { + if ($value === false && ! $this->redis->exists($key)) { + continue; + } + + $foundItems[$key] = $value; + } + + return $foundItems; + } + + /** + * {@inheritdoc} + */ + protected function doSaveMultiple(array $keysAndValues, $lifetime = 0) + { + if ($lifetime) { + $success = true; + + // Keys have lifetime, use SETEX for each of them + foreach ($keysAndValues as $key => $value) { + if ($this->redis->setex($key, $lifetime, $value)) { + continue; + } + + $success = false; + } + + return $success; + } + + // No lifetime, use MSET + return (bool) $this->redis->mset($keysAndValues); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + $exists = $this->redis->exists($id); + + if (is_bool($exists)) { + return $exists; + } + + return $exists > 0; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + if ($lifeTime > 0) { + return $this->redis->setex($id, $lifeTime, $data); + } + + return $this->redis->set($id, $data); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->redis->delete($id) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doDeleteMultiple(array $keys) + { + return $this->redis->delete($keys) >= 0; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->redis->flushDB(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $info = $this->redis->info(); + return [ + Cache::STATS_HITS => $info['keyspace_hits'], + Cache::STATS_MISSES => $info['keyspace_misses'], + Cache::STATS_UPTIME => $info['uptime_in_seconds'], + Cache::STATS_MEMORY_USAGE => $info['used_memory'], + Cache::STATS_MEMORY_AVAILABLE => false, + ]; + } + + /** + * Returns the serializer constant to use. If Redis is compiled with + * igbinary support, that is used. Otherwise the default PHP serializer is + * used. + * + * @return int One of the Redis::SERIALIZER_* constants + */ + protected function getSerializerValue() + { + if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) { + return Redis::SERIALIZER_IGBINARY; + } + + return Redis::SERIALIZER_PHP; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php new file mode 100644 index 0000000..0ae2206 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/RiakCache.php @@ -0,0 +1,228 @@ +bucket = $bucket; + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + try { + $response = $this->bucket->get($id); + + // No objects found + if (! $response->hasObject()) { + return false; + } + + // Check for attempted siblings + $object = ($response->hasSiblings()) + ? $this->resolveConflict($id, $response->getVClock(), $response->getObjectList()) + : $response->getFirstObject(); + + // Check for expired object + if ($this->isExpired($object)) { + $this->bucket->delete($object); + + return false; + } + + return unserialize($object->getContent()); + } catch (Exception\RiakException $e) { + // Covers: + // - Riak\ConnectionException + // - Riak\CommunicationException + // - Riak\UnexpectedResponseException + // - Riak\NotFoundException + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + try { + // We only need the HEAD, not the entire object + $input = new Input\GetInput(); + + $input->setReturnHead(true); + + $response = $this->bucket->get($id, $input); + + // No objects found + if (! $response->hasObject()) { + return false; + } + + $object = $response->getFirstObject(); + + // Check for expired object + if ($this->isExpired($object)) { + $this->bucket->delete($object); + + return false; + } + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + try { + $object = new Object($id); + + $object->setContent(serialize($data)); + + if ($lifeTime > 0) { + $object->addMetadata(self::EXPIRES_HEADER, (string) (time() + $lifeTime)); + } + + $this->bucket->put($object); + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + try { + $this->bucket->delete($id); + + return true; + } catch (Exception\BadArgumentsException $e) { + // Key did not exist on cluster already + } catch (Exception\RiakException $e) { + // Covers: + // - Riak\Exception\ConnectionException + // - Riak\Exception\CommunicationException + // - Riak\Exception\UnexpectedResponseException + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + try { + $keyList = $this->bucket->getKeyList(); + + foreach ($keyList as $key) { + $this->bucket->delete($key); + } + + return true; + } catch (Exception\RiakException $e) { + // Do nothing + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + // Only exposed through HTTP stats API, not Protocol Buffers API + return null; + } + + /** + * Check if a given Riak Object have expired. + */ + private function isExpired(Object $object) : bool + { + $metadataMap = $object->getMetadataMap(); + + return isset($metadataMap[self::EXPIRES_HEADER]) + && $metadataMap[self::EXPIRES_HEADER] < time(); + } + + /** + * On-read conflict resolution. Applied approach here is last write wins. + * Specific needs may override this method to apply alternate conflict resolutions. + * + * {@internal Riak does not attempt to resolve a write conflict, and store + * it as sibling of conflicted one. By following this approach, it is up to + * the next read to resolve the conflict. When this happens, your fetched + * object will have a list of siblings (read as a list of objects). + * In our specific case, we do not care about the intermediate ones since + * they are all the same read from storage, and we do apply a last sibling + * (last write) wins logic. + * If by any means our resolution generates another conflict, it'll up to + * next read to properly solve it.} + * + * @param string $id + * @param string $vClock + * @param array $objectList + * + * @return Object + */ + protected function resolveConflict($id, $vClock, array $objectList) + { + // Our approach here is last-write wins + $winner = $objectList[count($objectList) - 1]; + + $putInput = new Input\PutInput(); + $putInput->setVClock($vClock); + + $mergedObject = new Object($id); + $mergedObject->setContent($winner->getContent()); + + $this->bucket->put($mergedObject, $putInput); + + return $mergedObject; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php new file mode 100644 index 0000000..8b3111f --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/SQLite3Cache.php @@ -0,0 +1,206 @@ +sqlite = $sqlite; + $this->table = (string) $table; + + $this->ensureTableExists(); + } + + private function ensureTableExists() : void + { + $this->sqlite->exec( + sprintf( + 'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)', + $this->table, + static::ID_FIELD, + static::DATA_FIELD, + static::EXPIRATION_FIELD + ) + ); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $item = $this->findById($id); + + if (! $item) { + return false; + } + + return unserialize($item[self::DATA_FIELD]); + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->findById($id, false) !== null; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $statement = $this->sqlite->prepare(sprintf( + 'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)', + $this->table, + implode(',', $this->getFields()) + )); + + $statement->bindValue(':id', $id); + $statement->bindValue(':data', serialize($data), SQLITE3_BLOB); + $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null); + + return $statement->execute() instanceof SQLite3Result; + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + list($idField) = $this->getFields(); + + $statement = $this->sqlite->prepare(sprintf( + 'DELETE FROM %s WHERE %s = :id', + $this->table, + $idField + )); + + $statement->bindValue(':id', $id); + + return $statement->execute() instanceof SQLite3Result; + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table)); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + // no-op. + } + + /** + * Find a single row by ID. + * + * @param mixed $id + * + * @return array|null + */ + private function findById($id, bool $includeData = true) : ?array + { + list($idField) = $fields = $this->getFields(); + + if (! $includeData) { + $key = array_search(static::DATA_FIELD, $fields); + unset($fields[$key]); + } + + $statement = $this->sqlite->prepare(sprintf( + 'SELECT %s FROM %s WHERE %s = :id LIMIT 1', + implode(',', $fields), + $this->table, + $idField + )); + + $statement->bindValue(':id', $id, SQLITE3_TEXT); + + $item = $statement->execute()->fetchArray(SQLITE3_ASSOC); + + if ($item === false) { + return null; + } + + if ($this->isExpired($item)) { + $this->doDelete($id); + + return null; + } + + return $item; + } + + /** + * Gets an array of the fields in our table. + * + * @return array + */ + private function getFields() : array + { + return [static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD]; + } + + /** + * Check if the item is expired. + * + * @param array $item + */ + private function isExpired(array $item) : bool + { + return isset($item[static::EXPIRATION_FIELD]) && + $item[self::EXPIRATION_FIELD] !== null && + $item[self::EXPIRATION_FIELD] < time(); + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php new file mode 100644 index 0000000..dbf55f4 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Version.php @@ -0,0 +1,8 @@ + $info['total_hit_count'], + Cache::STATS_MISSES => $info['total_miss_count'], + Cache::STATS_UPTIME => $info['total_cache_uptime'], + Cache::STATS_MEMORY_USAGE => $meminfo['memory_total'], + Cache::STATS_MEMORY_AVAILABLE => $meminfo['memory_free'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php new file mode 100644 index 0000000..2d5c10a --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/XcacheCache.php @@ -0,0 +1,102 @@ +doContains($id) ? unserialize(xcache_get($id)) : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return xcache_isset($id); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + return xcache_set($id, serialize($data), (int) $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return xcache_unset($id); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->checkAuthorization(); + + xcache_clear_cache(XC_TYPE_VAR); + + return true; + } + + /** + * Checks that xcache.admin.enable_auth is Off. + * + * @return void + * + * @throws \BadMethodCallException When xcache.admin.enable_auth is On. + */ + protected function checkAuthorization() + { + if (ini_get('xcache.admin.enable_auth')) { + throw new \BadMethodCallException( + 'To use all features of \Doctrine\Common\Cache\XcacheCache, ' + . 'you must set "xcache.admin.enable_auth" to "Off" in your php.ini.' + ); + } + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + $this->checkAuthorization(); + + $info = xcache_info(XC_TYPE_VAR, 0); + return [ + Cache::STATS_HITS => $info['hits'], + Cache::STATS_MISSES => $info['misses'], + Cache::STATS_UPTIME => null, + Cache::STATS_MEMORY_USAGE => $info['size'], + Cache::STATS_MEMORY_AVAILABLE => $info['avail'], + ]; + } +} diff --git a/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php new file mode 100644 index 0000000..275f3c4 --- /dev/null +++ b/vendor/doctrine/cache/lib/Doctrine/Common/Cache/ZendDataCache.php @@ -0,0 +1,68 @@ +getNamespace(); + if (empty($namespace)) { + return zend_shm_cache_clear(); + } + return zend_shm_cache_clear($namespace); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + return null; + } +} diff --git a/vendor/doctrine/collections/CONTRIBUTING.md b/vendor/doctrine/collections/CONTRIBUTING.md new file mode 100644 index 0000000..407a665 --- /dev/null +++ b/vendor/doctrine/collections/CONTRIBUTING.md @@ -0,0 +1,67 @@ +# Contribute to Doctrine + +Thank you for contributing to Doctrine! + +Before we can merge your Pull-Request here are some guidelines that you need to follow. +These guidelines exist not to annoy you, but to keep the code base clean, +unified and future proof. + +## We only accept PRs to "master" + +Our branching strategy is "everything to master first", even +bugfixes and we then merge them into the stable branches. You should only +open pull requests against the master branch. Otherwise we cannot accept the PR. + +There is one exception to the rule, when we merged a bug into some stable branches +we do occasionally accept pull requests that merge the same bug fix into earlier +branches. + +## Coding Standard + +We use [doctrine coding standard](https://github.com/doctrine/coding-standard) which is PSR-1 and PSR-2: + +* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md +* https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md + +with some exceptions/differences: + +* Keep the nesting of control structures per method as small as possible +* Align equals (=) signs +* Add spaces between assignment, control and return statements +* Prefer early exit over nesting conditions +* Add spaces around a negation if condition ``if ( ! $cond)`` +* Add legal information at the beginning of each source file +* Add ``@author`` [phpDoc](https://www.phpdoc.org/docs/latest/references/phpdoc/tags/author.html) comment at DockBlock of class/interface/trait that you create. + +## Unit-Tests + +Please try to add a test for your pull-request. + +* If you want to contribute new functionality add unit- or functional tests + depending on the scope of the feature. + +You can run the unit-tests by calling ``vendor/bin/phpunit`` from the root of the project. +It will run all the project tests. + +In order to do that, you will need a fresh copy of doctrine/collections, and you +will have to run a composer installation in the project: + +```sh +git clone git@github.com:doctrine/collections.git +cd collections +curl -sS https://getcomposer.org/installer | php -- +./composer.phar install +``` + +## Travis + +We automatically run your pull request through [Travis CI](https://www.travis-ci.org) +against supported PHP versions. If you break the tests, we cannot merge your code, +so please make sure that your code is working before opening up a Pull-Request. + +## Getting merged + +Please allow us time to review your pull requests. We will give our best to review +everything as fast as possible, but cannot always live up to our own expectations. + +Thank you very much again for your contribution! diff --git a/vendor/doctrine/collections/LICENSE b/vendor/doctrine/collections/LICENSE new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/vendor/doctrine/collections/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/collections/README.md b/vendor/doctrine/collections/README.md new file mode 100644 index 0000000..81e06d0 --- /dev/null +++ b/vendor/doctrine/collections/README.md @@ -0,0 +1,27 @@ +# Doctrine Collections + +[![Build Status](https://travis-ci.org/doctrine/collections.svg?branch=master)](https://travis-ci.org/doctrine/collections) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/collections/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/collections/?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/collections/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/collections/?branch=master) + +Collections Abstraction library + +## Changelog + +### v1.3.0 + +* [Explicit casting of first and max results in criteria API](https://github.com/doctrine/collections/pull/26) +* [Keep keys when using `ArrayCollection#matching()` with sorting](https://github.com/doctrine/collections/pull/49) +* [Made `AbstractLazyCollection#$initialized` protected for extensibility](https://github.com/doctrine/collections/pull/52) + +### v1.2.0 + +* Add a new ``AbstractLazyCollection`` + +### v1.1.0 + +* Deprecated ``Comparison::IS``, because it's only there for SQL semantics. + These are fixed in the ORM instead. +* Add ``Comparison::CONTAINS`` to perform partial string matches: + + $criteria->andWhere($criteria->expr()->contains('property', 'Foo')); diff --git a/vendor/doctrine/collections/composer.json b/vendor/doctrine/collections/composer.json new file mode 100644 index 0000000..b42dbb9 --- /dev/null +++ b/vendor/doctrine/collections/composer.json @@ -0,0 +1,35 @@ +{ + "name": "doctrine/collections", + "type": "library", + "description": "Collections Abstraction library", + "keywords": ["collections", "array", "iterator"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^5.7", + "doctrine/coding-standard": "~0.1@dev" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Collections\\": "lib/" } + }, + "autoload-dev": { + "psr-4": { + "Doctrine\\Tests\\": "tests/Doctrine/Tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php new file mode 100644 index 0000000..746ae71 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php @@ -0,0 +1,343 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Closure; + +/** + * Lazy collection that is backed by a concrete collection + * + * @author Michaël Gallego + * @since 1.2 + */ +abstract class AbstractLazyCollection implements Collection +{ + /** + * The backed collection to use + * + * @var Collection + */ + protected $collection; + + /** + * @var bool + */ + protected $initialized = false; + + /** + * {@inheritDoc} + */ + public function count() + { + $this->initialize(); + return $this->collection->count(); + } + + /** + * {@inheritDoc} + */ + public function add($element) + { + $this->initialize(); + return $this->collection->add($element); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->initialize(); + $this->collection->clear(); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + $this->initialize(); + return $this->collection->contains($element); + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + $this->initialize(); + return $this->collection->isEmpty(); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + $this->initialize(); + return $this->collection->remove($key); + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $this->initialize(); + return $this->collection->removeElement($element); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + $this->initialize(); + return $this->collection->containsKey($key); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + $this->initialize(); + return $this->collection->get($key); + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + $this->initialize(); + return $this->collection->getKeys(); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + $this->initialize(); + return $this->collection->getValues(); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->initialize(); + $this->collection->set($key, $value); + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + $this->initialize(); + return $this->collection->toArray(); + } + + /** + * {@inheritDoc} + */ + public function first() + { + $this->initialize(); + return $this->collection->first(); + } + + /** + * {@inheritDoc} + */ + public function last() + { + $this->initialize(); + return $this->collection->last(); + } + + /** + * {@inheritDoc} + */ + public function key() + { + $this->initialize(); + return $this->collection->key(); + } + + /** + * {@inheritDoc} + */ + public function current() + { + $this->initialize(); + return $this->collection->current(); + } + + /** + * {@inheritDoc} + */ + public function next() + { + $this->initialize(); + return $this->collection->next(); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + $this->initialize(); + return $this->collection->exists($p); + } + + /** + * {@inheritDoc} + */ + public function filter(Closure $p) + { + $this->initialize(); + return $this->collection->filter($p); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + $this->initialize(); + return $this->collection->forAll($p); + } + + /** + * {@inheritDoc} + */ + public function map(Closure $func) + { + $this->initialize(); + return $this->collection->map($func); + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $this->initialize(); + return $this->collection->partition($p); + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + $this->initialize(); + return $this->collection->indexOf($element); + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + $this->initialize(); + return $this->collection->slice($offset, $length); + } + + /** + * {@inheritDoc} + */ + public function getIterator() + { + $this->initialize(); + return $this->collection->getIterator(); + } + + /** + * {@inheritDoc} + */ + public function offsetExists($offset) + { + $this->initialize(); + return $this->collection->offsetExists($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetGet($offset) + { + $this->initialize(); + return $this->collection->offsetGet($offset); + } + + /** + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + $this->initialize(); + $this->collection->offsetSet($offset, $value); + } + + /** + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + $this->initialize(); + $this->collection->offsetUnset($offset); + } + + /** + * Is the lazy collection already initialized? + * + * @return bool + */ + public function isInitialized() + { + return $this->initialized; + } + + /** + * Initialize the collection + * + * @return void + */ + protected function initialize() + { + if ( ! $this->initialized) { + $this->doInitialize(); + $this->initialized = true; + } + } + + /** + * Do the initialization logic + * + * @return void + */ + abstract protected function doInitialize(); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php new file mode 100644 index 0000000..0bebce2 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php @@ -0,0 +1,413 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use ArrayIterator; +use Closure; +use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; + +/** + * An ArrayCollection is a Collection implementation that wraps a regular PHP array. + * + * Warning: Using (un-)serialize() on a collection is not a supported use-case + * and may break when we change the internals in the future. If you need to + * serialize a collection use {@link toArray()} and reconstruct the collection + * manually. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +class ArrayCollection implements Collection, Selectable +{ + /** + * An array containing the entries of this collection. + * + * @var array + */ + private $elements; + + /** + * Initializes a new ArrayCollection. + * + * @param array $elements + */ + public function __construct(array $elements = []) + { + $this->elements = $elements; + } + + /** + * Creates a new instance from the specified elements. + * + * This method is provided for derived classes to specify how a new + * instance should be created when constructor semantics have changed. + * + * @param array $elements Elements. + * + * @return static + */ + protected function createFrom(array $elements) + { + return new static($elements); + } + + /** + * {@inheritDoc} + */ + public function toArray() + { + return $this->elements; + } + + /** + * {@inheritDoc} + */ + public function first() + { + return reset($this->elements); + } + + /** + * {@inheritDoc} + */ + public function last() + { + return end($this->elements); + } + + /** + * {@inheritDoc} + */ + public function key() + { + return key($this->elements); + } + + /** + * {@inheritDoc} + */ + public function next() + { + return next($this->elements); + } + + /** + * {@inheritDoc} + */ + public function current() + { + return current($this->elements); + } + + /** + * {@inheritDoc} + */ + public function remove($key) + { + if ( ! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) { + return null; + } + + $removed = $this->elements[$key]; + unset($this->elements[$key]); + + return $removed; + } + + /** + * {@inheritDoc} + */ + public function removeElement($element) + { + $key = array_search($element, $this->elements, true); + + if ($key === false) { + return false; + } + + unset($this->elements[$key]); + + return true; + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetExists($offset) + { + return $this->containsKey($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetSet($offset, $value) + { + if ( ! isset($offset)) { + $this->add($value); + return; + } + + $this->set($offset, $value); + } + + /** + * Required by interface ArrayAccess. + * + * {@inheritDoc} + */ + public function offsetUnset($offset) + { + $this->remove($offset); + } + + /** + * {@inheritDoc} + */ + public function containsKey($key) + { + return isset($this->elements[$key]) || array_key_exists($key, $this->elements); + } + + /** + * {@inheritDoc} + */ + public function contains($element) + { + return in_array($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function exists(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function indexOf($element) + { + return array_search($element, $this->elements, true); + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + return $this->elements[$key] ?? null; + } + + /** + * {@inheritDoc} + */ + public function getKeys() + { + return array_keys($this->elements); + } + + /** + * {@inheritDoc} + */ + public function getValues() + { + return array_values($this->elements); + } + + /** + * {@inheritDoc} + */ + public function count() + { + return count($this->elements); + } + + /** + * {@inheritDoc} + */ + public function set($key, $value) + { + $this->elements[$key] = $value; + } + + /** + * {@inheritDoc} + */ + public function add($element) + { + $this->elements[] = $element; + + return true; + } + + /** + * {@inheritDoc} + */ + public function isEmpty() + { + return empty($this->elements); + } + + /** + * Required by interface IteratorAggregate. + * + * {@inheritDoc} + */ + public function getIterator() + { + return new ArrayIterator($this->elements); + } + + /** + * {@inheritDoc} + * + * @return static + */ + public function map(Closure $func) + { + return $this->createFrom(array_map($func, $this->elements)); + } + + /** + * {@inheritDoc} + * + * @return static + */ + public function filter(Closure $p) + { + return $this->createFrom(array_filter($this->elements, $p)); + } + + /** + * {@inheritDoc} + */ + public function forAll(Closure $p) + { + foreach ($this->elements as $key => $element) { + if ( ! $p($key, $element)) { + return false; + } + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function partition(Closure $p) + { + $matches = $noMatches = []; + + foreach ($this->elements as $key => $element) { + if ($p($key, $element)) { + $matches[$key] = $element; + } else { + $noMatches[$key] = $element; + } + } + + return [$this->createFrom($matches), $this->createFrom($noMatches)]; + } + + /** + * Returns a string representation of this object. + * + * @return string + */ + public function __toString() + { + return __CLASS__ . '@' . spl_object_hash($this); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->elements = []; + } + + /** + * {@inheritDoc} + */ + public function slice($offset, $length = null) + { + return array_slice($this->elements, $offset, $length, true); + } + + /** + * {@inheritDoc} + */ + public function matching(Criteria $criteria) + { + $expr = $criteria->getWhereExpression(); + $filtered = $this->elements; + + if ($expr) { + $visitor = new ClosureExpressionVisitor(); + $filter = $visitor->dispatch($expr); + $filtered = array_filter($filtered, $filter); + } + + if ($orderings = $criteria->getOrderings()) { + $next = null; + foreach (array_reverse($orderings) as $field => $ordering) { + $next = ClosureExpressionVisitor::sortByField($field, $ordering == Criteria::DESC ? -1 : 1, $next); + } + + uasort($filtered, $next); + } + + $offset = $criteria->getFirstResult(); + $length = $criteria->getMaxResults(); + + if ($offset || $length) { + $filtered = array_slice($filtered, (int)$offset, $length); + } + + return $this->createFrom($filtered); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php new file mode 100644 index 0000000..d4777fc --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php @@ -0,0 +1,263 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use ArrayAccess; +use Closure; +use Countable; +use IteratorAggregate; + +/** + * The missing (SPL) Collection/Array/OrderedMap interface. + * + * A Collection resembles the nature of a regular PHP array. That is, + * it is essentially an ordered map that can also be used + * like a list. + * + * A Collection has an internal iterator just like a PHP array. In addition, + * a Collection can be iterated with external iterators, which is preferable. + * To use an external iterator simply use the foreach language construct to + * iterate over the collection (which calls {@link getIterator()} internally) or + * explicitly retrieve an iterator though {@link getIterator()} which can then be + * used to iterate over the collection. + * You can not rely on the internal iterator of the collection being at a certain + * position unless you explicitly positioned it before. Prefer iteration with + * external iterators. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface Collection extends Countable, IteratorAggregate, ArrayAccess +{ + /** + * Adds an element at the end of the collection. + * + * @param mixed $element The element to add. + * + * @return bool Always TRUE. + */ + public function add($element); + + /** + * Clears the collection, removing all elements. + * + * @return void + */ + public function clear(); + + /** + * Checks whether an element is contained in the collection. + * This is an O(n) operation, where n is the size of the collection. + * + * @param mixed $element The element to search for. + * + * @return bool TRUE if the collection contains the element, FALSE otherwise. + */ + public function contains($element); + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return bool TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty(); + + /** + * Removes the element at the specified index from the collection. + * + * @param string|int $key The kex/index of the element to remove. + * + * @return mixed The removed element or NULL, if the collection did not contain the element. + */ + public function remove($key); + + /** + * Removes the specified element from the collection, if it is found. + * + * @param mixed $element The element to remove. + * + * @return bool TRUE if this collection contained the specified element, FALSE otherwise. + */ + public function removeElement($element); + + /** + * Checks whether the collection contains an element with the specified key/index. + * + * @param string|int $key The key/index to check for. + * + * @return bool TRUE if the collection contains an element with the specified key/index, + * FALSE otherwise. + */ + public function containsKey($key); + + /** + * Gets the element at the specified key/index. + * + * @param string|int $key The key/index of the element to retrieve. + * + * @return mixed + */ + public function get($key); + + /** + * Gets all keys/indices of the collection. + * + * @return array The keys/indices of the collection, in the order of the corresponding + * elements in the collection. + */ + public function getKeys(); + + /** + * Gets all values of the collection. + * + * @return array The values of all elements in the collection, in the order they + * appear in the collection. + */ + public function getValues(); + + /** + * Sets an element in the collection at the specified key/index. + * + * @param string|int $key The key/index of the element to set. + * @param mixed $value The element to set. + * + * @return void + */ + public function set($key, $value); + + /** + * Gets a native PHP array representation of the collection. + * + * @return array + */ + public function toArray(); + + /** + * Sets the internal iterator to the first element in the collection and returns this element. + * + * @return mixed + */ + public function first(); + + /** + * Sets the internal iterator to the last element in the collection and returns this element. + * + * @return mixed + */ + public function last(); + + /** + * Gets the key/index of the element at the current iterator position. + * + * @return int|string + */ + public function key(); + + /** + * Gets the element of the collection at the current iterator position. + * + * @return mixed + */ + public function current(); + + /** + * Moves the internal iterator position to the next element and returns this element. + * + * @return mixed + */ + public function next(); + + /** + * Tests for the existence of an element that satisfies the given predicate. + * + * @param Closure $p The predicate. + * + * @return bool TRUE if the predicate is TRUE for at least one element, FALSE otherwise. + */ + public function exists(Closure $p); + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + * + * @param Closure $p The predicate used for filtering. + * + * @return Collection A collection with the results of the filter operation. + */ + public function filter(Closure $p); + + /** + * Tests whether the given predicate p holds for all elements of this collection. + * + * @param Closure $p The predicate. + * + * @return bool TRUE, if the predicate yields TRUE for all elements, FALSE otherwise. + */ + public function forAll(Closure $p); + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @param Closure $func + * + * @return Collection + */ + public function map(Closure $func); + + /** + * Partitions this collection in two collections according to a predicate. + * Keys are preserved in the resulting collections. + * + * @param Closure $p The predicate on which to partition. + * + * @return Collection[] An array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + */ + public function partition(Closure $p); + + /** + * Gets the index/key of a given element. The comparison of two elements is strict, + * that means not only the value but also the type must match. + * For objects this means reference equality. + * + * @param mixed $element The element to search for. + * + * @return int|string|bool The key/index of the element or FALSE if the element was not found. + */ + public function indexOf($element); + + /** + * Extracts a slice of $length elements starting at position $offset from the Collection. + * + * If $length is null it returns all elements from $offset to the end of the Collection. + * Keys have to be preserved by this method. Calling this method will only return the + * selected slice and NOT change the elements contained in the collection slice is called on. + * + * @param int $offset The offset to start from. + * @param int|null $length The maximum number of elements to return, or null for no limit. + * + * @return array + */ + public function slice($offset, $length = null); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php new file mode 100644 index 0000000..748a084 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Criteria.php @@ -0,0 +1,261 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Expression; +use Doctrine\Common\Collections\Expr\CompositeExpression; + +/** + * Criteria for filtering Selectable collections. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Criteria +{ + /** + * @var string + */ + const ASC = 'ASC'; + + /** + * @var string + */ + const DESC = 'DESC'; + + /** + * @var \Doctrine\Common\Collections\ExpressionBuilder|null + */ + private static $expressionBuilder; + + /** + * @var \Doctrine\Common\Collections\Expr\Expression|null + */ + private $expression; + + /** + * @var string[] + */ + private $orderings = []; + + /** + * @var int|null + */ + private $firstResult; + + /** + * @var int|null + */ + private $maxResults; + + /** + * Creates an instance of the class. + * + * @return Criteria + */ + public static function create() + { + return new static(); + } + + /** + * Returns the expression builder. + * + * @return \Doctrine\Common\Collections\ExpressionBuilder + */ + public static function expr() + { + if (self::$expressionBuilder === null) { + self::$expressionBuilder = new ExpressionBuilder(); + } + + return self::$expressionBuilder; + } + + /** + * Construct a new Criteria. + * + * @param Expression $expression + * @param string[]|null $orderings + * @param int|null $firstResult + * @param int|null $maxResults + */ + public function __construct(Expression $expression = null, array $orderings = null, $firstResult = null, $maxResults = null) + { + $this->expression = $expression; + + $this->setFirstResult($firstResult); + $this->setMaxResults($maxResults); + + if (null !== $orderings) { + $this->orderBy($orderings); + } + } + + /** + * Sets the where expression to evaluate when this Criteria is searched for. + * + * @param Expression $expression + * + * @return Criteria + */ + public function where(Expression $expression) + { + $this->expression = $expression; + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an AND with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function andWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression( + CompositeExpression::TYPE_AND, + [$this->expression, $expression] + ); + + return $this; + } + + /** + * Appends the where expression to evaluate when this Criteria is searched for + * using an OR with previous expression. + * + * @param Expression $expression + * + * @return Criteria + */ + public function orWhere(Expression $expression) + { + if ($this->expression === null) { + return $this->where($expression); + } + + $this->expression = new CompositeExpression( + CompositeExpression::TYPE_OR, + [$this->expression, $expression] + ); + + return $this; + } + + /** + * Gets the expression attached to this Criteria. + * + * @return Expression|null + */ + public function getWhereExpression() + { + return $this->expression; + } + + /** + * Gets the current orderings of this Criteria. + * + * @return string[] + */ + public function getOrderings() + { + return $this->orderings; + } + + /** + * Sets the ordering of the result of this Criteria. + * + * Keys are field and values are the order, being either ASC or DESC. + * + * @see Criteria::ASC + * @see Criteria::DESC + * + * @param string[] $orderings + * + * @return Criteria + */ + public function orderBy(array $orderings) + { + $this->orderings = array_map( + function (string $ordering) : string { + return strtoupper($ordering) === Criteria::ASC ? Criteria::ASC : Criteria::DESC; + }, + $orderings + ); + + return $this; + } + + /** + * Gets the current first result option of this Criteria. + * + * @return int|null + */ + public function getFirstResult() + { + return $this->firstResult; + } + + /** + * Set the number of first result that this Criteria should return. + * + * @param int|null $firstResult The value to set. + * + * @return Criteria + */ + public function setFirstResult($firstResult) + { + $this->firstResult = null === $firstResult ? null : (int) $firstResult; + + return $this; + } + + /** + * Gets maxResults. + * + * @return int|null + */ + public function getMaxResults() + { + return $this->maxResults; + } + + /** + * Sets maxResults. + * + * @param int|null $maxResults The value to set. + * + * @return Criteria + */ + public function setMaxResults($maxResults) + { + $this->maxResults = null === $maxResults ? null : (int) $maxResults; + + return $this; + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php new file mode 100644 index 0000000..70b6b7e --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ClosureExpressionVisitor.php @@ -0,0 +1,267 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Walks an expression graph and turns it into a PHP closure. + * + * This closure can be used with {@Collection#filter()} and is used internally + * by {@ArrayCollection#select()}. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ClosureExpressionVisitor extends ExpressionVisitor +{ + /** + * Accesses the field of a given object. This field has to be public + * directly or indirectly (through an accessor get*, is*, or a magic + * method, __get, __call). + * + * @param object|array $object + * @param string $field + * + * @return mixed + */ + public static function getObjectFieldValue($object, $field) + { + if (is_array($object)) { + return $object[$field]; + } + + $accessors = ['get', 'is']; + + foreach ($accessors as $accessor) { + $accessor .= $field; + + if ( ! method_exists($object, $accessor)) { + continue; + } + + return $object->$accessor(); + } + + // __call should be triggered for get. + $accessor = $accessors[0] . $field; + + if (method_exists($object, '__call')) { + return $object->$accessor(); + } + + if ($object instanceof \ArrayAccess) { + return $object[$field]; + } + + if (isset($object->$field)) { + return $object->$field; + } + + // camelcase field name to support different variable naming conventions + $ccField = preg_replace_callback('/_(.?)/', function($matches) { return strtoupper($matches[1]); }, $field); + + foreach ($accessors as $accessor) { + $accessor .= $ccField; + + + if ( ! method_exists($object, $accessor)) { + continue; + } + + return $object->$accessor(); + } + + return $object->$field; + } + + /** + * Helper for sorting arrays of objects based on multiple fields + orientations. + * + * @param string $name + * @param int $orientation + * @param \Closure $next + * + * @return \Closure + */ + public static function sortByField($name, $orientation = 1, \Closure $next = null) + { + if ( ! $next) { + $next = function() : int { + return 0; + }; + } + + return function ($a, $b) use ($name, $next, $orientation) : int { + $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name); + $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name); + + if ($aValue === $bValue) { + return $next($a, $b); + } + + return (($aValue > $bValue) ? 1 : -1) * $orientation; + }; + } + + /** + * {@inheritDoc} + */ + public function walkComparison(Comparison $comparison) + { + $field = $comparison->getField(); + $value = $comparison->getValue()->getValue(); // shortcut for walkValue() + + switch ($comparison->getOperator()) { + case Comparison::EQ: + return function ($object) use ($field, $value) : bool { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) === $value; + }; + + case Comparison::NEQ: + return function ($object) use ($field, $value) : bool { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) !== $value; + }; + + case Comparison::LT: + return function ($object) use ($field, $value) : bool { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) < $value; + }; + + case Comparison::LTE: + return function ($object) use ($field, $value) : bool { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) <= $value; + }; + + case Comparison::GT: + return function ($object) use ($field, $value) : bool { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) > $value; + }; + + case Comparison::GTE: + return function ($object) use ($field, $value) : bool { + return ClosureExpressionVisitor::getObjectFieldValue($object, $field) >= $value; + }; + + case Comparison::IN: + return function ($object) use ($field, $value) : bool { + return in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value, true); + }; + + case Comparison::NIN: + return function ($object) use ($field, $value) : bool { + return ! in_array(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value, true); + }; + + case Comparison::CONTAINS: + return function ($object) use ($field, $value) { + return false !== strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::MEMBER_OF: + return function ($object) use ($field, $value) : bool { + $fieldValues = ClosureExpressionVisitor::getObjectFieldValue($object, $field); + if (!is_array($fieldValues)) { + $fieldValues = iterator_to_array($fieldValues); + } + return in_array($value, $fieldValues, true); + }; + + case Comparison::STARTS_WITH: + return function ($object) use ($field, $value) : bool { + return 0 === strpos(ClosureExpressionVisitor::getObjectFieldValue($object, $field), $value); + }; + + case Comparison::ENDS_WITH: + return function ($object) use ($field, $value) : bool { + return $value === substr(ClosureExpressionVisitor::getObjectFieldValue($object, $field), -strlen($value)); + }; + + + default: + throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + } + } + + /** + * {@inheritDoc} + */ + public function walkValue(Value $value) + { + return $value->getValue(); + } + + /** + * {@inheritDoc} + */ + public function walkCompositeExpression(CompositeExpression $expr) + { + $expressionList = []; + + foreach ($expr->getExpressionList() as $child) { + $expressionList[] = $this->dispatch($child); + } + + switch($expr->getType()) { + case CompositeExpression::TYPE_AND: + return $this->andExpressions($expressionList); + + case CompositeExpression::TYPE_OR: + return $this->orExpressions($expressionList); + + default: + throw new \RuntimeException("Unknown composite " . $expr->getType()); + } + } + + /** + * @param array $expressions + * + * @return callable + */ + private function andExpressions(array $expressions) : callable + { + return function ($object) use ($expressions) : bool { + foreach ($expressions as $expression) { + if ( ! $expression($object)) { + return false; + } + } + + return true; + }; + } + + /** + * @param array $expressions + * + * @return callable + */ + private function orExpressions(array $expressions) : callable + { + return function ($object) use ($expressions) : bool { + foreach ($expressions as $expression) { + if ($expression($object)) { + return true; + } + } + + return false; + }; + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php new file mode 100644 index 0000000..72fa5eb --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Comparison.php @@ -0,0 +1,106 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Comparison of a field with a value by the given operator. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class Comparison implements Expression +{ + const EQ = '='; + const NEQ = '<>'; + const LT = '<'; + const LTE = '<='; + const GT = '>'; + const GTE = '>='; + const IS = '='; // no difference with EQ + const IN = 'IN'; + const NIN = 'NIN'; + const CONTAINS = 'CONTAINS'; + const MEMBER_OF = 'MEMBER_OF'; + const STARTS_WITH = 'STARTS_WITH'; + const ENDS_WITH = 'ENDS_WITH'; + + /** + * @var string + */ + private $field; + + /** + * @var string + */ + private $op; + + /** + * @var Value + */ + private $value; + + /** + * @param string $field + * @param string $operator + * @param mixed $value + */ + public function __construct($field, $operator, $value) + { + if ( ! ($value instanceof Value)) { + $value = new Value($value); + } + + $this->field = $field; + $this->op = $operator; + $this->value = $value; + } + + /** + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * @return Value + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function getOperator() + { + return $this->op; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkComparison($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php new file mode 100644 index 0000000..2879754 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/CompositeExpression.php @@ -0,0 +1,90 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression of Expressions combined by AND or OR operation. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class CompositeExpression implements Expression +{ + const TYPE_AND = 'AND'; + const TYPE_OR = 'OR'; + + /** + * @var string + */ + private $type; + + /** + * @var Expression[] + */ + private $expressions = []; + + /** + * @param string $type + * @param array $expressions + * + * @throws \RuntimeException + */ + public function __construct($type, array $expressions) + { + $this->type = $type; + + foreach ($expressions as $expr) { + if ($expr instanceof Value) { + throw new \RuntimeException("Values are not supported expressions as children of and/or expressions."); + } + if ( ! ($expr instanceof Expression)) { + throw new \RuntimeException("No expression given to CompositeExpression."); + } + + $this->expressions[] = $expr; + } + } + + /** + * Returns the list of expressions nested in this composite. + * + * @return Expression[] + */ + public function getExpressionList() + { + return $this->expressions; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkCompositeExpression($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php new file mode 100644 index 0000000..68db767 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Expression.php @@ -0,0 +1,35 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * Expression for the {@link Selectable} interface. + * + * @author Benjamin Eberlei + */ +interface Expression +{ + /** + * @param ExpressionVisitor $visitor + * + * @return mixed + */ + public function visit(ExpressionVisitor $visitor); +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php new file mode 100644 index 0000000..080afdc --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/ExpressionVisitor.php @@ -0,0 +1,82 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +/** + * An Expression visitor walks a graph of expressions and turns them into a + * query for the underlying implementation. + * + * @author Benjamin Eberlei + */ +abstract class ExpressionVisitor +{ + /** + * Converts a comparison expression into the target query language output. + * + * @param Comparison $comparison + * + * @return mixed + */ + abstract public function walkComparison(Comparison $comparison); + + /** + * Converts a value expression into the target query language part. + * + * @param Value $value + * + * @return mixed + */ + abstract public function walkValue(Value $value); + + /** + * Converts a composite expression into the target query language output. + * + * @param CompositeExpression $expr + * + * @return mixed + */ + abstract public function walkCompositeExpression(CompositeExpression $expr); + + /** + * Dispatches walking an expression to the appropriate handler. + * + * @param Expression $expr + * + * @return mixed + * + * @throws \RuntimeException + */ + public function dispatch(Expression $expr) + { + switch (true) { + case ($expr instanceof Comparison): + return $this->walkComparison($expr); + + case ($expr instanceof Value): + return $this->walkValue($expr); + + case ($expr instanceof CompositeExpression): + return $this->walkCompositeExpression($expr); + + default: + throw new \RuntimeException("Unknown Expression " . get_class($expr)); + } + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php new file mode 100644 index 0000000..7f6e831 --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php @@ -0,0 +1,52 @@ +. + */ + +namespace Doctrine\Common\Collections\Expr; + +class Value implements Expression +{ + /** + * @var mixed + */ + private $value; + + /** + * @param mixed $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * {@inheritDoc} + */ + public function visit(ExpressionVisitor $visitor) + { + return $visitor->walkValue($this); + } +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php new file mode 100644 index 0000000..1a44a7b --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php @@ -0,0 +1,200 @@ +. + */ + +namespace Doctrine\Common\Collections; + +use Doctrine\Common\Collections\Expr\Comparison; +use Doctrine\Common\Collections\Expr\CompositeExpression; +use Doctrine\Common\Collections\Expr\Value; + +/** + * Builder for Expressions in the {@link Selectable} interface. + * + * Important Notice for interoperable code: You have to use scalar + * values only for comparisons, otherwise the behavior of the comparison + * may be different between implementations (Array vs ORM vs ODM). + * + * @author Benjamin Eberlei + * @since 2.3 + */ +class ExpressionBuilder +{ + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function andX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args()); + } + + /** + * @param mixed $x + * + * @return CompositeExpression + */ + public function orX($x = null) + { + return new CompositeExpression(CompositeExpression::TYPE_OR, func_get_args()); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function eq($field, $value) + { + return new Comparison($field, Comparison::EQ, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gt($field, $value) + { + return new Comparison($field, Comparison::GT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lt($field, $value) + { + return new Comparison($field, Comparison::LT, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function gte($field, $value) + { + return new Comparison($field, Comparison::GTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function lte($field, $value) + { + return new Comparison($field, Comparison::LTE, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function neq($field, $value) + { + return new Comparison($field, Comparison::NEQ, new Value($value)); + } + + /** + * @param string $field + * + * @return Comparison + */ + public function isNull($field) + { + return new Comparison($field, Comparison::EQ, new Value(null)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function in($field, array $values) + { + return new Comparison($field, Comparison::IN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $values + * + * @return Comparison + */ + public function notIn($field, array $values) + { + return new Comparison($field, Comparison::NIN, new Value($values)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function contains($field, $value) + { + return new Comparison($field, Comparison::CONTAINS, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function memberOf ($field, $value) + { + return new Comparison($field, Comparison::MEMBER_OF, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function startsWith($field, $value) + { + return new Comparison($field, Comparison::STARTS_WITH, new Value($value)); + } + + /** + * @param string $field + * @param mixed $value + * + * @return Comparison + */ + public function endsWith($field, $value) + { + return new Comparison($field, Comparison::ENDS_WITH, new Value($value)); + } + +} diff --git a/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php new file mode 100644 index 0000000..57660ed --- /dev/null +++ b/vendor/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php @@ -0,0 +1,48 @@ +. + */ + +namespace Doctrine\Common\Collections; + +/** + * Interface for collections that allow efficient filtering with an expression API. + * + * Goal of this interface is a backend independent method to fetch elements + * from a collections. {@link Expression} is crafted in a way that you can + * implement queries from both in-memory and database-backed collections. + * + * For database backed collections this allows very efficient access by + * utilizing the query APIs, for example SQL in the ORM. Applications using + * this API can implement efficient database access without having to ask the + * EntityManager or Repositories. + * + * @author Benjamin Eberlei + * @since 2.3 + */ +interface Selectable +{ + /** + * Selects all elements from a selectable that match the expression and + * returns a new collection containing these elements. + * + * @param Criteria $criteria + * + * @return Collection + */ + public function matching(Criteria $criteria); +} diff --git a/vendor/doctrine/common/LICENSE b/vendor/doctrine/common/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/common/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/common/README.md b/vendor/doctrine/common/README.md new file mode 100644 index 0000000..5cffb1a --- /dev/null +++ b/vendor/doctrine/common/README.md @@ -0,0 +1,11 @@ +# Doctrine Common + +[![Build Status](https://secure.travis-ci.org/doctrine/common.png)](https://travis-ci.org/doctrine/common) + +The Doctrine Common project is a library that provides extensions to core PHP functionality. + +## More resources: + +* [Website](https://www.doctrine-project.org/) +* [Documentation](https://www.doctrine-project.org/projects/doctrine-common/en/latest/) +* [Downloads](https://github.com/doctrine/common/releases) diff --git a/vendor/doctrine/common/UPGRADE_TO_2_1 b/vendor/doctrine/common/UPGRADE_TO_2_1 new file mode 100644 index 0000000..891a2e5 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_1 @@ -0,0 +1,39 @@ +This document details all the possible changes that you should investigate when updating +your project from Doctrine Common 2.0.x to 2.1 + +## AnnotationReader changes + +The annotation reader was heavily refactored between 2.0 and 2.1-RC1. In theory the operation of the new reader should be backwards compatible, but it has to be setup differently to work that way: + + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + // new code necessary starting here + $reader->setIgnoreNotImportedAnnotations(true); + $reader->setEnableParsePhpImports(false); + $reader = new \Doctrine\Common\Annotations\CachedReader( + new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache() + ); + +## Annotation Base class or @Annotation + +Beginning after 2.1-RC2 you have to either extend ``Doctrine\Common\Annotations\Annotation`` or add @Annotation to your annotations class-level docblock, otherwise the class will simply be ignored. + +## Removed methods on AnnotationReader + +* AnnotationReader::setAutoloadAnnotations() +* AnnotationReader::getAutoloadAnnotations() +* AnnotationReader::isAutoloadAnnotations() + +## AnnotationRegistry + +Autoloading through the PHP autoloader is removed from the 2.1 AnnotationReader. Instead you have to use the global AnnotationRegistry for loading purposes: + + \Doctrine\Common\Annotations\AnnotationRegistry::registerFile($fileWithAnnotations); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace($namespace, $dirs = null); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespaces($namespaces); + \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader($callable); + +The $callable for registering a loader accepts a class as first and only parameter and must try to silently autoload it. On success true has to be returned. +The registerAutoloadNamespace function registers a PSR-0 compatible silent autoloader for all classes with the given namespace in the given directories. +If null is passed as directory the include path will be used. + diff --git a/vendor/doctrine/common/UPGRADE_TO_2_2 b/vendor/doctrine/common/UPGRADE_TO_2_2 new file mode 100644 index 0000000..1d93a13 --- /dev/null +++ b/vendor/doctrine/common/UPGRADE_TO_2_2 @@ -0,0 +1,61 @@ +This document details all the possible changes that you should investigate when +updating your project from Doctrine Common 2.1 to 2.2: + +## Annotation Changes + +- AnnotationReader::setIgnoreNotImportedAnnotations has been removed, you need to + add ignore annotation names which are supposed to be ignored via + AnnotationReader::addGlobalIgnoredName + +- AnnotationReader::setAutoloadAnnotations was deprecated by the AnnotationRegistry + in 2.1 and has been removed in 2.2 + +- AnnotationReader::setEnableParsePhpImports was added to ease transition to the new + annotation mechanism in 2.1 and is removed in 2.2 + +- AnnotationReader::isParsePhpImportsEnabled is removed (see above) + +- AnnotationReader::setDefaultAnnotationNamespace was deprecated in favor of explicit + configuration in 2.1 and will be removed in 2.2 (for isolated projects where you + have full-control over _all_ available annotations, we offer a dedicated reader + class ``SimpleAnnotationReader``) + +- AnnotationReader::setAnnotationCreationFunction was deprecated in 2.1 and will be + removed in 2.2. We only offer two creation mechanisms which cannot be changed + anymore to allow the same reader instance to work with all annotations regardless + of which library they are coming from. + +- AnnotationReader::setAnnotationNamespaceAlias was deprecated in 2.1 and will be + removed in 2.2 (see setDefaultAnnotationNamespace) + +- If you use a class as annotation which has not the @Annotation marker in it's + class block, we will now throw an exception instead of silently ignoring it. You + can however still achieve the previous behavior using the @IgnoreAnnotation, or + AnnotationReader::addGlobalIgnoredName (the exception message will contain detailed + instructions when you run into this problem). + +## Cache Changes + +- Renamed old AbstractCache to CacheProvider + +- Dropped the support to the following functions of all cache providers: + + - CacheProvider::deleteByWildcard + + - CacheProvider::deleteByRegEx + + - CacheProvider::deleteByPrefix + + - CacheProvider::deleteBySuffix + +- CacheProvider::deleteAll will not remove ALL entries, it will only mark them as invalid + +- CacheProvider::flushAll will remove ALL entries, namespaced or not + +- Added support to MemcachedCache + +- Added support to WincacheCache + +## ClassLoader Changes + +- ClassLoader::fileExistsInIncludePath() no longer exists. Use the native stream_resolve_include_path() PHP function \ No newline at end of file diff --git a/vendor/doctrine/common/composer.json b/vendor/doctrine/common/composer.json new file mode 100644 index 0000000..c3cd736 --- /dev/null +++ b/vendor/doctrine/common/composer.json @@ -0,0 +1,48 @@ +{ + "name": "doctrine/common", + "type": "library", + "description": "Common Library for Doctrine projects", + "keywords": ["collections", "spl", "eventmanager", "annotations", "persistence"], + "homepage": "https://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}, + {"name": "Marco Pivetta", "email": "ocramius@gmail.com"} + ], + "require": { + "php": "^7.1", + "doctrine/inflector": "^1.0", + "doctrine/cache": "^1.0", + "doctrine/collections": "^1.0", + "doctrine/lexer": "^1.0", + "doctrine/annotations": "^1.0", + "doctrine/event-manager": "^1.0", + "doctrine/reflection": "^1.0", + "doctrine/persistence": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^6.3", + "doctrine/coding-standard": "^1.0", + "squizlabs/php_codesniffer": "^3.0", + "symfony/phpunit-bridge": "^4.0.5" + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "autoload-dev": { + "psr-4": { + "Doctrine\\Tests\\": "tests/Doctrine/Tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.9.x-dev" + } + } +} diff --git a/vendor/doctrine/common/docs/en/index.rst b/vendor/doctrine/common/docs/en/index.rst new file mode 100644 index 0000000..7550d8b --- /dev/null +++ b/vendor/doctrine/common/docs/en/index.rst @@ -0,0 +1,10 @@ +Common Documentation +==================== + +Welcome to the Doctrine Common Library documentation. + +.. toctree:: + :depth: 2 + :glob: + + * diff --git a/vendor/doctrine/common/docs/en/reference/class-loading.rst b/vendor/doctrine/common/docs/en/reference/class-loading.rst new file mode 100644 index 0000000..73c285d --- /dev/null +++ b/vendor/doctrine/common/docs/en/reference/class-loading.rst @@ -0,0 +1,242 @@ +Class Loading +============= + +Class loading is an essential part of any PHP application that +makes heavy use of classes and interfaces. Unfortunately, a lot of +people and projects spend a lot of time and effort on custom and +specialized class loading strategies. It can quickly become a pain +to understand what is going on when using multiple libraries and/or +frameworks, each with its own way to do class loading. Class +loading should be simple and it is an ideal candidate for +convention over configuration. + +Overview +-------- + +The Doctrine Common ClassLoader implements a simple and efficient +approach to class loading that is easy to understand and use. The +implementation is based on the widely used and accepted convention +of mapping namespace and class names to a directory structure. This +approach is used for example by Symfony2, the Zend Framework and of +course, Doctrine. + +For example, the following class: + +.. code-block :: php + + register(); + $dbalLoader->register(); + $ormLoader->register(); + +Do not be afraid of using multiple class loaders. Due to the +efficient class loading design you will not incur much overhead +from using many class loaders. Take a look at the implementation of +``ClassLoader#loadClass`` to see how simple and efficient the class +loading is. The iteration over the installed class loaders happens +in C (with the exception of using ``ClassLoader::classExists``). + +A ClassLoader can be used in the following other variations, +however, these are rarely used/needed: + + +- If only the second argument is not supplied, the class loader + will be responsible for the namespace prefix given in the first + argument and it will rely on the PHP include_path. + +- If only the first argument is not supplied, the class loader + will be responsible for *all* classes and it will try to look up + *all* classes starting at the directory given as the second + argument. + +- If both arguments are not supplied, the class loader will be + responsible for *all* classes and it will rely on the PHP + include_path. + + +File Extension +-------------- + +By default, a ClassLoader uses the ``.php`` file extension for all +class files. You can change this behavior, for example to use a +ClassLoader to load classes from a library that uses the +".class.php" convention (but it must nevertheless adhere to the +directory structure convention!): + +.. code-block :: php + + setFileExtension('.class.php'); + $customLoader->register(); + +Namespace Separator +------------------- + +By default, a ClassLoader uses the ``\`` namespace separator. You +can change this behavior, for example to use a ClassLoader to load +legacy Zend Framework classes that still use the underscore "_" +separator: + +.. code-block :: php + + setNamespaceSeparator('_'); + $zend1Loader->register(); + +Failing Silently and class_exists +---------------------------------- + +A lot of class/autoloaders these days try to fail silently when a +class file is not found. For the most part this is necessary in +order to support using ``class_exists('ClassName', true)`` which is +supposed to return a boolean value but triggers autoloading. This +is a bad thing as it basically forces class loaders to fail +silently, which in turn requires costly file_exists or fopen calls +for each class being loaded, even though in at least 99% of the +cases this is not necessary (compare the number of +class_exists(..., true) invocations to the total number of classes +being loaded in a request). + +The Doctrine Common ClassLoader does not fail silently, by design. +It therefore does not need any costly checks for file existence. A +ClassLoader is always responsible for all classes with a certain +namespace prefix and if a class is requested to be loaded and can +not be found this is considered to be a fatal error. This also +means that using class_exists(..., true) to check for class +existence when using a Doctrine Common ClassLoader is not possible +but this is not a bad thing. What class\_exists(..., true) actually +means is two things: 1) Check whether the class is already +defined/exists (i.e. class_exists(..., false)) and if not 2) check +whether a class file can be loaded for that class. In the Doctrine +Common ClassLoader the two responsibilities of loading a class and +checking for its existence are separated, which can be observed by +the existence of the two methods ``loadClass`` and +``canLoadClass``. Thereby ``loadClass`` does not invoke +``canLoadClass`` internally, by design. However, you are free to +use it yourself to check whether a class can be loaded and the +following code snippet is thus equivalent to class\_exists(..., +true): + +.. code-block :: php + + canLoadClass('Foo')) { + // ... + } + +The only problem with this is that it is inconvenient as you need +to have a reference to the class loaders around (and there are +often multiple class loaders in use). Therefore, a simpler +alternative exists for the cases in which you really want to ask +all installed class loaders whether they can load the class: +``ClassLoader::classExists($className)``: + +.. code-block :: php + + ClassLoader is an autoloader for class files that can be + * installed on the SPL autoload stack. It is a class loader that either loads only classes + * of a specific namespace or all namespaces and it is suitable for working together + * with other autoloaders in the SPL autoload stack. + * + * If no include path is configured through the constructor or {@link setIncludePath}, a ClassLoader + * relies on the PHP include_path. + * + * @author Roman Borschel + * @since 2.0 + * + * @deprecated The ClassLoader is deprecated and will be removed in version 3.0 of doctrine/common. + */ +class ClassLoader +{ + /** + * PHP file extension. + * + * @var string + */ + protected $fileExtension = '.php'; + + /** + * Current namespace. + * + * @var string|null + */ + protected $namespace; + + /** + * Current include path. + * + * @var string|null + */ + protected $includePath; + + /** + * PHP namespace separator. + * + * @var string + */ + protected $namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace from the specified include path. + * + * If no include path is given, the ClassLoader relies on the PHP include_path. + * If neither a namespace nor an include path is given, the ClassLoader will + * be responsible for loading all classes, thereby relying on the PHP include_path. + * + * @param string|null $ns The namespace of the classes to load. + * @param string|null $includePath The base include path to use. + */ + public function __construct($ns = null, $includePath = null) + { + $this->namespace = $ns; + $this->includePath = $includePath; + } + + /** + * Sets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @param string $sep The separator to use. + * + * @return void + */ + public function setNamespaceSeparator($sep) + { + $this->namespaceSeparator = $sep; + } + + /** + * Gets the namespace separator used by classes in the namespace of this ClassLoader. + * + * @return string + */ + public function getNamespaceSeparator() + { + return $this->namespaceSeparator; + } + + /** + * Sets the base include path for all class files in the namespace of this ClassLoader. + * + * @param string|null $includePath + * + * @return void + */ + public function setIncludePath($includePath) + { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string|null + */ + public function getIncludePath() + { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Registers this ClassLoader on the SPL autoload stack. + * + * @return void + */ + public function register() + { + spl_autoload_register([$this, 'loadClass']); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + * + * @return void + */ + public function unregister() + { + spl_autoload_unregister([$this, 'loadClass']); + } + + /** + * Loads the given class or interface. + * + * @param string $className The name of the class to load. + * + * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. + */ + public function loadClass($className) + { + if (self::typeExists($className)) { + return true; + } + + if ( ! $this->canLoadClass($className)) { + return false; + } + + require($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') + . str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) + . $this->fileExtension; + + return self::typeExists($className); + } + + /** + * Asks this ClassLoader whether it can potentially load the class (file) with + * the given name. + * + * @param string $className The fully-qualified name of the class. + * + * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. + */ + public function canLoadClass($className) + { + if ($this->namespace !== null && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { + return false; + } + + $file = str_replace($this->namespaceSeparator, DIRECTORY_SEPARATOR, $className) . $this->fileExtension; + + if ($this->includePath !== null) { + return is_file($this->includePath . DIRECTORY_SEPARATOR . $file); + } + + return (false !== stream_resolve_include_path($file)); + } + + /** + * Checks whether a class with a given name exists. A class "exists" if it is either + * already defined in the current request or if there is an autoloader on the SPL + * autoload stack that is a) responsible for the class in question and b) is able to + * load a class file in which the class definition resides. + * + * If the class is not already defined, each autoloader in the SPL autoload stack + * is asked whether it is able to tell if the class exists. If the autoloader is + * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload + * function of the autoloader is invoked and expected to return a value that + * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports + * that the class exists, TRUE is returned. + * + * Note that, depending on what kinds of autoloaders are installed on the SPL + * autoload stack, the class (file) might already be loaded as a result of checking + * for its existence. This is not the case with a ClassLoader, who separates + * these responsibilities. + * + * @param string $className The fully-qualified name of the class. + * + * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. + */ + public static function classExists($className) + { + return self::typeExists($className, true); + } + + /** + * Gets the ClassLoader from the SPL autoload stack that is responsible + * for (and is able to load) the class with the given name. + * + * @param string $className The name of the class. + * + * @return ClassLoader|null The ClassLoader for the class or NULL if no such ClassLoader exists. + */ + public static function getClassLoader($className) + { + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader) + && ($classLoader = reset($loader)) + && $classLoader instanceof ClassLoader + && $classLoader->canLoadClass($className) + ) { + return $classLoader; + } + } + + return null; + } + + /** + * Checks whether a given type exists + * + * @param string $type + * @param bool $autoload + * + * @return bool + */ + private static function typeExists($type, $autoload = false) + { + return class_exists($type, $autoload) + || interface_exists($type, $autoload) + || trait_exists($type, $autoload); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php new file mode 100644 index 0000000..bf60621 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/CommonException.php @@ -0,0 +1,13 @@ + + * @author Guilherme Blanco + */ +interface Comparable +{ + /** + * Compares the current object to the passed $other. + * + * Returns 0 if they are semantically equal, 1 if the other object + * is less than the current one, or -1 if its more than the current one. + * + * This method should not check for identity using ===, only for semantical equality for example + * when two different DateTime instances point to the exact same Date + TZ. + * + * @param mixed $other + * + * @return int + */ + public function compareTo($other); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php new file mode 100644 index 0000000..8f92c26 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Lexer.php @@ -0,0 +1,25 @@ + + * @author Jonathan Wage + * @author Roman Borschel + * + * @deprecated Use Doctrine\Common\Lexer\AbstractLexer from doctrine/lexer package instead. + */ +abstract class Lexer extends AbstractLexer +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php new file mode 100644 index 0000000..63b1d6f --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/NotifyPropertyChanged.php @@ -0,0 +1,24 @@ + + * @author Jonathan Wage + * @author Roman Borschel + */ +interface NotifyPropertyChanged +{ + /** + * Adds a listener that wants to be notified about property changes. + * + * @param PropertyChangedListener $listener + * + * @return void + */ + public function addPropertyChangedListener(PropertyChangedListener $listener); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php new file mode 100644 index 0000000..940f00e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/PropertyChangedListener.php @@ -0,0 +1,27 @@ +NotifyPropertyChanged + * implementor. + * + * @link www.doctrine-project.org + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +interface PropertyChangedListener +{ + /** + * Notifies the listener of a property change. + * + * @param object $sender The object on which the property changed. + * @param string $propertyName The name of the property that changed. + * @param mixed $oldValue The old value of the property that changed. + * @param mixed $newValue The new value of the property that changed. + * + * @return void + */ + public function propertyChanged($sender, $propertyName, $oldValue, $newValue); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php new file mode 100644 index 0000000..00879fc --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/AbstractProxyFactory.php @@ -0,0 +1,245 @@ + + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +abstract class AbstractProxyFactory +{ + /** + * Never autogenerate a proxy and rely that it was generated by some + * process before deployment. + * + * @var integer + */ + const AUTOGENERATE_NEVER = 0; + + /** + * Always generates a new proxy in every request. + * + * This is only sane during development. + * + * @var integer + */ + const AUTOGENERATE_ALWAYS = 1; + + /** + * Autogenerate the proxy class when the proxy file does not exist. + * + * This strategy causes a file exists call whenever any proxy is used the + * first time in a request. + * + * @var integer + */ + const AUTOGENERATE_FILE_NOT_EXISTS = 2; + + /** + * Generate the proxy classes using eval(). + * + * This strategy is only sane for development, and even then it gives me + * the creeps a little. + * + * @var integer + */ + const AUTOGENERATE_EVAL = 3; + + private const AUTOGENERATE_MODES = [ + self::AUTOGENERATE_NEVER, + self::AUTOGENERATE_ALWAYS, + self::AUTOGENERATE_FILE_NOT_EXISTS, + self::AUTOGENERATE_EVAL, + ]; + + /** + * @var \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory + */ + private $metadataFactory; + + /** + * @var \Doctrine\Common\Proxy\ProxyGenerator the proxy generator responsible for creating the proxy classes/files. + */ + private $proxyGenerator; + + /** + * @var int Whether to automatically (re)generate proxy classes. + */ + private $autoGenerate; + + /** + * @var \Doctrine\Common\Proxy\ProxyDefinition[] + */ + private $definitions = []; + + /** + * @param \Doctrine\Common\Proxy\ProxyGenerator $proxyGenerator + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadataFactory $metadataFactory + * @param bool|int $autoGenerate + * + * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException When auto generate mode is not valid. + */ + public function __construct(ProxyGenerator $proxyGenerator, ClassMetadataFactory $metadataFactory, $autoGenerate) + { + $this->proxyGenerator = $proxyGenerator; + $this->metadataFactory = $metadataFactory; + $this->autoGenerate = (int) $autoGenerate; + + if ( ! in_array($this->autoGenerate, self::AUTOGENERATE_MODES, true)) { + throw InvalidArgumentException::invalidAutoGenerateMode($autoGenerate); + } + } + + /** + * Gets a reference proxy instance for the entity of the given type and identified by + * the given identifier. + * + * @param string $className + * @param array $identifier + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\OutOfBoundsException + */ + public function getProxy($className, array $identifier) + { + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + $fqcn = $definition->proxyClassName; + $proxy = new $fqcn($definition->initializer, $definition->cloner); + + foreach ($definition->identifierFields as $idField) { + if ( ! isset($identifier[$idField])) { + throw OutOfBoundsException::missingPrimaryKeyValue($className, $idField); + } + + $definition->reflectionFields[$idField]->setValue($proxy, $identifier[$idField]); + } + + return $proxy; + } + + /** + * Generates proxy classes for all given classes. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata[] $classes The classes (ClassMetadata instances) + * for which to generate proxies. + * @param string $proxyDir The target directory of the proxy classes. If not specified, the + * directory configured on the Configuration of the EntityManager used + * by this factory is used. + * @return int Number of generated proxies. + */ + public function generateProxyClasses(array $classes, $proxyDir = null) + { + $generated = 0; + + foreach ($classes as $class) { + if ($this->skipClass($class)) { + continue; + } + + $proxyFileName = $this->proxyGenerator->getProxyFileName($class->getName(), $proxyDir); + + $this->proxyGenerator->generateProxyClass($class, $proxyFileName); + + $generated += 1; + } + + return $generated; + } + + /** + * Reset initialization/cloning logic for an un-initialized proxy + * + * @param \Doctrine\Common\Proxy\Proxy $proxy + * + * @return \Doctrine\Common\Proxy\Proxy + * + * @throws \Doctrine\Common\Proxy\Exception\InvalidArgumentException + */ + public function resetUninitializedProxy(Proxy $proxy) + { + if ($proxy->__isInitialized()) { + throw InvalidArgumentException::unitializedProxyExpected($proxy); + } + + $className = ClassUtils::getClass($proxy); + $definition = isset($this->definitions[$className]) + ? $this->definitions[$className] + : $this->getProxyDefinition($className); + + $proxy->__setInitializer($definition->initializer); + $proxy->__setCloner($definition->cloner); + + return $proxy; + } + + /** + * Get a proxy definition for the given class name. + * + * @param string $className + * + * @return ProxyDefinition + */ + private function getProxyDefinition($className) + { + $classMetadata = $this->metadataFactory->getMetadataFor($className); + $className = $classMetadata->getName(); // aliases and case sensitivity + + $this->definitions[$className] = $this->createProxyDefinition($className); + $proxyClassName = $this->definitions[$className]->proxyClassName; + + if ( ! class_exists($proxyClassName, false)) { + $fileName = $this->proxyGenerator->getProxyFileName($className); + + switch ($this->autoGenerate) { + case self::AUTOGENERATE_NEVER: + require $fileName; + break; + + case self::AUTOGENERATE_FILE_NOT_EXISTS: + if ( ! file_exists($fileName)) { + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + } + require $fileName; + break; + + case self::AUTOGENERATE_ALWAYS: + $this->proxyGenerator->generateProxyClass($classMetadata, $fileName); + require $fileName; + break; + + case self::AUTOGENERATE_EVAL: + $this->proxyGenerator->generateProxyClass($classMetadata, false); + break; + } + } + + return $this->definitions[$className]; + } + + /** + * Determine if this class should be skipped during proxy generation. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $metadata + * + * @return bool + */ + abstract protected function skipClass(ClassMetadata $metadata); + + /** + * @param string $className + * + * @return ProxyDefinition + */ + abstract protected function createProxyDefinition($className); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php new file mode 100644 index 0000000..f5a08d7 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Autoloader.php @@ -0,0 +1,82 @@ + + * + * @internal + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +class Autoloader +{ + /** + * Resolves proxy class name to a filename based on the following pattern. + * + * 1. Remove Proxy namespace from class name. + * 2. Remove namespace separators from remaining class name. + * 3. Return PHP filename from proxy-dir with the result from 2. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param string $className + * + * @return string + * + * @throws InvalidArgumentException + */ + public static function resolveFile($proxyDir, $proxyNamespace, $className) + { + if (0 !== strpos($className, $proxyNamespace)) { + throw InvalidArgumentException::notProxyClass($className, $proxyNamespace); + } + + // remove proxy namespace from class name + $classNameRelativeToProxyNamespace = substr($className, strlen($proxyNamespace)); + + // remove namespace separators from remaining class name + $fileName = str_replace('\\', '', $classNameRelativeToProxyNamespace); + + return $proxyDir . DIRECTORY_SEPARATOR . $fileName . '.php'; + } + + /** + * Registers and returns autoloader callback for the given proxy dir and namespace. + * + * @param string $proxyDir + * @param string $proxyNamespace + * @param callable|null $notFoundCallback Invoked when the proxy file is not found. + * + * @return \Closure + * + * @throws InvalidArgumentException + */ + public static function register($proxyDir, $proxyNamespace, $notFoundCallback = null) + { + $proxyNamespace = ltrim($proxyNamespace, '\\'); + + if ( ! (null === $notFoundCallback || is_callable($notFoundCallback))) { + throw InvalidArgumentException::invalidClassNotFoundCallback($notFoundCallback); + } + + $autoloader = function ($className) use ($proxyDir, $proxyNamespace, $notFoundCallback) { + if (0 === strpos($className, $proxyNamespace)) { + $file = Autoloader::resolveFile($proxyDir, $proxyNamespace, $className); + + if ($notFoundCallback && ! file_exists($file)) { + call_user_func($notFoundCallback, $proxyDir, $proxyNamespace, $className); + } + + require $file; + } + }; + + spl_autoload_register($autoloader); + + return $autoloader; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..bab3d70 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/InvalidArgumentException.php @@ -0,0 +1,106 @@ + + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +class InvalidArgumentException extends BaseInvalidArgumentException implements ProxyException +{ + /** + * @return self + */ + public static function proxyDirectoryRequired() + { + return new self('You must configure a proxy directory. See docs for details'); + } + + /** + * @param string $className + * @param string $proxyNamespace + * + * @return self + */ + public static function notProxyClass($className, $proxyNamespace) + { + return new self(sprintf('The class "%s" is not part of the proxy namespace "%s"', $className, $proxyNamespace)); + } + + /** + * @param string $name + * + * @return self + */ + public static function invalidPlaceholder($name) + { + return new self(sprintf('Provided placeholder for "%s" must be either a string or a valid callable', $name)); + } + + /** + * @return self + */ + public static function proxyNamespaceRequired() + { + return new self('You must configure a proxy namespace'); + } + + /** + * @param Proxy $proxy + * + * @return self + */ + public static function unitializedProxyExpected(Proxy $proxy) + { + return new self(sprintf('Provided proxy of type "%s" must not be initialized.', get_class($proxy))); + } + + /** + * @param mixed $callback + * + * @return self + */ + public static function invalidClassNotFoundCallback($callback) + { + $type = is_object($callback) ? get_class($callback) : gettype($callback); + + return new self(sprintf('Invalid \$notFoundCallback given: must be a callable, "%s" given', $type)); + } + + /** + * @param string $className + * + * @return self + */ + public static function classMustNotBeAbstract($className) + { + return new self(sprintf('Unable to create a proxy for an abstract class "%s".', $className)); + } + + /** + * @param string $className + * + * @return self + */ + public static function classMustNotBeFinal($className) + { + return new self(sprintf('Unable to create a proxy for a final class "%s".', $className)); + } + + /** + * @param mixed $value + * + * @return self + */ + public static function invalidAutoGenerateMode($value) : self + { + return new self(sprintf('Invalid auto generate mode "%s" given.', $value)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..df40268 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/OutOfBoundsException.php @@ -0,0 +1,26 @@ + + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +class OutOfBoundsException extends BaseOutOfBoundsException implements ProxyException +{ + /** + * @param string $className + * @param string $idField + * + * @return self + */ + public static function missingPrimaryKeyValue($className, $idField) + { + return new self(sprintf("Missing value for primary key %s on %s", $idField, $className)); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php new file mode 100644 index 0000000..751f9a9 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/ProxyException.php @@ -0,0 +1,15 @@ + + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +interface ProxyException +{ +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php new file mode 100644 index 0000000..cbe6d7e --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Exception/UnexpectedValueException.php @@ -0,0 +1,72 @@ + + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +class UnexpectedValueException extends BaseUnexpectedValueException implements ProxyException +{ + /** + * @param string $proxyDirectory + * + * @return self + */ + public static function proxyDirectoryNotWritable($proxyDirectory) + { + return new self(sprintf('Your proxy directory "%s" must be writable', $proxyDirectory)); + } + + /** + * @param string $className + * @param string $methodName + * @param string $parameterName + * @param \Exception|null $previous + * + * @return self + */ + public static function invalidParameterTypeHint( + $className, + $methodName, + $parameterName, + \Exception $previous = null + ) { + return new self( + sprintf( + 'The type hint of parameter "%s" in method "%s" in class "%s" is invalid.', + $parameterName, + $methodName, + $className + ), + 0, + $previous + ); + } + + /** + * @param $className + * @param $methodName + * @param \Exception|null $previous + * + * @return self + */ + public static function invalidReturnTypeHint($className, $methodName, \Exception $previous = null) + { + return new self( + sprintf( + 'The return type of method "%s" in class "%s" is invalid.', + $methodName, + $className + ), + 0, + $previous + ); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php new file mode 100644 index 0000000..1e76b71 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/Proxy.php @@ -0,0 +1,74 @@ + + * @author Marco Pivetta + * @since 2.4 + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +interface Proxy extends BaseProxy +{ + /** + * Marks the proxy as initialized or not. + * + * @param boolean $initialized + * + * @return void + */ + public function __setInitialized($initialized); + + /** + * Sets the initializer callback to be used when initializing the proxy. That + * initializer should accept 3 parameters: $proxy, $method and $params. Those + * are respectively the proxy object that is being initialized, the method name + * that triggered initialization and the parameters passed to that method. + * + * @param Closure|null $initializer + * + * @return void + */ + public function __setInitializer(Closure $initializer = null); + + /** + * Retrieves the initializer callback used to initialize the proxy. + * + * @see __setInitializer + * + * @return Closure|null + */ + public function __getInitializer(); + + /** + * Sets the callback to be used when cloning the proxy. That initializer should accept + * a single parameter, which is the cloned proxy instance itself. + * + * @param Closure|null $cloner + * + * @return void + */ + public function __setCloner(Closure $cloner = null); + + /** + * Retrieves the callback to be used when cloning the proxy. + * + * @see __setCloner + * + * @return Closure|null + */ + public function __getCloner(); + + /** + * Retrieves the list of lazy loaded properties for a given proxy + * + * @return array Keys are the property names, and values are the default values + * for those properties. + */ + public function __getLazyProperties(); +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php new file mode 100644 index 0000000..cb13cec --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyDefinition.php @@ -0,0 +1,53 @@ + + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +class ProxyDefinition +{ + /** + * @var string + */ + public $proxyClassName; + + /** + * @var array + */ + public $identifierFields; + + /** + * @var \ReflectionProperty[] + */ + public $reflectionFields; + + /** + * @var callable + */ + public $initializer; + + /** + * @var callable + */ + public $cloner; + + /** + * @param string $proxyClassName + * @param array $identifierFields + * @param array $reflectionFields + * @param callable $initializer + * @param callable $cloner + */ + public function __construct($proxyClassName, array $identifierFields, array $reflectionFields, $initializer, $cloner) + { + $this->proxyClassName = $proxyClassName; + $this->identifierFields = $identifierFields; + $this->reflectionFields = $reflectionFields; + $this->initializer = $initializer; + $this->cloner = $cloner; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php new file mode 100644 index 0000000..791c304 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Proxy/ProxyGenerator.php @@ -0,0 +1,1064 @@ + + * @since 2.4 + * + * @deprecated The Doctrine\Common\Proxy component is deprecated, please use ocramius/proxy-manager instead. + */ +class ProxyGenerator +{ + /** + * Used to match very simple id methods that don't need + * to be decorated since the identifier is known. + */ + const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*(?::\s*\??\s*\\\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*\s*)?{\s*return\s*\$this->%s;\s*})i'; + + /** + * The namespace that contains all proxy classes. + * + * @var string + */ + private $proxyNamespace; + + /** + * The directory that contains all proxy classes. + * + * @var string + */ + private $proxyDirectory; + + /** + * Map of callables used to fill in placeholders set in the template. + * + * @var string[]|callable[] + */ + protected $placeholders = [ + 'baseProxyInterface' => Proxy::class, + 'additionalProperties' => '', + ]; + + /** + * Template used as a blueprint to generate proxies. + * + * @var string + */ + protected $proxyClassTemplate = '; + +/** + * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR + */ +class extends \ implements \ +{ + /** + * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with + * three parameters, being respectively the proxy object to be initialized, the method that triggered the + * initialization process and an array of ordered parameters that were passed to that method. + * + * @see \Doctrine\Common\Persistence\Proxy::__setInitializer + */ + public $__initializer__; + + /** + * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object + * + * @see \Doctrine\Common\Persistence\Proxy::__setCloner + */ + public $__cloner__; + + /** + * @var boolean flag indicating if this object was already initialized + * + * @see \Doctrine\Common\Persistence\Proxy::__isInitialized + */ + public $__isInitialized__ = false; + + /** + * @var array properties to be lazy loaded, with keys being the property + * names and values being their default values + * + * @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties + */ + public static $lazyPropertiesDefaults = []; + + + + + + + + + + + + + + + + + + /** + * Forces initialization of the proxy + */ + public function __load() + { + $this->__initializer__ && $this->__initializer__->__invoke($this, \'__load\', []); + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __isInitialized() + { + return $this->__isInitialized__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitialized($initialized) + { + $this->__isInitialized__ = $initialized; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setInitializer(\Closure $initializer = null) + { + $this->__initializer__ = $initializer; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __getInitializer() + { + return $this->__initializer__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + */ + public function __setCloner(\Closure $cloner = null) + { + $this->__cloner__ = $cloner; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific cloning logic + */ + public function __getCloner() + { + return $this->__cloner__; + } + + /** + * {@inheritDoc} + * @internal generated method: use only when explicitly handling proxy specific loading logic + * @static + */ + public function __getLazyProperties() + { + return self::$lazyPropertiesDefaults; + } + + +} +'; + + /** + * Initializes a new instance of the ProxyFactory class that is + * connected to the given EntityManager. + * + * @param string $proxyDirectory The directory to use for the proxy classes. It must exist. + * @param string $proxyNamespace The namespace to use for the proxy classes. + * + * @throws InvalidArgumentException + */ + public function __construct($proxyDirectory, $proxyNamespace) + { + if ( ! $proxyDirectory) { + throw InvalidArgumentException::proxyDirectoryRequired(); + } + + if ( ! $proxyNamespace) { + throw InvalidArgumentException::proxyNamespaceRequired(); + } + + $this->proxyDirectory = $proxyDirectory; + $this->proxyNamespace = $proxyNamespace; + } + + /** + * Sets a placeholder to be replaced in the template. + * + * @param string $name + * @param string|callable $placeholder + * + * @throws InvalidArgumentException + */ + public function setPlaceholder($name, $placeholder) + { + if ( ! is_string($placeholder) && ! is_callable($placeholder)) { + throw InvalidArgumentException::invalidPlaceholder($name); + } + + $this->placeholders[$name] = $placeholder; + } + + /** + * Sets the base template used to create proxy classes. + * + * @param string $proxyClassTemplate + */ + public function setProxyClassTemplate($proxyClassTemplate) + { + $this->proxyClassTemplate = (string) $proxyClassTemplate; + } + + /** + * Generates a proxy class file. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class Metadata for the original class. + * @param string|bool $fileName Filename (full path) for the generated class. If none is given, eval() is used. + * + * @throws InvalidArgumentException + * @throws UnexpectedValueException + */ + public function generateProxyClass(ClassMetadata $class, $fileName = false) + { + $this->verifyClassCanBeProxied($class); + + preg_match_all('(<([a-zA-Z]+)>)', $this->proxyClassTemplate, $placeholderMatches); + + $placeholderMatches = array_combine($placeholderMatches[0], $placeholderMatches[1]); + $placeholders = []; + + foreach ($placeholderMatches as $placeholder => $name) { + $placeholders[$placeholder] = isset($this->placeholders[$name]) + ? $this->placeholders[$name] + : [$this, 'generate' . $name]; + } + + foreach ($placeholders as & $placeholder) { + if (is_callable($placeholder)) { + $placeholder = call_user_func($placeholder, $class); + } + } + + $proxyCode = strtr($this->proxyClassTemplate, $placeholders); + + if ( ! $fileName) { + $proxyClassName = $this->generateNamespace($class) . '\\' . $this->generateProxyShortClassName($class); + + if ( ! class_exists($proxyClassName)) { + eval(substr($proxyCode, 5)); + } + + return; + } + + $parentDirectory = dirname($fileName); + + if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + if ( ! is_writable($parentDirectory)) { + throw UnexpectedValueException::proxyDirectoryNotWritable($this->proxyDirectory); + } + + $tmpFileName = $fileName . '.' . uniqid('', true); + + file_put_contents($tmpFileName, $proxyCode); + @chmod($tmpFileName, 0664); + rename($tmpFileName, $fileName); + } + + /** + * @param ClassMetadata $class + * + * @throws InvalidArgumentException + */ + private function verifyClassCanBeProxied(ClassMetadata $class) + { + if ($class->getReflectionClass()->isFinal()) { + throw InvalidArgumentException::classMustNotBeFinal($class->getName()); + } + + if ($class->getReflectionClass()->isAbstract()) { + throw InvalidArgumentException::classMustNotBeAbstract($class->getName()); + } + } + + /** + * Generates the proxy short class name to be used in the template. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateProxyShortClassName(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[0]); + } + + /** + * Generates the proxy namespace. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateNamespace(ClassMetadata $class) + { + $proxyClassName = ClassUtils::generateProxyClassName($class->getName(), $this->proxyNamespace); + $parts = explode('\\', strrev($proxyClassName), 2); + + return strrev($parts[1]); + } + + /** + * Generates the original class name. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateClassName(ClassMetadata $class) + { + return ltrim($class->getName(), '\\'); + } + + /** + * Generates the array representation of lazy loaded public properties and their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateLazyPropertiesDefaults(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $values = []; + + foreach ($lazyPublicProperties as $key => $value) { + $values[] = var_export($key, true) . ' => ' . var_export($value, true); + } + + return implode(', ', $values); + } + + /** + * Generates the constructor code (un-setting public lazy loaded properties, setting identifier field values). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateConstructorImpl(ClassMetadata $class) + { + $constructorImpl = <<<'EOT' + /** + * @param \Closure $initializer + * @param \Closure $cloner + */ + public function __construct($initializer = null, $cloner = null) + { + +EOT; + $toUnset = []; + + foreach ($this->getLazyLoadedPublicProperties($class) as $lazyPublicProperty => $unused) { + $toUnset[] = '$this->' . $lazyPublicProperty; + } + + $constructorImpl .= (empty($toUnset) ? '' : ' unset(' . implode(', ', $toUnset) . ");\n") + . <<<'EOT' + + $this->__initializer__ = $initializer; + $this->__cloner__ = $cloner; + } +EOT; + + return $constructorImpl; + } + + /** + * Generates the magic getter invoked when lazy loaded public properties are requested. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicGet(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $reflectionClass = $class->getReflectionClass(); + $hasParentGet = false; + $returnReference = ''; + $inheritDoc = ''; + + if ($reflectionClass->hasMethod('__get')) { + $hasParentGet = true; + $inheritDoc = '{@inheritDoc}'; + + if ($reflectionClass->getMethod('__get')->returnsReference()) { + $returnReference = '& '; + } + } + + if (empty($lazyPublicProperties) && ! $hasParentGet) { + return ''; + } + + $magicGet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return $this->$name; + } + + +EOT; + } + + if ($hasParentGet) { + $magicGet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__get', [$name]); + + return parent::__get($name); + +EOT; + } else { + $magicGet .= <<<'EOT' + trigger_error(sprintf('Undefined property: %s::$%s', __CLASS__, $name), E_USER_NOTICE); + +EOT; + } + + $magicGet .= " }"; + + return $magicGet; + } + + /** + * Generates the magic setter (currently unused). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicSet(ClassMetadata $class) + { + $lazyPublicProperties = $this->getLazyLoadedPublicProperties($class); + $hasParentSet = $class->getReflectionClass()->hasMethod('__set'); + + if (empty($lazyPublicProperties) && ! $hasParentSet) { + return ''; + } + + $inheritDoc = $hasParentSet ? '{@inheritDoc}' : ''; + $magicSet = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + $this->$name = $value; + + return; + } + + +EOT; + } + + if ($hasParentSet) { + $magicSet .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__set', [$name, $value]); + + return parent::__set($name, $value); +EOT; + } else { + $magicSet .= " \$this->\$name = \$value;"; + } + + $magicSet .= "\n }"; + + return $magicSet; + } + + /** + * Generates the magic issetter invoked when lazy loaded public properties are checked against isset(). + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMagicIsset(ClassMetadata $class) + { + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $hasParentIsset = $class->getReflectionClass()->hasMethod('__isset'); + + if (empty($lazyPublicProperties) && ! $hasParentIsset) { + return ''; + } + + $inheritDoc = $hasParentIsset ? '{@inheritDoc}' : ''; + $magicIsset = <<__getLazyProperties())) { + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return isset($this->$name); + } + + +EOT; + } + + if ($hasParentIsset) { + $magicIsset .= <<<'EOT' + $this->__initializer__ && $this->__initializer__->__invoke($this, '__isset', [$name]); + + return parent::__isset($name); + +EOT; + } else { + $magicIsset .= " return false;"; + } + + return $magicIsset . "\n }"; + } + + /** + * Generates implementation for the `__sleep` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateSleepImpl(ClassMetadata $class) + { + $hasParentSleep = $class->getReflectionClass()->hasMethod('__sleep'); + $inheritDoc = $hasParentSleep ? '{@inheritDoc}' : ''; + $sleepImpl = <<__isInitialized__) { + $properties = array_diff($properties, array_keys($this->__getLazyProperties())); + } + + return $properties; + } +EOT; + } + + $allProperties = ['__isInitialized__']; + + /* @var $prop \ReflectionProperty */ + foreach ($class->getReflectionClass()->getProperties() as $prop) { + if ($prop->isStatic()) { + continue; + } + + $allProperties[] = $prop->isPrivate() + ? "\0" . $prop->getDeclaringClass()->getName() . "\0" . $prop->getName() + : $prop->getName(); + } + + $lazyPublicProperties = array_keys($this->getLazyLoadedPublicProperties($class)); + $protectedProperties = array_diff($allProperties, $lazyPublicProperties); + + foreach ($allProperties as &$property) { + $property = var_export($property, true); + } + + foreach ($protectedProperties as &$property) { + $property = var_export($property, true); + } + + $allProperties = implode(', ', $allProperties); + $protectedProperties = implode(', ', $protectedProperties); + + return $sleepImpl . <<__isInitialized__) { + return [$allProperties]; + } + + return [$protectedProperties]; + } +EOT; + } + + /** + * Generates implementation for the `__wakeup` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateWakeupImpl(ClassMetadata $class) + { + $unsetPublicProperties = []; + $hasWakeup = $class->getReflectionClass()->hasMethod('__wakeup'); + + foreach (array_keys($this->getLazyLoadedPublicProperties($class)) as $lazyPublicProperty) { + $unsetPublicProperties[] = '$this->' . $lazyPublicProperty; + } + + $shortName = $this->generateProxyShortClassName($class); + $inheritDoc = $hasWakeup ? '{@inheritDoc}' : ''; + $wakeupImpl = <<__isInitialized__) { + \$this->__initializer__ = function ($shortName \$proxy) { + \$proxy->__setInitializer(null); + \$proxy->__setCloner(null); + + \$existingProperties = get_object_vars(\$proxy); + + foreach (\$proxy->__getLazyProperties() as \$property => \$defaultValue) { + if ( ! array_key_exists(\$property, \$existingProperties)) { + \$proxy->\$property = \$defaultValue; + } + } + }; + +EOT; + + if ( ! empty($unsetPublicProperties)) { + $wakeupImpl .= "\n unset(" . implode(', ', $unsetPublicProperties) . ");"; + } + + $wakeupImpl .= "\n }"; + + if ($hasWakeup) { + $wakeupImpl .= "\n parent::__wakeup();"; + } + + $wakeupImpl .= "\n }"; + + return $wakeupImpl; + } + + /** + * Generates implementation for the `__clone` method of proxies. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateCloneImpl(ClassMetadata $class) + { + $hasParentClone = $class->getReflectionClass()->hasMethod('__clone'); + $inheritDoc = $hasParentClone ? '{@inheritDoc}' : ''; + $callParentClone = $hasParentClone ? "\n parent::__clone();\n" : ''; + + return <<__cloner__ && \$this->__cloner__->__invoke(\$this, '__clone', []); +$callParentClone } +EOT; + } + + /** + * Generates decorated methods by picking those available in the parent class. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return string + */ + private function generateMethods(ClassMetadata $class) + { + $methods = ''; + $methodNames = []; + $reflectionMethods = $class->getReflectionClass()->getMethods(\ReflectionMethod::IS_PUBLIC); + $skippedMethods = [ + '__sleep' => true, + '__clone' => true, + '__wakeup' => true, + '__get' => true, + '__set' => true, + '__isset' => true, + ]; + + foreach ($reflectionMethods as $method) { + $name = $method->getName(); + + if ($method->isConstructor() || + isset($skippedMethods[strtolower($name)]) || + isset($methodNames[$name]) || + $method->isFinal() || + $method->isStatic() || + ( ! $method->isPublic()) + ) { + continue; + } + + $methodNames[$name] = true; + $methods .= "\n /**\n" + . " * {@inheritDoc}\n" + . " */\n" + . ' public function '; + + if ($method->returnsReference()) { + $methods .= '&'; + } + + $methods .= $name . '(' . $this->buildParametersString($method->getParameters()) . ')'; + $methods .= $this->getMethodReturnType($method); + $methods .= "\n" . ' {' . "\n"; + + if ($this->isShortIdentifierGetter($method, $class)) { + $identifier = lcfirst(substr($name, 3)); + $fieldType = $class->getTypeOfField($identifier); + $cast = in_array($fieldType, ['integer', 'smallint']) ? '(int) ' : ''; + + $methods .= ' if ($this->__isInitialized__ === false) {' . "\n"; + $methods .= ' '; + $methods .= $this->shouldProxiedMethodReturn($method) ? 'return ' : ''; + $methods .= $cast . ' parent::' . $method->getName() . "();\n"; + $methods .= ' }' . "\n\n"; + } + + $invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters())); + $callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters())); + + $methods .= "\n \$this->__initializer__ " + . "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true) + . ", [" . $invokeParamsString . "]);" + . "\n\n " + . ($this->shouldProxiedMethodReturn($method) ? 'return ' : '') + . "parent::" . $name . '(' . $callParamsString . ');' + . "\n" . ' }' . "\n"; + } + + return $methods; + } + + /** + * Generates the Proxy file name. + * + * @param string $className + * @param string $baseDirectory Optional base directory for proxy file name generation. + * If not specified, the directory configured on the Configuration of the + * EntityManager will be used by this factory. + * + * @return string + */ + public function getProxyFileName($className, $baseDirectory = null) + { + $baseDirectory = $baseDirectory ?: $this->proxyDirectory; + + return rtrim($baseDirectory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . Proxy::MARKER + . str_replace('\\', '', $className) . '.php'; + } + + /** + * Checks if the method is a short identifier getter. + * + * What does this mean? For proxy objects the identifier is already known, + * however accessing the getter for this identifier usually triggers the + * lazy loading, leading to a query that may not be necessary if only the + * ID is interesting for the userland code (for example in views that + * generate links to the entity, but do not display anything else). + * + * @param \ReflectionMethod $method + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return boolean + */ + private function isShortIdentifierGetter($method, ClassMetadata $class) + { + $identifier = lcfirst(substr($method->getName(), 3)); + $startLine = $method->getStartLine(); + $endLine = $method->getEndLine(); + $cheapCheck = ( + $method->getNumberOfParameters() == 0 + && substr($method->getName(), 0, 3) == 'get' + && in_array($identifier, $class->getIdentifier(), true) + && $class->hasField($identifier) + && (($endLine - $startLine) <= 4) + ); + + if ($cheapCheck) { + $code = file($method->getDeclaringClass()->getFileName()); + $code = trim(implode(' ', array_slice($code, $startLine - 1, $endLine - $startLine + 1))); + + $pattern = sprintf(self::PATTERN_MATCH_ID_METHOD, $method->getName(), $identifier); + + if (preg_match($pattern, $code)) { + return true; + } + } + + return false; + } + + /** + * Generates the list of public properties to be lazy loaded, with their default values. + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $class + * + * @return mixed[] + */ + private function getLazyLoadedPublicProperties(ClassMetadata $class) + { + $defaultProperties = $class->getReflectionClass()->getDefaultProperties(); + $properties = []; + + foreach ($class->getReflectionClass()->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { + $name = $property->getName(); + + if (($class->hasField($name) || $class->hasAssociation($name)) && ! $class->isIdentifier($name)) { + $properties[$name] = $defaultProperties[$name]; + } + } + + return $properties; + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string + */ + private function buildParametersString(array $parameters) + { + $parameterDefinitions = []; + + /* @var $param \ReflectionParameter */ + foreach ($parameters as $param) { + $parameterDefinition = ''; + + if ($parameterType = $this->getParameterType($param)) { + $parameterDefinition .= $parameterType . ' '; + } + + if ($param->isPassedByReference()) { + $parameterDefinition .= '&'; + } + + if ($param->isVariadic()) { + $parameterDefinition .= '...'; + } + + $parameterDefinition .= '$' . $param->getName(); + + if ($param->isDefaultValueAvailable()) { + $parameterDefinition .= ' = ' . var_export($param->getDefaultValue(), true); + } + + $parameterDefinitions[] = $parameterDefinition; + } + + return implode(', ', $parameterDefinitions); + } + + /** + * @param ClassMetadata $class + * @param \ReflectionMethod $method + * @param \ReflectionParameter $parameter + * + * @return string|null + */ + private function getParameterType(\ReflectionParameter $parameter) + { + if ( ! $parameter->hasType()) { + return null; + } + + return $this->formatType($parameter->getType(), $parameter->getDeclaringFunction(), $parameter); + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForInvoke(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + return '$' . $parameter->getName(); + }, + $parameters + ); + } + + /** + * @param \ReflectionParameter[] $parameters + * + * @return string[] + */ + private function getParameterNamesForParentCall(array $parameters) + { + return array_map( + function (\ReflectionParameter $parameter) { + $name = ''; + + if ($parameter->isVariadic()) { + $name .= '...'; + } + + $name .= '$' . $parameter->getName(); + + return $name; + }, + $parameters + ); + } + + /** + * @param \ReflectionMethod $method + * + * @return string + */ + private function getMethodReturnType(\ReflectionMethod $method) + { + if ( ! $method->hasReturnType()) { + return ''; + } + + return ': ' . $this->formatType($method->getReturnType(), $method); + } + + /** + * @param \ReflectionMethod $method + * + * @return bool + */ + private function shouldProxiedMethodReturn(\ReflectionMethod $method) + { + if ( ! $method->hasReturnType()) { + return true; + } + + return 'void' !== strtolower($this->formatType($method->getReturnType(), $method)); + } + + /** + * @param \ReflectionType $type + * @param \ReflectionMethod $method + * @param \ReflectionParameter|null $parameter + * + * @return string + */ + private function formatType( + \ReflectionType $type, + \ReflectionMethod $method, + \ReflectionParameter $parameter = null + ) { + $name = (string) $type; + $nameLower = strtolower($name); + + if ('self' === $nameLower) { + $name = $method->getDeclaringClass()->getName(); + } + + if ('parent' === $nameLower) { + $name = $method->getDeclaringClass()->getParentClass()->getName(); + } + + if ( ! $type->isBuiltin() && ! class_exists($name) && ! interface_exists($name)) { + if (null !== $parameter) { + throw UnexpectedValueException::invalidParameterTypeHint( + $method->getDeclaringClass()->getName(), + $method->getName(), + $parameter->getName() + ); + } + + throw UnexpectedValueException::invalidReturnTypeHint( + $method->getDeclaringClass()->getName(), + $method->getName() + ); + } + + if ( ! $type->isBuiltin()) { + $name = '\\' . $name; + } + + if ($type->allowsNull() + && (null === $parameter || ! $parameter->isDefaultValueAvailable() || null !== $parameter->getDefaultValue()) + ) { + $name = '?' . $name; + } + + return $name; + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php new file mode 100644 index 0000000..e3d24cf --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/ClassUtils.php @@ -0,0 +1,93 @@ + + * @author Johannes Schmitt + * + * @deprecated The ClassUtils class is deprecated. + */ +class ClassUtils +{ + /** + * Gets the real class name of a class name that could be a proxy. + * + * @param string $class + * + * @return string + */ + public static function getRealClass($class) + { + if (false === $pos = strrpos($class, '\\' . Proxy::MARKER . '\\')) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } + + /** + * Gets the real class name of an object (even if its a proxy). + * + * @param object $object + * + * @return string + */ + public static function getClass($object) + { + return self::getRealClass(get_class($object)); + } + + /** + * Gets the real parent class name of a class or object. + * + * @param string $className + * + * @return string + */ + public static function getParentClass($className) + { + return get_parent_class(self::getRealClass($className)); + } + + /** + * Creates a new reflection class. + * + * @param string $class + * + * @return \ReflectionClass + */ + public static function newReflectionClass($class) + { + return new \ReflectionClass(self::getRealClass($class)); + } + + /** + * Creates a new reflection object. + * + * @param object $object + * + * @return \ReflectionClass + */ + public static function newReflectionObject($object) + { + return self::newReflectionClass(self::getClass($object)); + } + + /** + * Given a class name and a proxy namespace returns the proxy name. + * + * @param string $className + * @param string $proxyNamespace + * + * @return string + */ + public static function generateProxyClassName($className, $proxyNamespace) + { + return rtrim($proxyNamespace, '\\') . '\\' . Proxy::MARKER . '\\' . ltrim($className, '\\'); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php new file mode 100644 index 0000000..950a239 --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Debug.php @@ -0,0 +1,167 @@ + + * @author Jonathan Wage + * @author Roman Borschel + * @author Giorgio Sironi + * + * @deprecated The Debug class is deprecated, please use symfony/var-dumper instead. + */ +final class Debug +{ + /** + * Private constructor (prevents instantiation). + */ + private function __construct() + { + } + + /** + * Prints a dump of the public, protected and private properties of $var. + * + * @link https://xdebug.org/ + * + * @param mixed $var The variable to dump. + * @param integer $maxDepth The maximum nesting level for object properties. + * @param boolean $stripTags Whether output should strip HTML tags. + * @param boolean $echo Send the dumped value to the output buffer + * + * @return string + */ + public static function dump($var, $maxDepth = 2, $stripTags = true, $echo = true) + { + $html = ini_get('html_errors'); + + if ($html !== true) { + ini_set('html_errors', true); + } + + if (extension_loaded('xdebug')) { + ini_set('xdebug.var_display_max_depth', $maxDepth); + } + + $var = self::export($var, $maxDepth); + + ob_start(); + var_dump($var); + + $dump = ob_get_contents(); + + ob_end_clean(); + + $dumpText = ($stripTags ? strip_tags(html_entity_decode($dump)) : $dump); + + ini_set('html_errors', $html); + + if ($echo) { + echo $dumpText; + } + + return $dumpText; + } + + /** + * @param mixed $var + * @param int $maxDepth + * + * @return mixed + */ + public static function export($var, $maxDepth) + { + $return = null; + $isObj = is_object($var); + + if ($var instanceof Collection) { + $var = $var->toArray(); + } + + if ( ! $maxDepth) { + return is_object($var) ? get_class($var) + : (is_array($var) ? 'Array(' . count($var) . ')' : $var); + } + + if (is_array($var)) { + $return = []; + + foreach ($var as $k => $v) { + $return[$k] = self::export($v, $maxDepth - 1); + } + + return $return; + } + + if ( ! $isObj) { + return $var; + } + + $return = new \stdclass(); + if ($var instanceof \DateTimeInterface) { + $return->__CLASS__ = get_class($var); + $return->date = $var->format('c'); + $return->timezone = $var->getTimezone()->getName(); + + return $return; + } + + $return->__CLASS__ = ClassUtils::getClass($var); + + if ($var instanceof Proxy) { + $return->__IS_PROXY__ = true; + $return->__PROXY_INITIALIZED__ = $var->__isInitialized(); + } + + if ($var instanceof \ArrayObject || $var instanceof \ArrayIterator) { + $return->__STORAGE__ = self::export($var->getArrayCopy(), $maxDepth - 1); + } + + return self::fillReturnWithClassAttributes($var, $return, $maxDepth); + } + + /** + * Fill the $return variable with class attributes + * Based on obj2array function from {@see https://secure.php.net/manual/en/function.get-object-vars.php#47075} + * + * @param object $var + * @param \stdClass $return + * @param int $maxDepth + * + * @return mixed + */ + private static function fillReturnWithClassAttributes($var, \stdClass $return, $maxDepth) + { + $clone = (array) $var; + + foreach (array_keys($clone) as $key) { + $aux = explode("\0", $key); + $name = end($aux); + if ($aux[0] === '') { + $name .= ':' . ($aux[1] === '*' ? 'protected' : $aux[1] . ':private'); + } + $return->$name = self::export($clone[$key], $maxDepth - 1); + ; + } + + return $return; + } + + /** + * Returns a string representation of an object. + * + * @param object $obj + * + * @return string + */ + public static function toString($obj) + { + return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); + } +} diff --git a/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php new file mode 100644 index 0000000..f41c54a --- /dev/null +++ b/vendor/doctrine/common/lib/Doctrine/Common/Util/Inflector.php @@ -0,0 +1,19 @@ + + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + * + * @deprecated The Version class is deprecated, please refrain from checking the version of doctrine/common. + */ +class Version +{ + /** + * Current Doctrine Version. + */ + const VERSION = '2.9.0'; + + /** + * Compares a Doctrine version with the current one. + * + * @param string $version Doctrine version to compare. + * + * @return int -1 if older, 0 if it is the same, 1 if version passed as argument is newer. + */ + public static function compare($version) + { + $currentVersion = str_replace(' ', '', strtolower(self::VERSION)); + $version = str_replace(' ', '', $version); + + return version_compare($version, $currentVersion); + } +} diff --git a/vendor/doctrine/common/phpstan.neon b/vendor/doctrine/common/phpstan.neon new file mode 100644 index 0000000..ab1c7fa --- /dev/null +++ b/vendor/doctrine/common/phpstan.neon @@ -0,0 +1,10 @@ +parameters: + autoload_directories: + - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/ClassLoaderTest + excludes_analyse: + - %currentWorkingDirectory%/lib/vendor/doctrine-build-common + - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Proxy/InvalidReturnTypeClass.php + - %currentWorkingDirectory%/tests/Doctrine/Tests/Common/Proxy/InvalidTypeHintClass.php + ignoreErrors: + - '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#' + - '#Access to an undefined property Doctrine\\Tests\\Common\\Proxy\\MagicGetByRefClass::\$nonExisting#' diff --git a/vendor/doctrine/data-fixtures/.gitignore b/vendor/doctrine/data-fixtures/.gitignore new file mode 100644 index 0000000..f1a8d7d --- /dev/null +++ b/vendor/doctrine/data-fixtures/.gitignore @@ -0,0 +1,3 @@ +phpunit.xml +vendor/ +composer.lock diff --git a/vendor/doctrine/data-fixtures/.travis.yml b/vendor/doctrine/data-fixtures/.travis.yml new file mode 100644 index 0000000..4aa5fd5 --- /dev/null +++ b/vendor/doctrine/data-fixtures/.travis.yml @@ -0,0 +1,32 @@ +language: php + +sudo: false + +cache: + directories: + - $HOME/.composer/cache + +php: + - 7.1 + - 7.2 + - nightly + +matrix: + allow_failures: + - php: nightly + +services: mongodb + +before_install: + - pecl install -f mongodb + +before_script: + - composer self-update + - composer install --prefer-source + - composer config "platform.ext-mongo" "1.6.16" + - composer require alcaeus/mongo-php-adapter + - composer require doctrine/mongodb-odm + - composer require jackalope/jackalope-doctrine-dbal doctrine/phpcr-odm + +script: + - ./vendor/bin/phpunit -v diff --git a/vendor/doctrine/data-fixtures/CHANGELOG.md b/vendor/doctrine/data-fixtures/CHANGELOG.md new file mode 100644 index 0000000..8bae0c9 --- /dev/null +++ b/vendor/doctrine/data-fixtures/CHANGELOG.md @@ -0,0 +1,170 @@ +# Change Log +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [1.3.0] - 2017-11-27 + +### Fixed + - [248: Rename example classes names for consistency](https://github.com/doctrine/data-fixtures/pull/248) - @Kwadz + - [253: Fix syntax highlighting on README](https://github.com/doctrine/data-fixtures/pull/253) - @fefas + - [269: Remove tabs](https://github.com/doctrine/data-fixtures/pull/269) - @garak + +### Changed + - [266: Bump PHP requirement to 7.1](https://github.com/doctrine/data-fixtures/pull/266) - @alcaeus + +### Added + - [255: Added handling of PHPCR documents on method `getIdentifier()`](https://github.com/doctrine/data-fixtures/pull/255) - @aemaething + - [274: Hook point to control creating fixtures objects](https://github.com/doctrine/data-fixtures/pull/274) - @weaverryan + +### Fixed + - [572: Only show generated migration in verbose mode](https://github.com/doctrine/migrations/pull/572) - @alcaeus + +## [1.6.0] - 2017-11-09 + +### Fixed + - [536: Fix typo in exception message](https://github.com/doctrine/migrations/pull/536) - @mantiz + - [545: Allow nullable custom template](https://github.com/doctrine/migrations/pull/545) - @PapsOu + - [560: Fix `--dry-run` and `--write-sql` with `addSql`](https://github.com/doctrine/migrations/pull/560) - @mikeSimonson + - [569: Allow using absolute paths for custom template](https://github.com/doctrine/migrations/pull/569) - @alcaeus + +### Changed + - [537: Require PHP 7.1](https://github.com/doctrine/migrations/pull/537) - @lcobucci + - [563: Remove copyright headers](https://github.com/doctrine/migrations/pull/563) - @mikeSimonson + - [534: Change RecursiveRegexFinder to follow Symlinks](https://github.com/doctrine/migrations/pull/534) - @burci + +### Added + - [506: Add migration events](https://github.com/doctrine/migrations/pull/506) - @chrisguitarguy + - [509: Add delta in migrate command](https://github.com/doctrine/migrations/pull/509) - @Safranil + - [535: Allow changing migration class template in generate command](https://github.com/doctrine/migrations/pull/535) - @PapsOu + - [542: Enable phpcs to ensure coding style](https://github.com/doctrine/migrations/pull/542) - @lcobucci + - [562: Allow providing a path for file dump](https://github.com/doctrine/migrations/pull/562) - @mikeSimonson + - [565: Allow Symfony 4](https://github.com/doctrine/migrations/565) - @weaverryan + - [568: Add `ConfigurationHelperInterface` to allow overriding internal class](https://github.com/doctrine/migrations/pull/568) - @alcaeus + - [571: Allow changing migration class template in diff command](https://github.com/doctrine/migrations/pull/571) - @alcaeus + +## [1.5.0] - 2016-12-25 + +### Fixed + - [447: Fix typo in error code](https://github.com/doctrine/migrations/pull/447) thanks to @funivan + - [448: Removed unused import](https://github.com/doctrine/migrations/pull/448) thanks to @localheinz + - [451: Case insensitive method calls and types](https://github.com/doctrine/migrations/pull/451) thanks to @localheinz + - [449: Use the class keyword](https://github.com/doctrine/migrations/pull/449) thanks to @localheinz + - [453: Use better assertions](https://github.com/doctrine/migrations/pull/453) thanks to @localheinz + - [452: Fix the visibility of the AbstractTest](https://github.com/doctrine/migrations/pull/452) thanks to @localheinz + - [450: Use the short array syntax](https://github.com/doctrine/migrations/pull/450) thanks to @localheinz + - [464: Clean the github autogenerated zip archive](https://github.com/doctrine/migrations/pull/464) thanks to @mlocati + - [457: Fix an issue where the migration wheren't sorted propely](https://github.com/doctrine/migrations/pull/457) thanks to @JHGitty, @Charles68 + - [451: Case insensitive method calls and types](https://github.com/doctrine/migrations/pull/451) thanks to @localheinz + - [471: Fix loading the configuration from a php file](https://github.com/doctrine/migrations/pull/471) thanks to @aaa2000 + - [451: Case insensitive method calls and types](https://github.com/doctrine/migrations/pull/451) thanks to @localheinz + - [472: Change comment marker a cross platform one ](https://github.com/doctrine/migrations/pull/472) thanks to @aaa2000 + - [476: Add test for the execute command](https://github.com/doctrine/migrations/pull/476) thanks to @chrisguitarguy + - [470: Case insensitive method calls and types](https://github.com/doctrine/migrations/pull/470) thanks to @abacaphiliac + - [489: Better help message in the execute command](https://github.com/doctrine/migrations/pull/489) thanks to @eko + - [500: Updating the dev dependencies](https://github.com/doctrine/migrations/pull/500) thanks to @mikeSimonson + - [501: Fix a bug in a rarely used message call](https://github.com/doctrine/migrations/pull/501) thanks to @Seldaek + +### Changed + - [478: Changed the default generated version number to UTC](https://github.com/doctrine/migrations/pull/446) thanks to @c960657 + - [479: Explicitly connecting to support master slave connections](https://github.com/doctrine/migrations/pull/479) thanks to @chrisguitarguy + - [482: Use strict comparaison in the generated migrations](https://github.com/doctrine/migrations/pull/482) thanks to @tifabien + +### Added + - [470: Add uptodate command](https://github.com/doctrine/migrations/pull/470) thanks to @abacaphiliac + - [477: Add parameters to the output of the dry run migrations](https://github.com/doctrine/migrations/pull/477) thanks to @chrisguitarguy + - [497: Show the content of the generated file on the console](https://github.com/doctrine/migrations/pull/497) thanks to @ErikTrapman + - [480: Don't prompt the user for confirmation when there is nothing to do](https://github.com/doctrine/migrations/pull/480) thanks to @chrisguitarguy + + +## [1.4.1] - 2016-03-14 +### Fixed + - [439: Add missing dependency in the phar build](https://github.com/doctrine/migrations/pull/439) + + +## [1.4.0] - 2016-02-23 +### Added + - [431: Formatted diffs](https://github.com/doctrine/migrations/pull/431) + - [437: Allowing to set the column name from config too](https://github.com/doctrine/migrations/pull/437) + +## [1.3.1] - 2016-02-23 +### Fixed + - [433: Fix: ExecuteCommand by making sure that it autoload the versions](https://github.com/doctrine/migrations/pull/433) + - [434: Fixing an issue in the order at which some configuration key are loaded](https://github.com/doctrine/migrations/pull/434) + +### Changed + - [429: code refactoring for clarity](https://github.com/doctrine/migrations/pull/429) + - [428: code refactoring for clarity](https://github.com/doctrine/migrations/pull/428) + +### Added + - [425: ProxyManager ~2.0 is also compatible with migrations](https://github.com/doctrine/migrations/pull/425) + + +## [1.3.0] - 2016-01-23 +### Fixed + - [421: Fix issue with some method when the migrations were not loaded](https://github.com/doctrine/migrations/pull/421) + - [405: Correcting composer constraints: allowing PHP 7, dropping 5.4 support](https://github.com/doctrine/migrations/pull/405) + - [406: putting the composer.json back in the archive](https://github.com/doctrine/migrations/pull/406) + +### Changed + - [233: Separate Configuration Objects from Configuration File Loading](https://github.com/doctrine/migrations/issues/233) + - [407: Replacing the Schema by a proxy](https://github.com/doctrine/migrations/pull/407) + - [422: Refactor the getConnection into a chainloader](https://github.com/doctrine/migrations/pull/422) + - [Dropped the support for php 5.4](https://github.com/doctrine/migrations/pull/393) + - [Again make the phar more than 3 times smaller](https://github.com/doctrine/migrations/pull/396) + +### Added + - [404: add possibility to read doctrine's config/cli-config.php](https://github.com/doctrine/migrations/pull/404) + - [409: Add a failing test case for an edge case with the write-sql option](https://github.com/doctrine/migrations/pull/409) + - [424: Adding a regression test for the configuration](https://github.com/doctrine/migrations/pull/424) + - [391: Give the possibility to override the Migration template](https://github.com/doctrine/migrations/pull/391) + +## [1.2.2] - 2016-01-07 +### Fixed + - [Fix the write-sql option in the version class too](https://github.com/doctrine/migrations/commit/91043f742da8506ab7115a1d14247ce26375f6f5) + +## [1.2.1] - 2015-12-23 +### Fixed + - [Fix the write-sql option](https://github.com/doctrine/migrations/pull/400) + +## [1.2.0] - 2015-12-15 +### Fixed + - [fix "all migrated versions shown as unavailable executed" ](https://github.com/doctrine/migrations/commit/875849e2a80d37dc8bf5dd0663e464b6789e3b56) + - [Prevent the use of 0 as migration name as it is used internally and conflict](http://github.com/doctrine/migrations/commit/5df49c5ad5dc2265401a54a3b9e6ecb3e7cda8d0) + - [composer: drop symfony/console from suggested packages, since it's required package](http://github.com/doctrine/migrations/commit/d263c7bfac7188009ab0717ed5aa6577e80) + - [fix spaces and complete missing file-docblocks](http://github.com/doctrine/migrations/commit/4b68a69c3e35492b36ec140ebb216cdb80ffe655) + - [RecursiveDirectoryIterator don't obtain some order of the file list.](http://github.com/doctrine/migrations/commit/ed95c05c14381e64404f1135763fcc9b65317b96) + - [Fix the yml parser issue with unescaped backslash in double quoted string](http://github.com/doctrine/migrations/commit/af3cce7d2e490ead821fcbdb54b4772b4913ee1d) + +### Changed + - [Adding compression to the generation of the phar](http://github.com/doctrine/migrations/commit/70730ff8655f0be89ce0f06d1e279930d7eb2550) + - [tests: move autoload of tests to composer](http://github.com/doctrine/migrations/commit/3a4f8368e4b7b95d2e6c51c26f6dc41bb05a5ce5) + - [travis: drop PHP 7.0 from allowed failures, it passes well](http://github.com/doctrine/migrations/commit/57ec2f071a7a840c554058b77f2089893d06ba23) + - [Revert the use of exec to run sql queries](http://github.com/doctrine/migrations/commit/0af6e6e705b905a56cbed26cb5c17faad4c2c04f) + +### Added + - [Adding a little script to prepare and generate the phar](http://github.com/doctrine/migrations/commit/3a8ef413e7f8a42d4e0f3d32d30601b26fb27e60) + - [Configuration: check if migration class exists added](http://github.com/doctrine/migrations/commit/a53d7c83b319341c985d2a21950e260fa55b0b8d) + - [Made composer.json compatible with SF 3.0](http://github.com/doctrine/migrations/commit/4e909f2e661a8414a3e04ce987a09c9e849cd13f) + - [Added configurable version column name](http://github.com/doctrine/migrations/commit/02ddf4318b84a20bb0e3486acfc6e6f41cc63426) + +## [1.1.0] - 2015-09-29 +### Fixed + - [Switch unexisting tag to ](http://github.com/doctrine/migrations/commit/93a81ff0dcc858de4df5c17d97f2f24b3bfa3c36) + - [Ensure that Yaml::parse return an array as it can apparently return a string](http://github.com/doctrine/migrations/commit/1499f0cc3e5f5b20a510ba8f0779d5c68a9e5084) + - [Avoid uploading code coverage to Scrutinizer for HHVM and PHP 7](http://github.com/doctrine/migrations/commit/d47d65021dcb711480bf27f6d0bbba138e220f12) + - [Improve the Travis configuration by persisting the composer cache](http://github.com/doctrine/migrations/commit/bda0509b479ae6605b8fa749b0999b4ce2ff8c04) + - [Keep the license file in the downloadable archives](https://github.com/doctrine/migrations/commit/dfbee38e004899bc078d5d47b13bea53799fca1e#diff-fc723d30b02a4cca7a534518111c1a66) + +### Changed + - [Use short array syntax](http://github.com/doctrine/migrations/commit/50a6e18c95ff617325229a4a649d65c1a11445bc) + - [composer: use PSR-4 autoload](http://github.com/doctrine/migrations/commit/7fb8d301b2f4d4a564433165e0604b7d34013844) + - [refactoring the configuration loading](http://github.com/doctrine/migrations/commit/e95b277111c74cfe65eb959d4471f45a815e1ece) + - [Drop support for php 5.3](https://github.com/doctrine/migrations/commit/0e60856a10e8b510daa612fe25f6245aece77e68) + - [compressing the phar so that it's half the size now](https://github.com/doctrine/migrations/commit/70730ff8655f0be89ce0f06d1e279930d7eb2550) + +### Added + - [Added the ability to auto create migrations in a folder by month and year](http://github.com/doctrine/migrations/commit/0b8e40868e12a36de7f689add61857b9ba29c4bc) + - [Set default name for configuration helper](http://github.com/doctrine/migrations/commit/1f3592f2f126a022db275192f17b8d5c01f19822) + - [Added ability to load config from php array or json files.](http://github.com/doctrine/migrations/commit/8cf01d623f9eb3728ba86c22970347107a8f0be7) + - [Added the possibility to inject the OutputWriter after instantiation](https://github.com/doctrine/migrations/pull/342) + - [Added the --allow-no-migration option to avoid throwing an exception if no migrations are found](https://github.com/doctrine/migrations/commit/a9ec86faa3a3f7f592a633af43b6ef6c9f358239#diff-0a4648a19ba565cda76b349e89552a9b) diff --git a/vendor/doctrine/data-fixtures/LICENSE b/vendor/doctrine/data-fixtures/LICENSE new file mode 100644 index 0000000..2336c7f --- /dev/null +++ b/vendor/doctrine/data-fixtures/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/doctrine/data-fixtures/README.md b/vendor/doctrine/data-fixtures/README.md new file mode 100644 index 0000000..2c95490 --- /dev/null +++ b/vendor/doctrine/data-fixtures/README.md @@ -0,0 +1,201 @@ +# Doctrine Data Fixtures Extension + +[![Build Status](https://travis-ci.org/doctrine/data-fixtures.png)](https://travis-ci.org/doctrine/data-fixtures) + +This extension aims to provide a simple way to manage and execute the loading of data fixtures +for the [Doctrine ORM or ODM](http://www.doctrine-project.org/). You can write fixture classes +by implementing the [`Doctrine\Common\DataFixtures\FixtureInterface`](lib/Doctrine/Common/DataFixtures/FixtureInterface.php) interface: + +```php +namespace MyDataFixtures; + +use Doctrine\Common\Persistence\ObjectManager; +use Doctrine\Common\DataFixtures\FixtureInterface; + +class UserFixtureLoader implements FixtureInterface +{ + public function load(ObjectManager $manager) + { + $user = new User(); + $user->setUsername('jwage'); + $user->setPassword('test'); + + $manager->persist($user); + $manager->flush(); + } +} +``` + +Now you can begin adding the fixtures to a loader instance: + +```php +use Doctrine\Common\DataFixtures\Loader; +use MyDataFixtures\UserDataLoader; + +$loader = new Loader(); +$loader->addFixture(new UserDataLoader()); +``` + +You can load a set of fixtures from a directory as well: + +```php +$loader->loadFromDirectory('/path/to/MyDataFixtures'); +``` + +Or you can load a set of fixtures from a file: + +```php +$loader->loadFromFile('/path/to/MyDataFixtures/MyFixture1.php'); +``` + +You can get the added fixtures using the getFixtures() method: + +```php +$fixtures = $loader->getFixtures(); +``` + +Now you can easily execute the fixtures: + +```php +use Doctrine\Common\DataFixtures\Executor\ORMExecutor; +use Doctrine\Common\DataFixtures\Purger\ORMPurger; + +$purger = new ORMPurger(); +$executor = new ORMExecutor($em, $purger); +$executor->execute($loader->getFixtures()); +``` + +If you want to append the fixtures instead of purging before loading then pass true +to the 2nd argument of execute: + +```php +$executor->execute($loader->getFixtures(), true); +``` + +## Sharing objects between fixtures + +In case if fixture objects have relations to other fixtures, it is now possible +to easily add a reference to that object by name and later reference it to form +a relation. Here is an example fixtures for **Role** and **User** relation + +```php +namespace MyDataFixtures; + +use Doctrine\Common\DataFixtures\AbstractFixture; +use Doctrine\Common\Persistence\ObjectManager; + +class UserRoleDataLoader extends AbstractFixture +{ + public function load(ObjectManager $manager) + { + $adminRole = new Role(); + $adminRole->setName('admin'); + + $anonymousRole = new Role(); + $anonymousRole->setName('anonymous'); + + $manager->persist($adminRole); + $manager->persist($anonymousRole); + $manager->flush(); + + // store reference to admin role for User relation to Role + $this->addReference('admin-role', $adminRole); + } +} +``` + +And the **User** data loading fixture: + +```php +namespace MyDataFixtures; + +use Doctrine\Common\DataFixtures\AbstractFixture; +use Doctrine\Common\Persistence\ObjectManager; + +class UserDataLoader extends AbstractFixture +{ + public function load(ObjectManager $manager) + { + $user = new User(); + $user->setUsername('jwage'); + $user->setPassword('test'); + $user->setRole( + $this->getReference('admin-role') // load the stored reference + ); + + $manager->persist($user); + $manager->flush(); + + // store reference of admin-user for other Fixtures + $this->addReference('admin-user', $user); + } +} +``` + +## Fixture ordering +**Notice** that the fixture loading order is important! To handle it manually +implement one of the following interfaces: + +### OrderedFixtureInterface + +Set the order manually: + +```php +namespace MyDataFixtures; + +use Doctrine\Common\DataFixtures\AbstractFixture; +use Doctrine\Common\DataFixtures\OrderedFixtureInterface; +use Doctrine\Common\Persistence\ObjectManager; + +class MyFixture extends AbstractFixture implements OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 10; // number in which order to load fixtures + } +} +``` + +### DependentFixtureInterface + +Provide an array of fixture class names: + +```php +namespace MyDataFixtures; + +use Doctrine\Common\DataFixtures\AbstractFixture; +use Doctrine\Common\DataFixtures\DependentFixtureInterface; +use Doctrine\Common\Persistence\ObjectManager; + +class MyFixture extends AbstractFixture implements DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return array('MyDataFixtures\MyOtherFixture'); // fixture classes fixture is dependent on + } +} + +class MyOtherFixture extends AbstractFixture +{ + public function load(ObjectManager $manager) + {} +} +``` + +**Notice** the ordering is relevant to Loader class. + +## Running the tests: + +Phpunit is included in the dev requirements of this package. + +To setup and run tests follow these steps: + +- go to the root directory of data-fixtures +- run: **composer install --dev** +- run: **vendor/bin/phpunit** diff --git a/vendor/doctrine/data-fixtures/UPGRADE b/vendor/doctrine/data-fixtures/UPGRADE new file mode 100644 index 0000000..dba81ad --- /dev/null +++ b/vendor/doctrine/data-fixtures/UPGRADE @@ -0,0 +1,17 @@ +# Between v1.0.0-ALPHA1 and v1.0.0-ALPHA2 + +The FixtureInterface was changed from + + interface FixtureInterface + { + load($manager); + } + +to + + use Doctrine\Common\Persistence\ObjectManager; + + interface FixtureInterface + { + load(ObjectManager $manager); + } diff --git a/vendor/doctrine/data-fixtures/composer.json b/vendor/doctrine/data-fixtures/composer.json new file mode 100644 index 0000000..7c2b629 --- /dev/null +++ b/vendor/doctrine/data-fixtures/composer.json @@ -0,0 +1,40 @@ +{ + "name": "doctrine/data-fixtures", + "type": "library", + "description": "Data Fixtures for all Doctrine Object Managers", + "keywords": ["database"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"} + ], + "require": { + "php": "^7.1", + "doctrine/common": "~2.2" + }, + "require-dev": { + "doctrine/orm": "^2.5.4", + "doctrine/dbal": "^2.5.4", + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "doctrine/orm": "For loading ORM fixtures", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures", + "alcaeus/mongo-php-adapter": "For using MongoDB ODM with PHP 7" + }, + "autoload": { + "psr-4": { "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" } + }, + "autoload-dev": { + "psr-4": { "Doctrine\\Tests\\": "tests/Doctrine/Tests" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "conflict": { + "doctrine/phpcr-odm": "<1.3.0" + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/AbstractFixture.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/AbstractFixture.php new file mode 100644 index 0000000..7edb131 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/AbstractFixture.php @@ -0,0 +1,89 @@ + + */ +abstract class AbstractFixture implements SharedFixtureInterface +{ + /** + * Fixture reference repository + * + * @var ReferenceRepository + */ + protected $referenceRepository; + + /** + * {@inheritdoc} + */ + public function setReferenceRepository(ReferenceRepository $referenceRepository) + { + $this->referenceRepository = $referenceRepository; + } + + /** + * Set the reference entry identified by $name + * and referenced to managed $object. If $name + * already is set, it overrides it + * + * @param string $name + * @param object $object - managed object + * @see Doctrine\Common\DataFixtures\ReferenceRepository::setReference + * @return void + */ + public function setReference($name, $object) + { + $this->referenceRepository->setReference($name, $object); + } + + /** + * Set the reference entry identified by $name + * and referenced to managed $object. If $name + * already is set, it throws a + * BadMethodCallException exception + * + * @param string $name + * @param object $object - managed object + * @see Doctrine\Common\DataFixtures\ReferenceRepository::addReference + * @throws \BadMethodCallException - if repository already has + * a reference by $name + * @return void + */ + public function addReference($name, $object) + { + $this->referenceRepository->addReference($name, $object); + } + + /** + * Loads an object using stored reference + * named by $name + * + * @param string $name + * @see Doctrine\Common\DataFixtures\ReferenceRepository::getReference + * @return object + */ + public function getReference($name) + { + return $this->referenceRepository->getReference($name); + } + + /** + * Check if an object is stored using reference + * named by $name + * + * @param string $name + * @see Doctrine\Common\DataFixtures\ReferenceRepository::hasReference + * @return boolean + */ + public function hasReference($name) + { + return $this->referenceRepository->hasReference($name); + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/DependentFixtureInterface.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/DependentFixtureInterface.php new file mode 100644 index 0000000..32e30d2 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/DependentFixtureInterface.php @@ -0,0 +1,37 @@ +. + */ + +namespace Doctrine\Common\DataFixtures; + +/** + * DependentFixtureInterface needs to be implemented + * by fixtures which depend on other fixtures + * + * @author Gustavo Adrian + */ +interface DependentFixtureInterface +{ + /** + * This method must return an array of fixtures classes + * on which the implementing class depends on + * + * @return array + */ + public function getDependencies(); +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/MongoDBReferenceListener.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/MongoDBReferenceListener.php new file mode 100644 index 0000000..746b469 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/MongoDBReferenceListener.php @@ -0,0 +1,62 @@ + + */ +final class MongoDBReferenceListener implements EventSubscriber +{ + /** + * @var ReferenceRepository + */ + private $referenceRepository; + + /** + * Initialize listener + * + * @param ReferenceRepository $referenceRepository + */ + public function __construct(ReferenceRepository $referenceRepository) + { + $this->referenceRepository = $referenceRepository; + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return [ + 'postPersist' + ]; + } + + /** + * Populates identities for stored references + * + * @param LifecycleEventArgs $args + */ + public function postPersist(LifecycleEventArgs $args) + { + $object = $args->getDocument(); + + if (($names = $this->referenceRepository->getReferenceNames($object)) !== false) { + foreach ($names as $name) { + $identity = $args->getDocumentManager() + ->getUnitOfWork() + ->getDocumentIdentifier($object); + + $this->referenceRepository->setReferenceIdentity($name, $identity); + } + } + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/ORMReferenceListener.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/ORMReferenceListener.php new file mode 100644 index 0000000..fb9327d --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Event/Listener/ORMReferenceListener.php @@ -0,0 +1,62 @@ + + */ +final class ORMReferenceListener implements EventSubscriber +{ + /** + * @var ReferenceRepository + */ + private $referenceRepository; + + /** + * Initialize listener + * + * @param ReferenceRepository $referenceRepository + */ + public function __construct(ReferenceRepository $referenceRepository) + { + $this->referenceRepository = $referenceRepository; + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return [ + 'postPersist' // would be better to use onClear, but it is supported only in 2.1 + ]; + } + + /** + * Populates identities for stored references + * + * @param LifecycleEventArgs $args + */ + public function postPersist(LifecycleEventArgs $args) + { + $object = $args->getEntity(); + + if (($names = $this->referenceRepository->getReferenceNames($object)) !== false) { + foreach ($names as $name) { + $identity = $args->getEntityManager() + ->getUnitOfWork() + ->getEntityIdentifier($object); + + $this->referenceRepository->setReferenceIdentity($name, $identity); + } + } + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Exception/CircularReferenceException.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Exception/CircularReferenceException.php new file mode 100644 index 0000000..f295e41 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Exception/CircularReferenceException.php @@ -0,0 +1,9 @@ + + */ +abstract class AbstractExecutor +{ + /** + * Purger instance for purging database before loading data fixtures + * + * @var PurgerInterface + */ + protected $purger; + + /** + * Logger callback for logging messages when loading data fixtures + * + * @var callable + */ + protected $logger; + + /** + * Fixture reference repository + * + * @var ReferenceRepository + */ + protected $referenceRepository; + + public function __construct(ObjectManager $manager) + { + $this->referenceRepository = new ReferenceRepository($manager); + } + + /** + * @return ReferenceRepository + */ + public function getReferenceRepository() + { + return $this->referenceRepository; + } + + public function setReferenceRepository(ReferenceRepository $referenceRepository) + { + $this->referenceRepository = $referenceRepository; + } + + /** + * Sets the Purger instance to use for this executor instance. + * + * @param PurgerInterface $purger + */ + public function setPurger(PurgerInterface $purger) + { + $this->purger = $purger; + } + + /** + * @return PurgerInterface + */ + public function getPurger() + { + return $this->purger; + } + + /** + * Set the logger callable to execute with the log() method. + * + * @param callable $logger + */ + public function setLogger($logger) + { + $this->logger = $logger; + } + + /** + * Logs a message using the logger. + * + * @param string $message + */ + public function log($message) + { + $logger = $this->logger; + $logger($message); + } + + /** + * Load a fixture with the given persistence manager. + * + * @param ObjectManager $manager + * @param FixtureInterface $fixture + */ + public function load(ObjectManager $manager, FixtureInterface $fixture) + { + if ($this->logger) { + $prefix = ''; + if ($fixture instanceof OrderedFixtureInterface) { + $prefix = sprintf('[%d] ',$fixture->getOrder()); + } + $this->log('loading ' . $prefix . get_class($fixture)); + } + // additionally pass the instance of reference repository to shared fixtures + if ($fixture instanceof SharedFixtureInterface) { + $fixture->setReferenceRepository($this->referenceRepository); + } + $fixture->load($manager); + $manager->clear(); + } + + /** + * Purges the database before loading. + * + * @throws \Exception if the purger is not defined + */ + public function purge() + { + if ($this->purger === null) { + throw new \Exception('Doctrine\Common\DataFixtures\Purger\PurgerInterface instance is required if you want to purge the database before loading your data fixtures.'); + } + if ($this->logger) { + $this->log('purging database'); + } + $this->purger->purge(); + } + + /** + * Executes the given array of data fixtures. + * + * @param array $fixtures Array of fixtures to execute. + * @param boolean $append Whether to append the data fixtures or purge the database before loading. + */ + abstract public function execute(array $fixtures, $append = false); +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/MongoDBExecutor.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/MongoDBExecutor.php new file mode 100644 index 0000000..daf1078 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/MongoDBExecutor.php @@ -0,0 +1,67 @@ + + */ +class MongoDBExecutor extends AbstractExecutor +{ + /** + * Construct new fixtures loader instance. + * + * @param DocumentManager $dm DocumentManager instance used for persistence. + */ + public function __construct(DocumentManager $dm, MongoDBPurger $purger = null) + { + $this->dm = $dm; + if ($purger !== null) { + $this->purger = $purger; + $this->purger->setDocumentManager($dm); + } + parent::__construct($dm); + $this->listener = new MongoDBReferenceListener($this->referenceRepository); + $dm->getEventManager()->addEventSubscriber($this->listener); + } + + /** + * Retrieve the DocumentManager instance this executor instance is using. + * + * @return \Doctrine\ODM\MongoDB\DocumentManager + */ + public function getObjectManager() + { + return $this->dm; + } + + /** @inheritDoc */ + public function setReferenceRepository(ReferenceRepository $referenceRepository) + { + $this->dm->getEventManager()->removeEventListener( + $this->listener->getSubscribedEvents(), + $this->listener + ); + + $this->referenceRepository = $referenceRepository; + $this->listener = new MongoDBReferenceListener($this->referenceRepository); + $this->dm->getEventManager()->addEventSubscriber($this->listener); + } + + /** @inheritDoc */ + public function execute(array $fixtures, $append = false) + { + if ($append === false) { + $this->purge(); + } + foreach ($fixtures as $fixture) { + $this->load($this->dm, $fixture); + } + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php new file mode 100644 index 0000000..5bcaab6 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/ORMExecutor.php @@ -0,0 +1,75 @@ + + */ +class ORMExecutor extends AbstractExecutor +{ + /** + * @var EntityManagerInterface + */ + private $em; + + /** + * Construct new fixtures loader instance. + * + * @param EntityManagerInterface $em EntityManagerInterface instance used for persistence. + */ + public function __construct(EntityManagerInterface $em, ORMPurger $purger = null) + { + $this->em = $em; + if ($purger !== null) { + $this->purger = $purger; + $this->purger->setEntityManager($em); + } + parent::__construct($em); + $this->listener = new ORMReferenceListener($this->referenceRepository); + $em->getEventManager()->addEventSubscriber($this->listener); + } + + /** + * Retrieve the EntityManagerInterface instance this executor instance is using. + * + * @return \Doctrine\ORM\EntityManagerInterface + */ + public function getObjectManager() + { + return $this->em; + } + + /** @inheritDoc */ + public function setReferenceRepository(ReferenceRepository $referenceRepository) + { + $this->em->getEventManager()->removeEventListener( + $this->listener->getSubscribedEvents(), + $this->listener + ); + + $this->referenceRepository = $referenceRepository; + $this->listener = new ORMReferenceListener($this->referenceRepository); + $this->em->getEventManager()->addEventSubscriber($this->listener); + } + + /** @inheritDoc */ + public function execute(array $fixtures, $append = false) + { + $executor = $this; + $this->em->transactional(function(EntityManagerInterface $em) use ($executor, $fixtures, $append) { + if ($append === false) { + $executor->purge(); + } + foreach ($fixtures as $fixture) { + $executor->load($em, $fixture); + } + }); + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php new file mode 100644 index 0000000..ec634c2 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Executor/PHPCRExecutor.php @@ -0,0 +1,63 @@ + + * @author Daniel Barsotti + */ +class PHPCRExecutor extends AbstractExecutor +{ + /** + * @var DocumentManagerInterface + */ + private $dm; + + /** + * @param DocumentManagerInterface $dm manager instance used for persisting the fixtures + * @param PHPCRPurger $purger to remove the current data if append is false + */ + public function __construct(DocumentManagerInterface $dm, PHPCRPurger $purger = null) + { + parent::__construct($dm); + + $this->dm = $dm; + if ($purger !== null) { + $purger->setDocumentManager($dm); + $this->setPurger($purger); + } + } + + public function getObjectManager() + { + return $this->dm; + } + + /** @inheritDoc */ + public function execute(array $fixtures, $append = false) + { + $that = $this; + + $function = function ($dm) use ($append, $that, $fixtures) { + if ($append === false) { + $that->purge(); + } + + foreach ($fixtures as $fixture) { + $that->load($dm, $fixture); + } + }; + + if (method_exists($this->dm, 'transactional')) { + $this->dm->transactional($function); + } else { + $function($this->dm); + } + } +} + diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/FixtureInterface.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/FixtureInterface.php new file mode 100644 index 0000000..96bfb10 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/FixtureInterface.php @@ -0,0 +1,20 @@ + + */ +interface FixtureInterface +{ + /** + * Load data fixtures with the passed EntityManager + * + * @param ObjectManager $manager + */ + public function load(ObjectManager $manager); +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Loader.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Loader.php new file mode 100644 index 0000000..5c5b0e5 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Loader.php @@ -0,0 +1,381 @@ + + */ +class Loader +{ + /** + * Array of fixture object instances to execute. + * + * @var array + */ + private $fixtures = []; + + /** + * Array of ordered fixture object instances. + * + * @var array + */ + private $orderedFixtures = []; + + /** + * Determines if we must order fixtures by number + * + * @var boolean + */ + private $orderFixturesByNumber = false; + + /** + * Determines if we must order fixtures by its dependencies + * + * @var boolean + */ + private $orderFixturesByDependencies = false; + + /** + * The file extension of fixture files. + * + * @var string + */ + private $fileExtension = '.php'; + + /** + * Find fixtures classes in a given directory and load them. + * + * @param string $dir Directory to find fixture classes in. + * @return array $fixtures Array of loaded fixture object instances. + */ + public function loadFromDirectory($dir) + { + if (!is_dir($dir)) { + throw new \InvalidArgumentException(sprintf('"%s" does not exist', $dir)); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + return $this->loadFromIterator($iterator); + } + + /** + * Find fixtures classes in a given file and load them. + * + * @param string $fileName File to find fixture classes in. + * @return array $fixtures Array of loaded fixture object instances. + */ + public function loadFromFile($fileName) + { + if (!is_readable($fileName)) { + throw new \InvalidArgumentException(sprintf('"%s" does not exist or is not readable', $fileName)); + } + + $iterator = new \ArrayIterator([new \SplFileInfo($fileName)]); + return $this->loadFromIterator($iterator); + } + + /** + * Has fixture? + * + * @param FixtureInterface $fixture + * + * @return boolean + */ + public function hasFixture($fixture) + { + return isset($this->fixtures[get_class($fixture)]); + } + + /** + * Get a specific fixture instance + * + * @param string $className + * @return FixtureInterface + */ + public function getFixture($className) + { + if (!isset($this->fixtures[$className])) { + throw new \InvalidArgumentException(sprintf( + '"%s" is not a registered fixture', + $className + )); + } + + return $this->fixtures[$className]; + } + + /** + * Add a fixture object instance to the loader. + * + * @param FixtureInterface $fixture + */ + public function addFixture(FixtureInterface $fixture) + { + $fixtureClass = get_class($fixture); + + if (!isset($this->fixtures[$fixtureClass])) { + if ($fixture instanceof OrderedFixtureInterface && $fixture instanceof DependentFixtureInterface) { + throw new \InvalidArgumentException(sprintf('Class "%s" can\'t implement "%s" and "%s" at the same time.', + get_class($fixture), + 'OrderedFixtureInterface', + 'DependentFixtureInterface')); + } + + $this->fixtures[$fixtureClass] = $fixture; + + if ($fixture instanceof OrderedFixtureInterface) { + $this->orderFixturesByNumber = true; + } elseif ($fixture instanceof DependentFixtureInterface) { + $this->orderFixturesByDependencies = true; + foreach($fixture->getDependencies() as $class) { + if (class_exists($class)) { + $this->addFixture($this->createFixture($class)); + } + } + } + } + } + + /** + * Returns the array of data fixtures to execute. + * + * @return array $fixtures + */ + public function getFixtures() + { + $this->orderedFixtures = []; + + if ($this->orderFixturesByNumber) { + $this->orderFixturesByNumber(); + } + + if ($this->orderFixturesByDependencies) { + $this->orderFixturesByDependencies(); + } + + if (!$this->orderFixturesByNumber && !$this->orderFixturesByDependencies) { + $this->orderedFixtures = $this->fixtures; + } + + return $this->orderedFixtures; + } + + /** + * Check if a given fixture is transient and should not be considered a data fixtures + * class. + * + * @return boolean + */ + public function isTransient($className) + { + $rc = new \ReflectionClass($className); + if ($rc->isAbstract()) return true; + + $interfaces = class_implements($className); + return in_array(FixtureInterface::class, $interfaces) ? false : true; + } + + /** + * Creates the fixture object from the class. + * + * @param string $class + * @return FixtureInterface + */ + protected function createFixture($class) + { + return new $class(); + } + + /** + * Orders fixtures by number + * + * @todo maybe there is a better way to handle reordering + * @return void + */ + private function orderFixturesByNumber() + { + $this->orderedFixtures = $this->fixtures; + usort($this->orderedFixtures, function($a, $b) { + if ($a instanceof OrderedFixtureInterface && $b instanceof OrderedFixtureInterface) { + if ($a->getOrder() === $b->getOrder()) { + return 0; + } + return $a->getOrder() < $b->getOrder() ? -1 : 1; + } elseif ($a instanceof OrderedFixtureInterface) { + return $a->getOrder() === 0 ? 0 : 1; + } elseif ($b instanceof OrderedFixtureInterface) { + return $b->getOrder() === 0 ? 0 : -1; + } + return 0; + }); + } + + + /** + * Orders fixtures by dependencies + * + * @return void + */ + private function orderFixturesByDependencies() + { + $sequenceForClasses = []; + + // If fixtures were already ordered by number then we need + // to remove classes which are not instances of OrderedFixtureInterface + // in case fixtures implementing DependentFixtureInterface exist. + // This is because, in that case, the method orderFixturesByDependencies + // will handle all fixtures which are not instances of + // OrderedFixtureInterface + if ($this->orderFixturesByNumber) { + $count = count($this->orderedFixtures); + + for ($i = 0 ; $i < $count ; ++$i) { + if (!($this->orderedFixtures[$i] instanceof OrderedFixtureInterface)) { + unset($this->orderedFixtures[$i]); + } + } + } + + // First we determine which classes has dependencies and which don't + foreach ($this->fixtures as $fixture) { + $fixtureClass = get_class($fixture); + + if ($fixture instanceof OrderedFixtureInterface) { + continue; + } elseif ($fixture instanceof DependentFixtureInterface) { + $dependenciesClasses = $fixture->getDependencies(); + + $this->validateDependencies($dependenciesClasses); + + if (!is_array($dependenciesClasses) || empty($dependenciesClasses)) { + throw new \InvalidArgumentException(sprintf('Method "%s" in class "%s" must return an array of classes which are dependencies for the fixture, and it must be NOT empty.', 'getDependencies', $fixtureClass)); + } + + if (in_array($fixtureClass, $dependenciesClasses)) { + throw new \InvalidArgumentException(sprintf('Class "%s" can\'t have itself as a dependency', $fixtureClass)); + } + + // We mark this class as unsequenced + $sequenceForClasses[$fixtureClass] = -1; + } else { + // This class has no dependencies, so we assign 0 + $sequenceForClasses[$fixtureClass] = 0; + } + } + + // Now we order fixtures by sequence + $sequence = 1; + $lastCount = -1; + + while (($count = count($unsequencedClasses = $this->getUnsequencedClasses($sequenceForClasses))) > 0 && $count !== $lastCount) { + foreach ($unsequencedClasses as $key => $class) { + $fixture = $this->fixtures[$class]; + $dependencies = $fixture->getDependencies(); + $unsequencedDependencies = $this->getUnsequencedClasses($sequenceForClasses, $dependencies); + + if (count($unsequencedDependencies) === 0) { + $sequenceForClasses[$class] = $sequence++; + } + } + + $lastCount = $count; + } + + $orderedFixtures = []; + + // If there're fixtures unsequenced left and they couldn't be sequenced, + // it means we have a circular reference + if ($count > 0) { + $msg = 'Classes "%s" have produced a CircularReferenceException. '; + $msg .= 'An example of this problem would be the following: Class C has class B as its dependency. '; + $msg .= 'Then, class B has class A has its dependency. Finally, class A has class C as its dependency. '; + $msg .= 'This case would produce a CircularReferenceException.'; + + throw new CircularReferenceException(sprintf($msg, implode(',', $unsequencedClasses))); + } else { + // We order the classes by sequence + asort($sequenceForClasses); + + foreach ($sequenceForClasses as $class => $sequence) { + // If fixtures were ordered + $orderedFixtures[] = $this->fixtures[$class]; + } + } + + $this->orderedFixtures = array_merge($this->orderedFixtures, $orderedFixtures); + } + + private function validateDependencies($dependenciesClasses) + { + $loadedFixtureClasses = array_keys($this->fixtures); + + foreach ($dependenciesClasses as $class) { + if (!in_array($class, $loadedFixtureClasses)) { + throw new \RuntimeException(sprintf('Fixture "%s" was declared as a dependency, but it should be added in fixture loader first.', $class)); + } + } + + return true; + } + + private function getUnsequencedClasses($sequences, $classes = null) + { + $unsequencedClasses = []; + + if (is_null($classes)) { + $classes = array_keys($sequences); + } + + foreach ($classes as $class) { + if ($sequences[$class] === -1) { + $unsequencedClasses[] = $class; + } + } + + return $unsequencedClasses; + } + + /** + * Load fixtures from files contained in iterator. + * + * @param \Iterator $iterator Iterator over files from which fixtures should be loaded. + * @return array $fixtures Array of loaded fixture object instances. + */ + private function loadFromIterator(\Iterator $iterator) + { + $includedFiles = []; + foreach ($iterator as $file) { + if (($fileName = $file->getBasename($this->fileExtension)) == $file->getBasename()) { + continue; + } + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + + $fixtures = []; + $declared = get_declared_classes(); + // Make the declared classes order deterministic + sort($declared); + + foreach ($declared as $className) { + $reflClass = new \ReflectionClass($className); + $sourceFile = $reflClass->getFileName(); + + if (in_array($sourceFile, $includedFiles) && ! $this->isTransient($className)) { + $fixture = $this->createFixture($className); + $fixtures[] = $fixture; + $this->addFixture($fixture); + } + } + + return $fixtures; + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/OrderedFixtureInterface.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/OrderedFixtureInterface.php new file mode 100644 index 0000000..8f7b943 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/OrderedFixtureInterface.php @@ -0,0 +1,21 @@ + + * @author Jonathan H. Wage + */ +interface OrderedFixtureInterface +{ + /** + * Get the order of this fixture + * + * @return integer + */ + public function getOrder(); +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php new file mode 100644 index 0000000..cddbe51 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ProxyReferenceRepository.php @@ -0,0 +1,121 @@ + + * @author Anthon Pang + */ +class ProxyReferenceRepository extends ReferenceRepository +{ + /** + * Get real class name of a reference that could be a proxy + * + * @param string $className Class name of reference object + * + * @return string + */ + protected function getRealClass($className) + { + if (Version::compare('2.2.0') <= 0) { + return ClassUtils::getRealClass($className); + } + + if (substr($className, -5) === 'Proxy') { + return substr($className, 0, -5); + } + + return $className; + } + + /** + * Serialize reference repository + * + * @return string + */ + public function serialize() + { + $unitOfWork = $this->getManager()->getUnitOfWork(); + $simpleReferences = []; + + foreach ($this->getReferences() as $name => $reference) { + $className = $this->getRealClass(get_class($reference)); + + $simpleReferences[$name] = [$className, $this->getIdentifier($reference, $unitOfWork)]; + } + + $serializedData = json_encode([ + 'references' => $simpleReferences, + 'identities' => $this->getIdentities(), + ]); + + return $serializedData; + } + + /** + * Unserialize reference repository + * + * @param string $serializedData Serialized data + */ + public function unserialize($serializedData) + { + $repositoryData = json_decode($serializedData, true); + $references = $repositoryData['references']; + + foreach ($references as $name => $proxyReference) { + $this->setReference( + $name, + $this->getManager()->getReference( + $proxyReference[0], // entity class name + $proxyReference[1] // identifiers + ) + ); + } + + $identities = $repositoryData['identities']; + + foreach ($identities as $name => $identity) { + $this->setReferenceIdentity($name, $identity); + } + } + + /** + * Load data fixture reference repository + * + * @param string $baseCacheName Base cache name + * + * @return boolean + */ + public function load($baseCacheName) + { + $filename = $baseCacheName . '.ser'; + + if ( ! file_exists($filename) || ($serializedData = file_get_contents($filename)) === false) { + return false; + } + + $this->unserialize($serializedData); + + return true; + } + + /** + * Save data fixture reference repository + * + * @param string $baseCacheName Base cache name + */ + public function save($baseCacheName) + { + $serializedData = $this->serialize(); + + file_put_contents($baseCacheName . '.ser', $serializedData); + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/MongoDBPurger.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/MongoDBPurger.php new file mode 100644 index 0000000..ce5cce9 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/MongoDBPurger.php @@ -0,0 +1,58 @@ + + */ +class MongoDBPurger implements PurgerInterface +{ + /** DocumentManager instance used for persistence. */ + private $dm; + + /** + * Construct new purger instance. + * + * @param DocumentManager $dm DocumentManager instance used for persistence. + */ + public function __construct(DocumentManager $dm = null) + { + $this->dm = $dm; + } + + /** + * Set the DocumentManager instance this purger instance should use. + * + * @param DocumentManager $dm + */ + public function setDocumentManager(DocumentManager $dm) + { + $this->dm = $dm; + } + + /** + * Retrieve the DocumentManager instance this purger instance is using. + * + * @return \Doctrine\ODM\MongoDB\DocumentManager + */ + public function getObjectManager() + { + return $this->dm; + } + + /** @inheritDoc */ + public function purge() + { + $metadatas = $this->dm->getMetadataFactory()->getAllMetadata(); + foreach ($metadatas as $metadata) { + if ( ! $metadata->isMappedSuperclass) { + $this->dm->getDocumentCollection($metadata->name)->drop(); + } + } + $this->dm->getSchemaManager()->ensureIndexes(); + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/ORMPurger.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/ORMPurger.php new file mode 100644 index 0000000..af393f0 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/ORMPurger.php @@ -0,0 +1,248 @@ + + * @author Benjamin Eberlei + */ +class ORMPurger implements PurgerInterface +{ + const PURGE_MODE_DELETE = 1; + const PURGE_MODE_TRUNCATE = 2; + + /** EntityManagerInterface instance used for persistence. */ + private $em; + + /** + * If the purge should be done through DELETE or TRUNCATE statements + * + * @var int + */ + private $purgeMode = self::PURGE_MODE_DELETE; + + /** + * Table/view names to be excleded from purge + * + * @var string[] + */ + private $excluded; + + /** + * Construct new purger instance. + * + * @param EntityManagerInterface $em EntityManagerInterface instance used for persistence. + * @param string[] $excluded array of table/view names to be excleded from purge + */ + public function __construct(EntityManagerInterface $em = null, array $excluded = []) + { + $this->em = $em; + $this->excluded = $excluded; + } + + /** + * Set the purge mode + * + * @param $mode + * @return void + */ + public function setPurgeMode($mode) + { + $this->purgeMode = $mode; + } + + /** + * Get the purge mode + * + * @return int + */ + public function getPurgeMode() + { + return $this->purgeMode; + } + + /** + * Set the EntityManagerInterface instance this purger instance should use. + * + * @param EntityManagerInterface $em + */ + public function setEntityManager(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * Retrieve the EntityManagerInterface instance this purger instance is using. + * + * @return \Doctrine\ORM\EntityManagerInterface + */ + public function getObjectManager() + { + return $this->em; + } + + /** @inheritDoc */ + public function purge() + { + $classes = []; + + foreach ($this->em->getMetadataFactory()->getAllMetadata() as $metadata) { + if (! $metadata->isMappedSuperclass && ! (isset($metadata->isEmbeddedClass) && $metadata->isEmbeddedClass)) { + $classes[] = $metadata; + } + } + + $commitOrder = $this->getCommitOrder($this->em, $classes); + + // Get platform parameters + $platform = $this->em->getConnection()->getDatabasePlatform(); + + // Drop association tables first + $orderedTables = $this->getAssociationTables($commitOrder, $platform); + + // Drop tables in reverse commit order + for ($i = count($commitOrder) - 1; $i >= 0; --$i) { + $class = $commitOrder[$i]; + + if ( + (isset($class->isEmbeddedClass) && $class->isEmbeddedClass) || + $class->isMappedSuperclass || + ($class->isInheritanceTypeSingleTable() && $class->name !== $class->rootEntityName) + ) { + continue; + } + + $orderedTables[] = $this->getTableName($class, $platform); + } + + $connection = $this->em->getConnection(); + $filterExpr = $connection->getConfiguration()->getFilterSchemaAssetsExpression(); + $emptyFilterExpression = empty($filterExpr); + foreach($orderedTables as $tbl) { + if(($emptyFilterExpression||preg_match($filterExpr, $tbl)) && array_search($tbl, $this->excluded) === false){ + if ($this->purgeMode === self::PURGE_MODE_DELETE) { + $connection->executeUpdate("DELETE FROM " . $tbl); + } else { + $connection->executeUpdate($platform->getTruncateTableSQL($tbl, true)); + } + } + } + } + + /** + * @param EntityManagerInterface $em + * @param ClassMetadata[] $classes + * + * @return ClassMetadata[] + */ + private function getCommitOrder(EntityManagerInterface $em, array $classes) + { + $sorter = new TopologicalSorter(); + + foreach ($classes as $class) { + if ( ! $sorter->hasNode($class->name)) { + $sorter->addNode($class->name, $class); + } + + // $class before its parents + foreach ($class->parentClasses as $parentClass) { + $parentClass = $em->getClassMetadata($parentClass); + $parentClassName = $parentClass->getName(); + + if ( ! $sorter->hasNode($parentClassName)) { + $sorter->addNode($parentClassName, $parentClass); + } + + $sorter->addDependency($class->name, $parentClassName); + } + + foreach ($class->associationMappings as $assoc) { + if ($assoc['isOwningSide']) { + /* @var $targetClass ClassMetadata */ + $targetClass = $em->getClassMetadata($assoc['targetEntity']); + $targetClassName = $targetClass->getName(); + + if ( ! $sorter->hasNode($targetClassName)) { + $sorter->addNode($targetClassName, $targetClass); + } + + // add dependency ($targetClass before $class) + $sorter->addDependency($targetClassName, $class->name); + + // parents of $targetClass before $class, too + foreach ($targetClass->parentClasses as $parentClass) { + $parentClass = $em->getClassMetadata($parentClass); + $parentClassName = $parentClass->getName(); + + if ( ! $sorter->hasNode($parentClassName)) { + $sorter->addNode($parentClassName, $parentClass); + } + + $sorter->addDependency($parentClassName, $class->name); + } + } + } + } + + return array_reverse($sorter->sort()); + } + + /** + * @param array $classes + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @return array + */ + private function getAssociationTables(array $classes, AbstractPlatform $platform) + { + $associationTables = []; + + foreach ($classes as $class) { + foreach ($class->associationMappings as $assoc) { + if ($assoc['isOwningSide'] && $assoc['type'] == ClassMetadata::MANY_TO_MANY) { + $associationTables[] = $this->getJoinTableName($assoc, $class, $platform); + } + } + } + + return $associationTables; + } + + /** + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @return string + */ + private function getTableName($class, $platform) + { + if (isset($class->table['schema']) && !method_exists($class, 'getSchemaName')) { + return $class->table['schema'].'.'.$this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform); + } + + return $this->em->getConfiguration()->getQuoteStrategy()->getTableName($class, $platform); + } + + /** + * + * @param array $association + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform + * @return string + */ + private function getJoinTableName($assoc, $class, $platform) + { + if (isset($assoc['joinTable']['schema']) && !method_exists($class, 'getSchemaName')) { + return $assoc['joinTable']['schema'].'.'.$this->em->getConfiguration()->getQuoteStrategy()->getJoinTableName($assoc, $class, $platform); + } + + return $this->em->getConfiguration()->getQuoteStrategy()->getJoinTableName($assoc, $class, $platform); + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php new file mode 100644 index 0000000..e5c8527 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PHPCRPurger.php @@ -0,0 +1,45 @@ + + */ +class PHPCRPurger implements PurgerInterface +{ + /** + * @var DocumentManagerInterface + */ + private $dm; + + public function __construct(DocumentManagerInterface $dm = null) + { + $this->dm = $dm; + } + + public function setDocumentManager(DocumentManager $dm) + { + $this->dm = $dm; + } + + public function getObjectManager() + { + return $this->dm; + } + + /** + * @inheritDoc + */ + public function purge() + { + $session = $this->dm->getPhpcrSession(); + NodeHelper::purgeWorkspace($session); + $session->save(); + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PurgerInterface.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PurgerInterface.php new file mode 100644 index 0000000..76421fb --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Purger/PurgerInterface.php @@ -0,0 +1,18 @@ + + */ +interface PurgerInterface +{ + /** + * Purge the data from the database for the given EntityManager. + * + * @return void + */ + function purge(); +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ReferenceRepository.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ReferenceRepository.php new file mode 100644 index 0000000..da07656 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/ReferenceRepository.php @@ -0,0 +1,240 @@ + + */ +class ReferenceRepository +{ + /** + * List of named references to the fixture objects + * gathered during loads of fixtures + * + * @var array + */ + private $references = []; + + /** + * List of identifiers stored for references + * in case if reference gets unmanaged, it will + * use a proxy referenced by this identity + * + * @var array + */ + private $identities = []; + + /** + * Currently used object manager + * + * @var ObjectManager + */ + private $manager; + + public function __construct(ObjectManager $manager) + { + $this->manager = $manager; + } + + /** + * Get identifier for a unit of work + * + * @param object $reference Reference object + * @param object $uow Unit of work + * + * @return array + */ + protected function getIdentifier($reference, $uow) + { + // In case Reference is not yet managed in UnitOfWork + if ( ! $this->hasIdentifier($reference)) { + $class = $this->manager->getClassMetadata(get_class($reference)); + + return $class->getIdentifierValues($reference); + } + + // Dealing with ORM UnitOfWork + if (method_exists($uow, 'getEntityIdentifier')) { + return $uow->getEntityIdentifier($reference); + } + + // PHPCR ODM UnitOfWork + if ($this->manager instanceof PhpcrDocumentManager) { + return $uow->getDocumentId($reference); + } + + // ODM UnitOfWork + return $uow->getDocumentIdentifier($reference); + } + + /** + * Set the reference entry identified by $name + * and referenced to $reference. If $name + * already is set, it overrides it + * + * @param string $name + * @param object $reference + */ + public function setReference($name, $reference) + { + $this->references[$name] = $reference; + + if ($this->hasIdentifier($reference)) { + // in case if reference is set after flush, store its identity + $uow = $this->manager->getUnitOfWork(); + $this->identities[$name] = $this->getIdentifier($reference, $uow); + } + } + + /** + * Store the identifier of a reference + * + * @param string $name + * @param mixed $identity + */ + public function setReferenceIdentity($name, $identity) + { + $this->identities[$name] = $identity; + } + + /** + * Set the reference entry identified by $name + * and referenced to managed $object. $name must + * not be set yet + * + * Notice: in case if identifier is generated after + * the record is inserted, be sure tu use this method + * after $object is flushed + * + * @param string $name + * @param object $object - managed object + * @throws \BadMethodCallException - if repository already has + * a reference by $name + * @return void + */ + public function addReference($name, $object) + { + if (isset($this->references[$name])) { + throw new \BadMethodCallException("Reference to: ({$name}) already exists, use method setReference in order to override it"); + } + $this->setReference($name, $object); + } + + /** + * Loads an object using stored reference + * named by $name + * + * @param string $name + * @throws \OutOfBoundsException - if repository does not exist + * @return object + */ + public function getReference($name) + { + if (!$this->hasReference($name)) { + throw new \OutOfBoundsException("Reference to: ({$name}) does not exist"); + } + + $reference = $this->references[$name]; + $meta = $this->manager->getClassMetadata(get_class($reference)); + + if (!$this->manager->contains($reference) && isset($this->identities[$name])) { + $reference = $this->manager->getReference( + $meta->name, + $this->identities[$name] + ); + $this->references[$name] = $reference; // already in identity map + } + + return $reference; + } + + /** + * Check if an object is stored using reference + * named by $name + * + * @param string $name + * @return boolean + */ + public function hasReference($name) + { + return isset($this->references[$name]); + } + + /** + * Searches for reference names in the + * list of stored references + * + * @param object $reference + * @return array + */ + public function getReferenceNames($reference) + { + return array_keys($this->references, $reference, true); + } + + /** + * Checks if reference has identity stored + * + * @param string $name + */ + public function hasIdentity($name) + { + return array_key_exists($name, $this->identities); + } + + /** + * Get all stored identities + * + * @return array + */ + public function getIdentities() + { + return $this->identities; + } + + /** + * Get all stored references + * + * @return array + */ + public function getReferences() + { + return $this->references; + } + + /** + * Get object manager + * + * @return ObjectManager + */ + public function getManager() + { + return $this->manager; + } + + /** + * Checks if object has identifier already in unit of work. + * + * @param $reference + * + * @return bool + */ + private function hasIdentifier($reference) + { + // in case if reference is set after flush, store its identity + $uow = $this->manager->getUnitOfWork(); + + if ($this->manager instanceof PhpcrDocumentManager) { + return $uow->contains($reference); + } else { + return $uow->isInIdentityMap($reference); + } + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/SharedFixtureInterface.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/SharedFixtureInterface.php new file mode 100644 index 0000000..c3e145b --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/SharedFixtureInterface.php @@ -0,0 +1,23 @@ + + */ +interface SharedFixtureInterface extends FixtureInterface +{ + /** + * Set the reference repository + * + * @param ReferenceRepository $referenceRepository + */ + function setReferenceRepository(ReferenceRepository $referenceRepository); +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/TopologicalSorter.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/TopologicalSorter.php new file mode 100644 index 0000000..be4dd67 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/TopologicalSorter.php @@ -0,0 +1,180 @@ + + * @author Roman Borschel + * + * @internal this class is to be used only by data-fixtures internals: do not + * rely on it in your own libraries/applications. + */ +class TopologicalSorter +{ + /** + * Matrix of nodes (aka. vertex). + * Keys are provided hashes and values are the node definition objects. + * + * @var Vertex[] + */ + private $nodeList = []; + + /** + * Volatile variable holding calculated nodes during sorting process. + * + * @var array + */ + private $sortedNodeList = []; + + /** + * Allow or not cyclic dependencies + * + * @var boolean + */ + private $allowCyclicDependencies; + + /** + * Construct TopologicalSorter object + * + * @param boolean $allowCyclicDependencies + */ + public function __construct($allowCyclicDependencies = true) + { + $this->allowCyclicDependencies = $allowCyclicDependencies; + } + + /** + * Adds a new node (vertex) to the graph, assigning its hash and value. + * + * @param string $hash + * @param ClassMetadata $node + * + * @return void + */ + public function addNode($hash, ClassMetadata $node) + { + $this->nodeList[$hash] = new Vertex($node); + } + + /** + * Checks the existence of a node in the graph. + * + * @param string $hash + * + * @return bool + */ + public function hasNode($hash) + { + return isset($this->nodeList[$hash]); + } + + /** + * Adds a new dependency (edge) to the graph using their hashes. + * + * @param string $fromHash + * @param string $toHash + * + * @return void + */ + public function addDependency($fromHash, $toHash) + { + $definition = $this->nodeList[$fromHash]; + + $definition->dependencyList[] = $toHash; + } + + /** + * Return a valid order list of all current nodes. + * The desired topological sorting is the postorder of these searches. + * + * Note: Highly performance-sensitive method. + * + * @throws \RuntimeException + * @throws CircularReferenceException + * + * @return array + */ + public function sort() + { + foreach ($this->nodeList as $definition) { + if ($definition->state !== Vertex::NOT_VISITED) { + continue; + } + + $this->visit($definition); + } + + $sortedList = $this->sortedNodeList; + + $this->nodeList = []; + $this->sortedNodeList = []; + + return $sortedList; + } + + /** + * Visit a given node definition for reordering. + * + * Note: Highly performance-sensitive method. + * + * @throws \RuntimeException + * @throws CircularReferenceException + * + * @param Vertex $definition + */ + private function visit(Vertex $definition) + { + $definition->state = Vertex::IN_PROGRESS; + + foreach ($definition->dependencyList as $dependency) { + if ( ! isset($this->nodeList[$dependency])) { + throw new \RuntimeException(sprintf( + 'Fixture "%s" has a dependency of fixture "%s", but it not listed to be loaded.', + get_class($definition->value), + $dependency + )); + } + + $childDefinition = $this->nodeList[$dependency]; + + // allow self referencing classes + if ($definition === $childDefinition) { + continue; + } + + switch ($childDefinition->state) { + case Vertex::VISITED: + break; + case Vertex::IN_PROGRESS: + if ( ! $this->allowCyclicDependencies) { + throw new CircularReferenceException( + sprintf( + 'Graph contains cyclic dependency between the classes "%s" and' + .' "%s". An example of this problem would be the following: ' + .'Class C has class B as its dependency. Then, class B has class A has its dependency. ' + .'Finally, class A has class C as its dependency.', + $definition->value->getName(), + $childDefinition->value->getName() + ) + ); + } + break; + case Vertex::NOT_VISITED: + $this->visit($childDefinition); + } + } + + $definition->state = Vertex::VISITED; + + $this->sortedNodeList[] = $definition->value; + } +} diff --git a/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/Vertex.php b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/Vertex.php new file mode 100644 index 0000000..5b0cbf6 --- /dev/null +++ b/vendor/doctrine/data-fixtures/lib/Doctrine/Common/DataFixtures/Sorter/Vertex.php @@ -0,0 +1,43 @@ + + * + * @internal this class is to be used only by data-fixtures internals: do not + * rely on it in your own libraries/applications. This class is + * designed to work with {@see \Doctrine\Common\DataFixtures\Sorter\TopologicalSorter} + * only. + */ +class Vertex +{ + const NOT_VISITED = 0; + const IN_PROGRESS = 1; + const VISITED = 2; + + /** + * @var int one of either {@see self::NOT_VISITED}, {@see self::IN_PROGRESS} or {@see self::VISITED}. + */ + public $state = self::NOT_VISITED; + + /** + * @var ClassMetadata Actual node value + */ + public $value; + + /** + * @var string[] Map of node dependencies defined as hashes. + */ + public $dependencyList = []; + + /** + * @param ClassMetadata $value + */ + public function __construct(ClassMetadata $value) + { + $this->value = $value; + } +} diff --git a/vendor/doctrine/data-fixtures/phpunit.xml.dist b/vendor/doctrine/data-fixtures/phpunit.xml.dist new file mode 100644 index 0000000..885d430 --- /dev/null +++ b/vendor/doctrine/data-fixtures/phpunit.xml.dist @@ -0,0 +1,24 @@ + + + + + + ./tests/Doctrine/ + + + + + + lib + + + diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/BaseTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/BaseTest.php new file mode 100644 index 0000000..bde7a38 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/BaseTest.php @@ -0,0 +1,43 @@ + + */ +abstract class BaseTest extends TestCase +{ + /** + * EntityManager mock object together with + * annotation mapping driver + * + * @return EntityManager + */ + protected function getMockAnnotationReaderEntityManager() + { + $dbParams = ['driver' => 'pdo_sqlite', 'memory' => true]; + $config = Setup::createAnnotationMetadataConfiguration([__DIR__.'/TestEntity'], true); + return EntityManager::create($dbParams, $config); + } + + /** + * EntityManager mock object together with + * annotation mapping driver and pdo_sqlite + * database in memory + * + * @return EntityManager + */ + protected function getMockSqliteEntityManager() + { + $dbParams = ['driver' => 'pdo_sqlite', 'memory' => true]; + $config = Setup::createAnnotationMetadataConfiguration([__DIR__.'/TestEntity'], true); + return EntityManager::create($dbParams, $config); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/DependentFixtureTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/DependentFixtureTest.php new file mode 100644 index 0000000..66d72e0 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/DependentFixtureTest.php @@ -0,0 +1,363 @@ + + */ +class DependentFixtureTest extends BaseTest +{ + public function test_orderFixturesByDependencies_orderClassesWithASingleParent() + { + $loader = new Loader(); + $loader->addFixture(new DependentFixture3); + $loader->addFixture(new DependentFixture1); + $loader->addFixture(new DependentFixture2); + $loader->addFixture(new BaseParentFixture1); + + $orderedFixtures = $loader->getFixtures(); + + $this->assertCount(4, $orderedFixtures); + $this->assertInstanceOf(BaseParentFixture1::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture1::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture2::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture3::class, array_shift($orderedFixtures)); + } + + public function test_orderFixturesByDependencies_orderClassesWithAMultipleParents() + { + $loader = new Loader(); + + $addressFixture = new AddressFixture(); + $contactMethodFixture = new ContactMethodFixture(); + $contactFixture = new ContactFixture(); + $baseParentFixture = new BaseParentFixture1(); + $countryFixture = new CountryFixture(); + $stateFixture = new StateFixture(); + + $loader->addFixture($addressFixture); + $loader->addFixture($contactMethodFixture); + $loader->addFixture($contactFixture); + $loader->addFixture($baseParentFixture); + $loader->addFixture($countryFixture); + $loader->addFixture($stateFixture); + + $orderedFixtures = $loader->getFixtures(); + + $this->assertCount(6, $orderedFixtures); + + $contactFixtureOrder = array_search($contactFixture, $orderedFixtures); + $contactMethodFixtureOrder = array_search($contactMethodFixture, $orderedFixtures); + $addressFixtureOrder = array_search($addressFixture, $orderedFixtures); + $countryFixtureOrder = array_search($countryFixture, $orderedFixtures); + $stateFixtureOrder = array_search($stateFixture, $orderedFixtures); + $baseParentFixtureOrder = array_search($baseParentFixture, $orderedFixtures); + + // Order of fixtures is not exact. We need to test, however, that dependencies are + // indeed satisfied + + // BaseParentFixture1 has no dependencies, so it will always be first in this case + $this->assertEquals($baseParentFixtureOrder, 0); + + $this->assertGreaterThan($contactMethodFixtureOrder, $contactFixtureOrder); + $this->assertGreaterThan($addressFixtureOrder, $contactFixtureOrder); + $this->assertGreaterThan($countryFixtureOrder, $contactFixtureOrder); + $this->assertGreaterThan($stateFixtureOrder, $contactFixtureOrder); + $this->assertGreaterThan($contactMethodFixtureOrder, $contactFixtureOrder); + + $this->assertGreaterThan($stateFixtureOrder, $addressFixtureOrder); + $this->assertGreaterThan($countryFixtureOrder, $addressFixtureOrder); + } + + + public function test_orderFixturesByDependencies_circularReferencesMakeMethodThrowCircularReferenceException() + { + $loader = new Loader(); + + $loader->addFixture(new CircularReferenceFixture3); + $loader->addFixture(new CircularReferenceFixture); + $loader->addFixture(new CircularReferenceFixture2); + + $this->expectException(CircularReferenceException::class); + + $loader->getFixtures(); + } + + public function test_orderFixturesByDependencies_fixturesCantHaveItselfAsParent() + { + $loader = new Loader(); + + $loader->addFixture(new FixtureWithItselfAsParent); + + $this->expectException(InvalidArgumentException::class); + + $loader->getFixtures(); + } + + public function test_inCaseThereAreFixturesOrderedByNumberAndByDependenciesBothOrdersAreExecuted() + { + $loader = new Loader(); + $loader->addFixture(new OrderedByNumberFixture1); + $loader->addFixture(new OrderedByNumberFixture3); + $loader->addFixture(new OrderedByNumberFixture2); + $loader->addFixture(new DependentFixture3); + $loader->addFixture(new DependentFixture1); + $loader->addFixture(new DependentFixture2); + $loader->addFixture(new BaseParentFixture1); + + $orderedFixtures = $loader->getFixtures(); + + $this->assertCount(7, $orderedFixtures); + $this->assertInstanceOf(OrderedByNumberFixture1::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(OrderedByNumberFixture2::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(OrderedByNumberFixture3::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(BaseParentFixture1::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture1::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture2::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture3::class, array_shift($orderedFixtures)); + } + + public function test_inCaseAFixtureHasAnUnexistentDependencyOrIfItWasntLoaded_throwsException() + { + $loader = new Loader(); + $loader->addFixture(new FixtureWithUnexistentDependency); + + $this->expectException(RuntimeException::class); + + $loader->getFixtures(); + } + + public function test_inCaseGetFixturesReturnsDifferentResultsEachTime() + { + $loader = new Loader(); + $loader->addFixture(new DependentFixture1); + $loader->addFixture(new BaseParentFixture1); + + // Intentionally calling getFixtures() twice + $loader->getFixtures(); + $orderedFixtures = $loader->getFixtures(); + + $this->assertCount(2, $orderedFixtures); + $this->assertInstanceOf(BaseParentFixture1::class, array_shift($orderedFixtures)); + $this->assertInstanceOf(DependentFixture1::class, array_shift($orderedFixtures)); + } +} + +class DependentFixture1 implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [BaseParentFixture1::class]; + } +} + +class DependentFixture2 implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [DependentFixture1::class]; + } +} + +class DependentFixture3 implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [DependentFixture2::class]; + } +} + +class BaseParentFixture1 implements FixtureInterface +{ + public function load(ObjectManager $manager) + {} +} + +class CountryFixture implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [BaseParentFixture1::class]; + } +} + +class StateFixture implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [ + BaseParentFixture1::class, + CountryFixture::class + ]; + } +} + +class AddressFixture implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [ + BaseParentFixture1::class, + CountryFixture::class, + StateFixture::class + ]; + } +} + +class ContactMethodFixture implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [BaseParentFixture1::class]; + } +} + +class ContactFixture implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [ + AddressFixture::class, + ContactMethodFixture::class + ]; + } +} + +class CircularReferenceFixture implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [CircularReferenceFixture3::class]; + } +} + +class CircularReferenceFixture2 implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [CircularReferenceFixture::class]; + } +} + +class CircularReferenceFixture3 implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [CircularReferenceFixture2::class]; + } +} + +class FixtureWithItselfAsParent implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return [FixtureWithItselfAsParent::class]; + } +} + +class FixtureWithUnexistentDependency implements FixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getDependencies() + { + return ['UnexistentDependency']; + } +} + +class FixtureImplementingBothOrderingInterfaces implements FixtureInterface, OrderedFixtureInterface, DependentFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 1; + } + + public function getDependencies() + { + return [FixtureWithItselfAsParent::class]; + } +} + +class OrderedByNumberFixture1 implements FixtureInterface, OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 1; + } +} + +class OrderedByNumberFixture2 implements FixtureInterface, OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 5; + } +} + +class OrderedByNumberFixture3 implements FixtureInterface, OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 10; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorSharedFixtureTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorSharedFixtureTest.php new file mode 100644 index 0000000..419f495 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorSharedFixtureTest.php @@ -0,0 +1,79 @@ + + */ +class ORMExecutorSharedFixtureTest extends BaseTest +{ + const TEST_ENTITY_ROLE = Role::class; + const TEST_ENTITY_USER = User::class; + + public function testFixtureExecution() + { + $em = $this->getMockAnnotationReaderEntityManager(); + $purger = new ORMPurger(); + $executor = new ORMExecutor($em, $purger); + + $referenceRepository = $executor->getReferenceRepository(); + $fixture = $this->getMockFixture(); + $fixture->expects($this->once()) + ->method('load') + ->with($em); + + $fixture->expects($this->once()) + ->method('setReferenceRepository') + ->with($referenceRepository); + + $executor->execute([$fixture], true); + } + + public function testSharedFixtures() + { + if (!extension_loaded('pdo_sqlite')) { + $this->markTestSkipped('Missing pdo_sqlite extension.'); + } + + $em = $this->getMockSqliteEntityManager(); + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema([]); + $schemaTool->createSchema([ + $em->getClassMetadata(self::TEST_ENTITY_ROLE), + $em->getClassMetadata(self::TEST_ENTITY_USER) + ]); + + $purger = new ORMPurger(); + $executor = new ORMExecutor($em, $purger); + + $userFixture = new TestFixtures\UserFixture; + $roleFixture = new TestFixtures\RoleFixture; + $executor->execute([$roleFixture, $userFixture], true); + + $referenceRepository = $executor->getReferenceRepository(); + $references = $referenceRepository->getReferences(); + + $this->assertCount(2, $references); + $roleReference = $referenceRepository->getReference('admin-role'); + $this->assertInstanceOf(Role::class, $roleReference); + $this->assertEquals('admin', $roleReference->getName()); + + $userReference = $referenceRepository->getReference('admin'); + $this->assertInstanceOf(User::class, $userReference); + $this->assertEquals('admin@example.com', $userReference->getEmail()); + } + + private function getMockFixture() + { + return $this->createMock(SharedFixtureInterface::class); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorTest.php new file mode 100644 index 0000000..e50feb2 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/ORMExecutorTest.php @@ -0,0 +1,64 @@ + + */ +class ORMExecutorTest extends BaseTest +{ + public function testExecuteWithNoPurge() + { + $em = $this->getMockSqliteEntityManager(); + $purger = $this->getMockPurger(); + $purger->expects($this->once()) + ->method('setEntityManager') + ->with($em); + $executor = new ORMExecutor($em, $purger); + $fixture = $this->getMockFixture(); + $fixture->expects($this->once()) + ->method('load') + ->with($em); + $executor->execute([$fixture], true); + } + + public function testExecuteWithPurge() + { + $em = $this->getMockSqliteEntityManager(); + $purger = $this->getMockPurger(); + $purger->expects($this->once()) + ->method('purge') + ->will($this->returnValue(null)); + $executor = new ORMExecutor($em, $purger); + $fixture = $this->getMockFixture(); + $fixture->expects($this->once()) + ->method('load') + ->with($em); + $executor->execute([$fixture], false); + } + + public function testExecuteTransaction() + { + $em = $this->getMockSqliteEntityManager(); + $executor = new ORMExecutor($em); + $fixture = $this->getMockFixture(); + $executor->execute([$fixture], true); + } + + private function getMockFixture() + { + return $this->createMock(FixtureInterface::class); + } + + private function getMockPurger() + { + return $this->createMock(ORMPurger::class); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/PHPCRExecutorTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/PHPCRExecutorTest.php new file mode 100644 index 0000000..c601e37 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Executor/PHPCRExecutorTest.php @@ -0,0 +1,186 @@ + + * + * @covers \Doctrine\Common\DataFixtures\Executor\PHPCRExecutor + */ +class PHPCRExecutorTest extends BaseTest +{ + public function testExecuteSingleFixtureWithNoPurge() + { + $dm = $this->getDocumentManager(); + $executor = new PHPCRExecutor($dm); + $fixture = $this->getMockFixture(); + + $fixture->expects($this->once())->method('load')->with($dm); + $dm + ->expects($this->once()) + ->method('transactional') + ->with($this->isType('callable')) + ->will($this->returnCallback(function ($callback) use ($dm) { + return $callback($dm); + })); + + $executor->execute([$fixture], true); + } + + public function testExecuteMultipleFixturesWithNoPurge() + { + $dm = $this->getDocumentManager(); + $executor = new PHPCRExecutor($dm); + $fixture1 = $this->getMockFixture(); + $fixture2 = $this->getMockFixture(); + + $fixture1->expects($this->once())->method('load')->with($dm); + $fixture2->expects($this->once())->method('load')->with($dm); + $dm + ->expects($this->once()) + ->method('transactional') + ->with($this->isType('callable')) + ->will($this->returnCallback(function ($callback) use ($dm) { + return $callback($dm); + })); + + $executor->execute([$fixture1, $fixture2], true); + } + + public function testExecuteFixtureWithPurge() + { + $dm = $this->getDocumentManager(); + $purger = $this->getPurger(); + $executor = new PHPCRExecutor($dm, $purger); + $fixture = $this->getMockFixture(); + + $fixture->expects($this->once())->method('load')->with($dm); + $dm + ->expects($this->once()) + ->method('transactional') + ->with($this->isType('callable')) + ->will($this->returnCallback(function ($callback) use ($dm) { + return $callback($dm); + })); + $purger->expects($this->once())->method('purge'); + + $executor->execute([$fixture], false); + } + + public function testExecuteFixtureWithoutPurge() + { + $dm = $this->getDocumentManager(); + $purger = $this->getPurger(); + $executor = new PHPCRExecutor($dm, $purger); + $fixture = $this->getMockFixture(); + + $fixture->expects($this->once())->method('load')->with($dm); + $dm + ->expects($this->once()) + ->method('transactional') + ->with($this->isType('callable')) + ->will($this->returnCallback(function ($callback) use ($dm) { + return $callback($dm); + })); + $purger->expects($this->never())->method('purge'); + + $executor->execute([$fixture], true); + } + + public function testFailedTransactionalStopsPurgingAndFixtureLoading() + { + $dm = $this->getDocumentManager(); + $purger = $this->getPurger(); + $executor = new PHPCRExecutor($dm, $purger); + $fixture = $this->getMockFixture(); + $exception = new Exception(); + + $fixture->expects($this->never())->method('load'); + $dm->expects($this->once())->method('transactional')->will($this->throwException($exception)); + $purger->expects($this->never())->method('purge'); + + try { + $executor->execute([$fixture], true); + } catch (\Exception $caughtException) { + $this->assertSame($exception, $caughtException); + } + } + + /** + * @return PHPCRPurger|\PHPUnit_Framework_MockObject_MockObject + */ + private function getPurger() + { + return $this->createMock(PHPCRPurger::class); + } + + /** + * @return DocumentManager|\PHPUnit_Framework_MockObject_MockObject + */ + private function getDocumentManager() + { + $this->loadDocumentManagerClass(); + + return $this + ->getMockBuilder(DocumentManager::class) + ->setMethods([ + 'transactional', + 'flush', + 'clear', + ]) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return FixtureInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private function getMockFixture() + { + return $this->createMock(FixtureInterface::class); + } + + /** + * Ensures that the {@see \Doctrine\ODM\PHPCR\DocumentManager} class exists + */ + private function loadDocumentManagerClass() + { + + if (class_exists(DocumentManager::class)) { + return; + } + + // hold my beer while I do some mocking + eval( + <<<'PHP' +namespace Doctrine\ODM\PHPCR; + +class DocumentManager implements \Doctrine\Common\Persistence\ObjectManager +{ + public function find($className, $id) {} + public function persist($object) {} + public function remove($object) {} + public function merge($object) {} + public function clear($objectName = null) {} + public function detach($object) {} + public function refresh($object) {} + public function flush() {} + public function getRepository($className) {} + public function getClassMetadata($className) {} + public function getMetadataFactory() {} + public function initializeObject($obj) {} + public function contains($object) {} +} +PHP + ); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/FixtureTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/FixtureTest.php new file mode 100644 index 0000000..1ca9976 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/FixtureTest.php @@ -0,0 +1,33 @@ + + */ +class FixtureTest extends BaseTest +{ + public function testFixtureInterface() + { + $em = $this->createMock(ObjectManager::class); + $fixture = new MyFixture2(); + $fixture->load($em); + + self::assertTrue($fixture->loaded); + } +} + +class MyFixture2 implements FixtureInterface +{ + public $loaded = false; + + public function load(ObjectManager $manager) + { + $this->loaded = true; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/LoaderTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/LoaderTest.php new file mode 100644 index 0000000..0b6e3f7 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/LoaderTest.php @@ -0,0 +1,61 @@ + + */ +class LoaderTest extends BaseTest +{ + public function testLoadFromDirectory() + { + $loader = new Loader(); + $loader->addFixture($this->getMockBuilder(FixtureInterface::class)->setMockClassName('Mock1')->getMock()); + $loader->addFixture($this->getMockBuilder(FixtureInterface::class)->setMockClassName('Mock2')->getMock()); + $loader->addFixture($this->getMockBuilder(SharedFixtureInterface::class)->setMockClassName('Mock3')->getMock()); + + $this->assertCount(3, $loader->getFixtures()); + + $loader->loadFromDirectory(__DIR__.'/TestFixtures'); + $this->assertCount(7, $loader->getFixtures()); + $this->assertTrue($loader->isTransient(NotAFixture::class)); + $this->assertFalse($loader->isTransient(MyFixture1::class)); + } + + public function testLoadFromFile() + { + $loader = new Loader(); + $loader->addFixture($this->getMockBuilder(FixtureInterface::class)->setMockClassName('Mock1')->getMock()); + $loader->addFixture($this->getMockBuilder(FixtureInterface::class)->setMockClassName('Mock2')->getMock()); + $loader->addFixture($this->getMockBuilder(SharedFixtureInterface::class)->setMockClassName('Mock3')->getMock()); + + $this->assertCount(3, $loader->getFixtures()); + + $loader->loadFromFile(__DIR__.'/TestFixtures/MyFixture1.php'); + $this->assertCount(4, $loader->getFixtures()); + $loader->loadFromFile(__DIR__.'/TestFixtures/NotAFixture.php'); + $this->assertCount(4, $loader->getFixtures()); + $loader->loadFromFile(__DIR__.'/TestFixtures/MyFixture2.php'); + $this->assertCount(5, $loader->getFixtures()); + $this->assertTrue($loader->isTransient(NotAFixture::class)); + $this->assertFalse($loader->isTransient(MyFixture1::class)); + } + + public function testGetFixture() + { + $loader = new Loader(); + $loader->loadFromFile(__DIR__.'/TestFixtures/MyFixture1.php'); + + $fixture = $loader->getFixture(MyFixture1::class); + + $this->assertInstanceOf(MyFixture1::class, $fixture); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/OrderedFixtureTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/OrderedFixtureTest.php new file mode 100644 index 0000000..3ad14ce --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/OrderedFixtureTest.php @@ -0,0 +1,72 @@ + + */ +class OrderedFixtureTest extends BaseTest +{ + public function testFixtureOrder() + { + $loader = new Loader(); + $loader->addFixture(new OrderedFixture1); + $loader->addFixture(new OrderedFixture2); + $loader->addFixture(new OrderedFixture3); + $loader->addFixture(new BaseFixture1); + + $orderedFixtures = $loader->getFixtures(); + + $this->assertCount(4, $orderedFixtures); + $this->assertInstanceOf(BaseFixture1::class, $orderedFixtures[0]); + $this->assertInstanceOf(OrderedFixture2::class, $orderedFixtures[1]); + $this->assertInstanceOf(OrderedFixture1::class, $orderedFixtures[2]); + $this->assertInstanceOf(OrderedFixture3::class, $orderedFixtures[3]); + } +} + +class OrderedFixture1 implements FixtureInterface, OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 5; + } +} + +class OrderedFixture2 implements FixtureInterface, OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 2; + } +} + +class OrderedFixture3 implements FixtureInterface, OrderedFixtureInterface +{ + public function load(ObjectManager $manager) + {} + + public function getOrder() + { + return 8; + } +} + +class BaseFixture1 implements FixtureInterface +{ + public function load(ObjectManager $manager) + {} +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ProxyReferenceRepositoryTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ProxyReferenceRepositoryTest.php new file mode 100644 index 0000000..9f72298 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ProxyReferenceRepositoryTest.php @@ -0,0 +1,141 @@ + + * @author Anthon Pang + */ +class ProxyReferenceRepositoryTest extends BaseTest +{ + const TEST_ENTITY_ROLE = Role::class; + + public function testReferenceEntry() + { + $em = $this->getMockAnnotationReaderEntityManager(); + $role = new TestEntity\Role; + $role->setName('admin'); + $meta = $em->getClassMetadata(self::TEST_ENTITY_ROLE); + $meta->getReflectionProperty('id')->setValue($role, 1); + + $referenceRepo = new ProxyReferenceRepository($em); + $referenceRepo->addReference('test', $role); + + $references = $referenceRepo->getReferences(); + + $this->assertCount(1, $references); + $this->assertArrayHasKey('test', $references); + $this->assertInstanceOf(self::TEST_ENTITY_ROLE, $references['test']); + } + + public function testReferenceIdentityPopulation() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = $this->getMockBuilder(ProxyReferenceRepository::class) + ->setConstructorArgs([$em]) + ->getMock(); + $em->getEventManager()->addEventSubscriber( + new ORMReferenceListener($referenceRepository) + ); + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema([]); + $schemaTool->createSchema([ + $em->getClassMetadata(self::TEST_ENTITY_ROLE) + ]); + + $referenceRepository->expects($this->once()) + ->method('addReference') + ->with('admin-role'); + + $referenceRepository->expects($this->once()) + ->method('getReferenceNames') + ->will($this->returnValue(['admin-role'])); + + $referenceRepository->expects($this->once()) + ->method('setReferenceIdentity') + ->with('admin-role', ['id' => 1]); + + $roleFixture = new TestFixtures\RoleFixture; + $roleFixture->setReferenceRepository($referenceRepository); + $roleFixture->load($em); + } + + public function testReferenceReconstruction() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = new ProxyReferenceRepository($em); + $listener = new ORMReferenceListener($referenceRepository); + $em->getEventManager()->addEventSubscriber($listener); + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema([]); + $schemaTool->createSchema([ + $em->getClassMetadata(self::TEST_ENTITY_ROLE) + ]); + $roleFixture = new TestFixtures\RoleFixture; + $roleFixture->setReferenceRepository($referenceRepository); + + $roleFixture->load($em); + // first test against managed state + $ref = $referenceRepository->getReference('admin-role'); + + $this->assertNotInstanceOf(Proxy::class, $ref); + + // test reference reconstruction from serialized data (was managed) + $serializedData = $referenceRepository->serialize(); + + $proxyReferenceRepository = new ProxyReferenceRepository($em); + $proxyReferenceRepository->unserialize($serializedData); + + $ref = $proxyReferenceRepository->getReference('admin-role'); + + // before clearing, the reference is not yet a proxy + $this->assertNotInstanceOf(Proxy::class, $ref); + $this->assertInstanceOf(self::TEST_ENTITY_ROLE, $ref); + + // now test reference reconstruction from identity + $em->clear(); + $ref = $referenceRepository->getReference('admin-role'); + + $this->assertInstanceOf(Proxy::class, $ref); + + // test reference reconstruction from serialized data (was identity) + $serializedData = $referenceRepository->serialize(); + + $proxyReferenceRepository = new ProxyReferenceRepository($em); + $proxyReferenceRepository->unserialize($serializedData); + + $ref = $proxyReferenceRepository->getReference('admin-role'); + + $this->assertInstanceOf(Proxy::class, $ref); + } + + public function testReferenceMultipleEntries() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = new ProxyReferenceRepository($em); + $em->getEventManager()->addEventSubscriber(new ORMReferenceListener($referenceRepository)); + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema([$em->getClassMetadata(self::TEST_ENTITY_ROLE)]); + + $role = new TestEntity\Role; + $role->setName('admin'); + + $em->persist($role); + $referenceRepository->addReference('admin', $role); + $referenceRepository->addReference('duplicate', $role); + $em->flush(); + $em->clear(); + + $this->assertInstanceOf(Proxy::class, $referenceRepository->getReference('admin')); + $this->assertInstanceOf(Proxy::class, $referenceRepository->getReference('duplicate')); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/MongoDBPurgerTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/MongoDBPurgerTest.php new file mode 100644 index 0000000..d50cd0e --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/MongoDBPurgerTest.php @@ -0,0 +1,67 @@ +markTestSkipped('Missing doctrine/mongodb-odm'); + } + + $root = dirname(dirname(dirname(dirname(dirname(__DIR__))))); + + $config = new Configuration(); + $config->setProxyDir($root . '/generate/proxies'); + $config->setProxyNamespace('Proxies'); + $config->setHydratorDir($root . '/generate/hydrators'); + $config->setHydratorNamespace('Hydrators'); + $config->setMetadataDriverImpl(AnnotationDriver::create(dirname(__DIR__) . '/TestDocument')); + AnnotationDriver::registerAnnotationClasses(); + + $dm = DocumentManager::create(null, $config); + if (!$dm->getConnection()->connect()) { + $this->markTestSkipped('Unable to connect to MongoDB'); + } + + return $dm; + } + + private function getPurger() + { + return new MongoDBPurger($this->getDocumentManager()); + } + + public function testPurgeKeepsIndices() + { + $purger = $this->getPurger(); + $dm = $purger->getObjectManager(); + + $collection = $dm->getDocumentCollection(self::TEST_DOCUMENT_ROLE); + $collection->drop(); + + $this->assertCount(0, $collection->getIndexInfo()); + + $role = new Role; + $role->setName('role'); + $dm->persist($role); + $dm->flush(); + + $schema = $dm->getSchemaManager()->ensureDocumentIndexes(self::TEST_DOCUMENT_ROLE); + $this->assertCount(2, $collection->getIndexInfo()); + + $purger->purge(); + $this->assertCount(2, $collection->getIndexInfo()); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerExcludeTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerExcludeTest.php new file mode 100644 index 0000000..41452a1 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerExcludeTest.php @@ -0,0 +1,100 @@ +markTestSkipped('Missing pdo_sqlite extension.'); + } + + $dbParams = ['driver' => 'pdo_sqlite', 'memory' => true]; + $config = Setup::createAnnotationMetadataConfiguration([__DIR__.'/../TestPurgeEntity'], true); + $em = EntityManager::create($dbParams, $config); + + $connection = $em->getConnection(); + $configuration = $connection->getConfiguration(); + $configuration->setFilterSchemaAssetsExpression(null); + + $schemaTool = new \Doctrine\ORM\Tools\SchemaTool($em); + $schemaTool->dropDatabase(); + $schemaTool->createSchema([ + $em->getClassMetadata(self::TEST_ENTITY_INCLUDED), + $em->getClassMetadata(self::TEST_ENTITY_EXCLUDED) + ]); + + $entity = new ExcludedEntity(); + $entity->setId(1); + $em->persist($entity); + + $entity = new IncludedEntity(); + $entity->setId(1); + $em->persist($entity); + + $em->flush(); + + return $em; + } + + /** + * Execute test purge + * + * @param string|null $expression + * @param array $list + */ + public function executeTestPurge($expression, array $list){ + $em = $this->loadTestData(); + $excludedRepository = $em->getRepository(self::TEST_ENTITY_EXCLUDED); + $includedRepository = $em->getRepository(self::TEST_ENTITY_INCLUDED); + + $excluded = $excludedRepository->findAll(); + $included = $includedRepository->findAll(); + + $this->assertGreaterThan(0, count($included)); + $this->assertGreaterThan(0, count($excluded)); + + $connection = $em->getConnection(); + $configuration = $connection->getConfiguration(); + $configuration->setFilterSchemaAssetsExpression($expression); + + $purger = new ORMPurger($em,$list); + $purger->purge(); + + $excluded = $excludedRepository->findAll(); + $included = $includedRepository->findAll(); + + $this->assertCount(0, $included); + $this->assertGreaterThan(0, count($excluded)); + } + + /** + * Test for purge exclusion usig dbal filter expression regexp. + */ + public function testPurgeExcludeUsingFilterExpression(){ + $this->executeTestPurge('~^(?!ExcludedEntity)~', []); + } + + /** + * Test for purge exclusion usig explicit exclution list. + */ + public function testPurgeExcludeUsingList(){ + $this->executeTestPurge(null, ['ExcludedEntity']); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerTest.php new file mode 100644 index 0000000..d5d53b9 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Purger/ORMPurgerTest.php @@ -0,0 +1,63 @@ + + */ +class ORMPurgerTest extends BaseTest +{ + const TEST_ENTITY_USER = TestEntity\User::class; + const TEST_ENTITY_USER_WITH_SCHEMA = TestEntity\UserWithSchema::class; + const TEST_ENTITY_QUOTED = TestEntity\Quoted::class; + + + public function testGetAssociationTables() + { + $em = $this->getMockAnnotationReaderEntityManager(); + $metadata = $em->getClassMetadata(self::TEST_ENTITY_USER); + $platform = $em->getConnection()->getDatabasePlatform(); + $purger = new ORMPurger($em); + $class = new ReflectionClass(ORMPurger::class); + $method = $class->getMethod('getAssociationTables'); + $method->setAccessible(true); + $associationTables = $method->invokeArgs($purger, [[$metadata], $platform]); + $this->assertEquals($associationTables[0], 'readers.author_reader'); + } + + public function testGetAssociationTablesQuoted() + { + $em = $this->getMockAnnotationReaderEntityManager(); + $metadata = $em->getClassMetadata(self::TEST_ENTITY_QUOTED); + $platform = $em->getConnection()->getDatabasePlatform(); + $purger = new ORMPurger($em); + $class = new ReflectionClass(ORMPurger::class); + $method = $class->getMethod('getAssociationTables'); + $method->setAccessible(true); + $associationTables = $method->invokeArgs($purger, [[$metadata], $platform]); + $this->assertEquals($associationTables[0], '"INSERT"'); + } + + public function testTableNameWithSchema() + { + $em = $this->getMockAnnotationReaderEntityManager(); + $metadata = $em->getClassMetadata(self::TEST_ENTITY_USER_WITH_SCHEMA); + $platform = $em->getConnection()->getDatabasePlatform(); + $purger = new ORMPurger($em); + $class = new ReflectionClass(ORMPurger::class); + $method = $class->getMethod('getTableName'); + $method->setAccessible(true); + $tableName = $method->invokeArgs($purger, [$metadata, $platform]); + $this->assertStringStartsWith('test_schema',$tableName); + } + +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ReferenceRepositoryTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ReferenceRepositoryTest.php new file mode 100644 index 0000000..64a989b --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/ReferenceRepositoryTest.php @@ -0,0 +1,212 @@ + + * @author Manuel Gonalez + */ +class ReferenceRepositoryTest extends BaseTest +{ + public function testReferenceEntry() + { + $em = $this->getMockAnnotationReaderEntityManager(); + + $role = new TestEntity\Role; + $role->setName('admin'); + + $meta = $em->getClassMetadata(Role::class); + $meta->getReflectionProperty('id')->setValue($role, 1); + + $referenceRepo = new ReferenceRepository($em); + $this->assertSame($em, $referenceRepo->getManager()); + + $referenceRepo->addReference('test', $role); + + $references = $referenceRepo->getReferences(); + $this->assertCount(1, $references); + $this->assertArrayHasKey('test', $references); + $this->assertInstanceOf(Role::class, $references['test']); + } + + public function testReferenceIdentityPopulation() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = $this->getMockBuilder(ReferenceRepository::class) + ->setConstructorArgs([$em]) + ->getMock(); + $em->getEventManager()->addEventSubscriber( + new ORMReferenceListener($referenceRepository) + ); + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema([]); + $schemaTool->createSchema([ + $em->getClassMetadata(Role::class) + ]); + + $referenceRepository->expects($this->once()) + ->method('addReference') + ->with('admin-role'); + + $referenceRepository->expects($this->once()) + ->method('getReferenceNames') + ->will($this->returnValue(['admin-role'])); + + $referenceRepository->expects($this->once()) + ->method('setReferenceIdentity') + ->with('admin-role', ['id' => 1]); + + $roleFixture = new TestFixtures\RoleFixture; + $roleFixture->setReferenceRepository($referenceRepository); + + $roleFixture->load($em); + } + + public function testReferenceReconstruction() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = new ReferenceRepository($em); + $em->getEventManager()->addEventSubscriber( + new ORMReferenceListener($referenceRepository) + ); + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema([]); + $schemaTool->createSchema([ + $em->getClassMetadata(Role::class) + ]); + $roleFixture = new TestFixtures\RoleFixture; + $roleFixture->setReferenceRepository($referenceRepository); + + $roleFixture->load($em); + // first test against managed state + $ref = $referenceRepository->getReference('admin-role'); + + $this->assertNotInstanceOf(Proxy::class, $ref); + + // now test reference reconstruction from identity + $em->clear(); + $ref = $referenceRepository->getReference('admin-role'); + + $this->assertInstanceOf(Proxy::class, $ref); + } + + public function testReferenceMultipleEntries() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = new ReferenceRepository($em); + $em->getEventManager()->addEventSubscriber(new ORMReferenceListener($referenceRepository)); + $schemaTool = new SchemaTool($em); + $schemaTool->createSchema([$em->getClassMetadata(Role::class)]); + + $role = new TestEntity\Role; + $role->setName('admin'); + + $em->persist($role); + $referenceRepository->addReference('admin', $role); + $referenceRepository->addReference('duplicate', $role); + $em->flush(); + $em->clear(); + + $this->assertInstanceOf(Proxy::class, $referenceRepository->getReference('admin')); + $this->assertInstanceOf(Proxy::class, $referenceRepository->getReference('duplicate')); + } + + public function testUndefinedReference() + { + $referenceRepository = new ReferenceRepository($this->getMockSqliteEntityManager()); + + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Reference to: (foo) does not exist'); + + $referenceRepository->getReference('foo'); + } + + public function testThrowsExceptionAddingDuplicatedReference() + { + $referenceRepository = new ReferenceRepository($this->getMockSqliteEntityManager()); + $referenceRepository->addReference('duplicated_reference', new \stdClass()); + + $this->expectException(\BadMethodCallException::class); + $this->expectExceptionMessage('Reference to: (duplicated_reference) already exists, use method setReference in order to override it'); + + $referenceRepository->addReference('duplicated_reference', new \stdClass()); + } + + public function testThrowsExceptionTryingToGetWrongReference() + { + $referenceRepository = new ReferenceRepository($this->getMockSqliteEntityManager()); + + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Reference to: (missing_reference) does not exist'); + + $referenceRepository->getReference('missing_reference'); + } + + public function testHasIdentityCheck() + { + $role = new Role(); + $referenceRepository = new ReferenceRepository($this->getMockSqliteEntityManager()); + $referenceRepository->setReferenceIdentity('entity', $role); + + $this->assertTrue($referenceRepository->hasIdentity('entity')); + $this->assertFalse($referenceRepository->hasIdentity('invalid_entity')); + $this->assertEquals(['entity' => $role], $referenceRepository->getIdentities()); + } + + public function testSetReferenceHavingIdentifier() + { + $em = $this->getMockSqliteEntityManager(); + $referenceRepository = new ReferenceRepository($em); + + $schemaTool = new SchemaTool($em); + $schemaTool->dropSchema([]); + $schemaTool->createSchema([ + $em->getClassMetadata(Role::class) + ]); + + $role = new Role(); + $role->setName('role_name'); + $em->persist($role); + $em->flush(); + + $referenceRepository->setReference('entity', $role); + $identities = $referenceRepository->getIdentities(); + $this->assertCount(1, $identities); + $this->assertArrayHasKey('entity', $identities); + } + + public function testGetIdentifierWhenHasNotBeenManagedYetByUnitOfWork() + { + $role = new Role(); + $identitiesExpected = ['id' => 1]; + + /** @var UnitOfWork | ProphecyInterface $uow */ + $uow = $this->prophesize(UnitOfWork::class); + $uow->isInIdentityMap($role)->shouldBeCalledTimes(2)->willReturn(true, false); + + /** @var ClassMetadata $classMetadata */ + $classMetadata = $this->prophesize(ClassMetadata::class); + $classMetadata->getIdentifierValues($role)->shouldBeCalled()->willReturn($identitiesExpected); + + /** @var EntityManagerInterface | ProphecyInterface $em */ + $em = $this->prophesize(EntityManagerInterface::class); + $em->getUnitOfWork()->shouldBeCalled()->willReturn($uow); + $em->getClassMetadata(Role::class)->shouldBeCalled()->willReturn($classMetadata); + + $referenceRepository = new ReferenceRepository($em->reveal()); + $referenceRepository->setReference('entity', $role); + $identities = $referenceRepository->getIdentities(); + + $this->assertEquals($identitiesExpected, $identities['entity']); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/TopologicalSorterTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/TopologicalSorterTest.php new file mode 100644 index 0000000..46df179 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/TopologicalSorterTest.php @@ -0,0 +1,166 @@ + + * + * @covers \Doctrine\Common\DataFixtures\Sorter\TopologicalSorter + */ +class TopologicalSorterTest extends BaseTest +{ + public function testSuccessSortLinearDependency() + { + $sorter = new TopologicalSorter(); + + $node1 = new ClassMetadata(1); + $node2 = new ClassMetadata(2); + $node3 = new ClassMetadata(3); + $node4 = new ClassMetadata(4); + $node5 = new ClassMetadata(5); + + $sorter->addNode('1', $node1); + $sorter->addNode('2', $node2); + $sorter->addNode('3', $node3); + $sorter->addNode('4', $node4); + $sorter->addNode('5', $node5); + + $sorter->addDependency('1', '2'); + $sorter->addDependency('2', '3'); + $sorter->addDependency('3', '4'); + $sorter->addDependency('5', '1'); + + $sortedList = $sorter->sort(); + $correctList = [$node4, $node3, $node2, $node1, $node5]; + + self::assertSame($correctList, $sortedList); + } + + public function testSuccessSortMultiDependency() + { + $sorter = new TopologicalSorter(); + + $node1 = new ClassMetadata(1); + $node2 = new ClassMetadata(2); + $node3 = new ClassMetadata(3); + $node4 = new ClassMetadata(4); + $node5 = new ClassMetadata(5); + + $sorter->addNode('1', $node1); + $sorter->addNode('2', $node2); + $sorter->addNode('3', $node3); + $sorter->addNode('4', $node4); + $sorter->addNode('5', $node5); + + $sorter->addDependency('3', '2'); + $sorter->addDependency('3', '4'); + $sorter->addDependency('3', '5'); + $sorter->addDependency('4', '1'); + $sorter->addDependency('5', '1'); + + $sortedList = $sorter->sort(); + $correctList = [$node1, $node2, $node4, $node5, $node3]; + + self::assertSame($correctList, $sortedList); + } + + public function testSortCyclicDependency() + { + $sorter = new TopologicalSorter(); + + $node1 = new ClassMetadata(1); + $node2 = new ClassMetadata(2); + $node3 = new ClassMetadata(3); + + $sorter->addNode('1', $node1); + $sorter->addNode('2', $node2); + $sorter->addNode('3', $node3); + + $sorter->addDependency('1', '2'); + $sorter->addDependency('2', '3'); + $sorter->addDependency('3', '1'); + + $sortedList = $sorter->sort(); + $correctList = [$node3, $node2, $node1]; + + self::assertSame($correctList, $sortedList); + + $sorter->sort(); + } + + public function testFailureSortCyclicDependency() + { + $sorter = new TopologicalSorter(false); + + $node1 = new ClassMetadata(1); + $node2 = new ClassMetadata(2); + $node3 = new ClassMetadata(3); + + $sorter->addNode('1', $node1); + $sorter->addNode('2', $node2); + $sorter->addNode('3', $node3); + + $sorter->addDependency('1', '2'); + $sorter->addDependency('2', '3'); + $sorter->addDependency('3', '1'); + + $this->expectException(CircularReferenceException::class); + + $sorter->sort(); + } + + public function testNoFailureOnSelfReferencingDependency() + { + $sorter = new TopologicalSorter(); + + $node1 = new ClassMetadata(1); + $node2 = new ClassMetadata(2); + $node3 = new ClassMetadata(3); + $node4 = new ClassMetadata(4); + $node5 = new ClassMetadata(5); + + $sorter->addNode('1', $node1); + $sorter->addNode('2', $node2); + $sorter->addNode('3', $node3); + $sorter->addNode('4', $node4); + $sorter->addNode('5', $node5); + + $sorter->addDependency('1', '2'); + $sorter->addDependency('1', '1'); + $sorter->addDependency('2', '3'); + $sorter->addDependency('3', '4'); + $sorter->addDependency('5', '1'); + + $sortedList = $sorter->sort(); + $correctList = [$node4, $node3, $node2, $node1, $node5]; + + self::assertSame($correctList, $sortedList); + } + + public function testFailureSortMissingDependency() + { + $sorter = new TopologicalSorter(); + + $node1 = new ClassMetadata(1); + + $sorter->addNode('1', $node1); + + $sorter->addDependency('1', '2'); + + $this->expectException(\RuntimeException::class); + + $sorter->sort(); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/VertexTest.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/VertexTest.php new file mode 100644 index 0000000..d11ed95 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/Sorter/VertexTest.php @@ -0,0 +1,26 @@ + + * + * @covers \Doctrine\Common\DataFixtures\Sorter\Vertex + */ +class VertexTest extends BaseTest +{ + public function testNode() + { + $value = new ClassMetadata('\Sample\Entity'); + $node = new Vertex($value); + + self::assertSame($value, $node->value); + self::assertSame(Vertex::NOT_VISITED, $node->state); + self::assertEmpty($node->dependencyList); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestDocument/Role.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestDocument/Role.php new file mode 100644 index 0000000..aa6e3db --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestDocument/Role.php @@ -0,0 +1,37 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/Quoted.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/Quoted.php new file mode 100644 index 0000000..c3d1d41 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/Quoted.php @@ -0,0 +1,31 @@ +id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } +} \ No newline at end of file diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/User.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/User.php new file mode 100644 index 0000000..ea8d355 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/User.php @@ -0,0 +1,132 @@ +id = $id; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function setPassword($password) + { + $this->password = md5($password); + } + + public function getPassword() + { + return $this->password; + } + + public function setEmail($email) + { + $this->email = $email; + } + + public function getEmail() + { + return $this->email; + } + + public function setRole(Role $role) + { + $this->role = $role; + } + + public function getRole() + { + return $this->role; + } + + /** + * @return User[] + */ + public function getReaders() + { + return $this->readers; + } + + /** + * @param User[] $readers + * @return User + */ + public function setReaders($readers) + { + $this->readers = $readers; + + return $this; + } + + /** + * @return User[] + */ + public function getAuthors() + { + return $this->authors; + } + + /** + * @param User[] $authors + * @return User + */ + public function setAuthors($authors) + { + $this->authors = $authors; + + return $this; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/UserWithSchema.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/UserWithSchema.php new file mode 100644 index 0000000..9258a59 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestEntity/UserWithSchema.php @@ -0,0 +1,133 @@ +id = $id; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function setPassword($password) + { + $this->password = md5($password); + } + + public function getPassword() + { + return $this->password; + } + + public function setEmail($email) + { + $this->email = $email; + } + + public function getEmail() + { + return $this->email; + } + + public function setRole(Role $role) + { + $this->role = $role; + } + + public function getRole() + { + return $this->role; + } + + /** + * @return User[] + */ + public function getReaders() + { + return $this->readers; + } + + /** + * @param User[] $readers + * @return User + */ + public function setReaders($readers) + { + $this->readers = $readers; + + return $this; + } + + /** + * @return User[] + */ + public function getAuthors() + { + return $this->authors; + } + + /** + * @param User[] $authors + * @return User + */ + public function setAuthors($authors) + { + $this->authors = $authors; + + return $this; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/MyFixture1.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/MyFixture1.php new file mode 100644 index 0000000..3cb00ba --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/MyFixture1.php @@ -0,0 +1,13 @@ +referenceRepository = $referenceRepository; + } + + public function load(ObjectManager $manager) + { + $adminRole = new Role(); + $adminRole->setName('admin'); + + $manager->persist($adminRole); + $this->referenceRepository->addReference('admin-role', $adminRole); + $manager->flush(); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/UserFixture.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/UserFixture.php new file mode 100644 index 0000000..fb3c191 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestFixtures/UserFixture.php @@ -0,0 +1,25 @@ +setId(4); + $admin->setCode('007'); + $admin->setEmail('admin@example.com'); + $admin->setPassword('secret'); + $role = $this->getReference('admin-role'); + $admin->setRole($role); + + $manager->persist($admin); + $manager->flush(); + + $this->addReference('admin', $admin); + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/ExcludedEntity.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/ExcludedEntity.php new file mode 100644 index 0000000..8999cfc --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/ExcludedEntity.php @@ -0,0 +1,25 @@ +id = $id; + } + + public function getId() { + return $this->id; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/IncludedEntity.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/IncludedEntity.php new file mode 100644 index 0000000..1512518 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Common/DataFixtures/TestPurgeEntity/IncludedEntity.php @@ -0,0 +1,25 @@ +id = $id; + } + + public function getId() { + return $this->id; + } +} diff --git a/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Mock/Node.php b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Mock/Node.php new file mode 100644 index 0000000..6dcad84 --- /dev/null +++ b/vendor/doctrine/data-fixtures/tests/Doctrine/Tests/Mock/Node.php @@ -0,0 +1,27 @@ + + */ +class Node +{ + /** + * @var mixed + */ + public $value; + + /** + * Constructor. + * + * @param mixed $value + */ + public function __construct($value) + { + $this->value = $value; + } +} diff --git a/vendor/doctrine/event-manager/.scrutinizer.yml b/vendor/doctrine/event-manager/.scrutinizer.yml new file mode 100644 index 0000000..626a478 --- /dev/null +++ b/vendor/doctrine/event-manager/.scrutinizer.yml @@ -0,0 +1,29 @@ +build: + nodes: + analysis: + environment: + php: + version: 7.1 + cache: + disabled: false + directories: + - ~/.composer/cache + project_setup: + override: true + tests: + override: + - php-scrutinizer-run + - phpcs-run + dependencies: + override: + - composer install -noa + +tools: + external_code_coverage: + timeout: 600 + +build_failure_conditions: + - 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed + - 'issues.label("coding-style").new.exists' # No new coding style issues allowed + - 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity + - 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection diff --git a/vendor/doctrine/event-manager/LICENSE b/vendor/doctrine/event-manager/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/event-manager/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/event-manager/README.md b/vendor/doctrine/event-manager/README.md new file mode 100644 index 0000000..3aee6b6 --- /dev/null +++ b/vendor/doctrine/event-manager/README.md @@ -0,0 +1,13 @@ +# Doctrine Event Manager + +[![Build Status](https://travis-ci.org/doctrine/event-manager.svg)](https://travis-ci.org/doctrine/event-manager) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/event-manager/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/event-manager/?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/event-manager/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/event-manager/?branch=master) + +The Doctrine Event Manager is a library that provides a simple event system. + +## More resources: + +* [Website](https://www.doctrine-project.org/) +* [Documentation](https://www.doctrine-project.org/projects/doctrine-event-manager/en/latest/) +* [Downloads](https://github.com/doctrine/event-manager/releases) diff --git a/vendor/doctrine/event-manager/composer.json b/vendor/doctrine/event-manager/composer.json new file mode 100644 index 0000000..b868554 --- /dev/null +++ b/vendor/doctrine/event-manager/composer.json @@ -0,0 +1,41 @@ +{ + "name": "doctrine/event-manager", + "type": "library", + "description": "Doctrine Event Manager component", + "keywords": ["eventmanager", "eventdispatcher", "event"], + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}, + {"name": "Marco Pivetta", "email": "ocramius@gmail.com"} + ], + "require": { + "php": "^7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0", + "doctrine/coding-standard": "^4.0" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "autoload-dev": { + "psr-4": { + "Doctrine\\Tests\\": "tests/Doctrine/Tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/event-manager/docs/en/index.rst b/vendor/doctrine/event-manager/docs/en/index.rst new file mode 100644 index 0000000..4872d33 --- /dev/null +++ b/vendor/doctrine/event-manager/docs/en/index.rst @@ -0,0 +1,22 @@ +Event Manager Documentation +=========================== + +The Doctrine Event Manager documentation is a reference guide to everything you need +to know about the project. + +Getting Help +------------ + +If this documentation is not helping to answer questions you have about the +Doctrine DBAL, don't panic. You can get help from different sources: + +- Gitter chat room `#doctrine/event-manager `_ +- On `Stack Overflow `_ +- The `Doctrine Mailing List `_ +- Report a bug on `GitHub `_. + +Getting Started +--------------- + +The best way to get started is with the :doc:`Introduction ` section +in the documentation. Use the sidebar to browse other documentation for the Doctrine Event Manager. diff --git a/vendor/doctrine/event-manager/docs/en/reference/index.rst b/vendor/doctrine/event-manager/docs/en/reference/index.rst new file mode 100644 index 0000000..2b09285 --- /dev/null +++ b/vendor/doctrine/event-manager/docs/en/reference/index.rst @@ -0,0 +1,133 @@ +Introduction +============ + +The Doctrine Event Manager is a simple event system used by the various Doctrine projects. It was originally built +for the DBAL and ORM but over time other projects adopted it and now it is available as a standalone library. + +Installation +============ + +The library can easily be installed with composer. + +.. code-block:: sh + + $ composer require doctrine/event-manager + +Setup +===== + +The event system is controlled by the ``Doctrine\Common\EventManager`` class. + +.. code-block:: php + + use Doctrine\Common\EventManager; + + $eventManager = new EventManager(); + +Listeners +========= + +Now you are ready to listen for events. Here is an example of a custom event listener named ``TestEvent``. + +.. code-block:: php + + use Doctrine\Common\EventArgs; + use Doctrine\Common\EventManager; + + final class TestEvent + { + public const preFoo = 'preFoo'; + public const postFoo = 'postFoo'; + + /** @var EventManager */ + private $eventManager; + + /** @var bool */ + public $preFooInvoked = false; + + /** @var bool */ + public $postFooInvoked = false; + + public function __construct(EventManager $eventManager) + { + $eventManager->addEventListener([self::preFoo, self::postFoo], $this); + } + + public function preFoo(EventArgs $eventArgs) : void + { + $this->preFooInvoked = true; + } + + public function postFoo(EventArgs $eventArgs) : void + { + $this->postFooInvoked = true; + } + } + + // Create a new instance + $testEvent = new TestEvent($eventManager); + +Dispatching Events +================== + +Now you can dispatch events with the ``dispatchEvent()`` method. + +.. code-block:: php + + $eventManager->dispatchEvent(TestEvent::preFoo); + $eventManager->dispatchEvent(TestEvent::postFoo); + +Removing Event Listeners +======================== + +You can easily remove a listener with the ``removeEventListener()`` method. + +.. code-block:: php + + $eventManager->removeEventListener([TestEvent::preFoo, TestEvent::postFoo], $testEvent); + +Event Subscribers +================= + +The Doctrine event system also has a simple concept of event subscribers. We can define a simple ``TestEventSubscriber`` class which implements the ``Doctrine\Common\EventSubscriber`` interface with a ``getSubscribedEvents()`` method which returns an array of events it should be subscribed to. + +.. code-block:: php + + use Doctrine\Common\EventSubscriber; + + final class TestEventSubscriber implements EventSubscriber + { + /** @var bool */ + public $preFooInvoked = false; + + public function preFoo() : void + { + $this->preFooInvoked = true; + } + + public function getSubscribedEvents() : array + { + return [TestEvent::preFoo]; + } + } + + $eventSubscriber = new TestEventSubscriber(); + $eventManager->addEventSubscriber($eventSubscriber); + +.. note:: + + The array returned by the ``getSubscribedEvents()`` method is a simple array with the values being the event names. The subscriber must have a method that is named exactly like the event. + +Now when you dispatch an event, any event subscribers will be notified of that event. + +.. code-block:: php + + $eventManager->dispatchEvent(TestEvent::preFoo); + +Now you can check the ``preFooInvoked`` property to see if the event subscriber was notified of the event: + +.. code-block:: php + + if ($eventSubscriber->preFooInvoked) { + // the preFoo method was invoked + } diff --git a/vendor/doctrine/event-manager/docs/en/sidebar.rst b/vendor/doctrine/event-manager/docs/en/sidebar.rst new file mode 100644 index 0000000..0672d8d --- /dev/null +++ b/vendor/doctrine/event-manager/docs/en/sidebar.rst @@ -0,0 +1,4 @@ +.. toctree:: + :depth: 3 + + reference/index diff --git a/vendor/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php b/vendor/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php new file mode 100644 index 0000000..12e6256 --- /dev/null +++ b/vendor/doctrine/event-manager/lib/Doctrine/Common/EventArgs.php @@ -0,0 +1,46 @@ + => + * + * @var object[][] + */ + private $_listeners = []; + + /** + * 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 EventArgs|null $eventArgs The event arguments to pass to the event handlers/listeners. + * If not supplied, the single empty EventArgs instance is used. + * + * @return void + */ + public function dispatchEvent($eventName, ?EventArgs $eventArgs = null) + { + if (! isset($this->_listeners[$eventName])) { + return; + } + + $eventArgs = $eventArgs ?? EventArgs::getEmptyInstance(); + + foreach ($this->_listeners[$eventName] as $listener) { + $listener->$eventName($eventArgs); + } + } + + /** + * Gets the listeners of a specific event or all listeners. + * + * @param string|null $event The name of the event. + * + * @return object[]|object[][] The event listeners for the specified event, or all event listeners. + */ + public function getListeners($event = null) + { + return $event ? $this->_listeners[$event] : $this->_listeners; + } + + /** + * Checks whether an event has any registered listeners. + * + * @param string $event + * + * @return bool TRUE if the specified event has any listeners, FALSE otherwise. + */ + public function hasListeners($event) + { + return ! empty($this->_listeners[$event]); + } + + /** + * Adds an event listener that listens on the specified events. + * + * @param string|string[] $events The event(s) to listen on. + * @param object $listener The listener object. + * + * @return void + */ + public function addEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + // Overrides listener if a previous one was associated already + // Prevents duplicate listeners on same event (same instance only) + $this->_listeners[$event][$hash] = $listener; + } + } + + /** + * Removes an event listener from the specified events. + * + * @param string|string[] $events + * @param object $listener + * + * @return void + */ + public function removeEventListener($events, $listener) + { + // Picks the hash code related to that listener + $hash = spl_object_hash($listener); + + foreach ((array) $events as $event) { + unset($this->_listeners[$event][$hash]); + } + } + + /** + * Adds an EventSubscriber. The subscriber is asked for all the events it is + * interested in and added as a listener for these events. + * + * @param EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function addEventSubscriber(EventSubscriber $subscriber) + { + $this->addEventListener($subscriber->getSubscribedEvents(), $subscriber); + } + + /** + * Removes an EventSubscriber. The subscriber is asked for all the events it is + * interested in and removed as a listener for these events. + * + * @param EventSubscriber $subscriber The subscriber. + * + * @return void + */ + public function removeEventSubscriber(EventSubscriber $subscriber) + { + $this->removeEventListener($subscriber->getSubscribedEvents(), $subscriber); + } +} diff --git a/vendor/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php b/vendor/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php new file mode 100644 index 0000000..7d5e2ea --- /dev/null +++ b/vendor/doctrine/event-manager/lib/Doctrine/Common/EventSubscriber.php @@ -0,0 +1,21 @@ +. + */ + +namespace Doctrine\Common\Inflector; + +/** + * Doctrine inflector has static methods for inflecting text. + * + * The methods in these classes are from several different sources collected + * across several different php projects and several different authors. The + * original author names and emails are not known. + * + * Pluralize & Singularize implementation are borrowed from CakePHP with some modifications. + * + * @link www.doctrine-project.org + * @since 1.0 + * @author Konsta Vesterinen + * @author Jonathan H. Wage + */ +class Inflector +{ + /** + * Plural inflector rules. + * + * @var string[][] + */ + private static $plural = array( + 'rules' => array( + '/(s)tatus$/i' => '\1\2tatuses', + '/(quiz)$/i' => '\1zes', + '/^(ox)$/i' => '\1\2en', + '/([m|l])ouse$/i' => '\1ice', + '/(matr|vert|ind)(ix|ex)$/i' => '\1ices', + '/(x|ch|ss|sh)$/i' => '\1es', + '/([^aeiouy]|qu)y$/i' => '\1ies', + '/(hive|gulf)$/i' => '\1s', + '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves', + '/sis$/i' => 'ses', + '/([ti])um$/i' => '\1a', + '/(c)riterion$/i' => '\1riteria', + '/(p)erson$/i' => '\1eople', + '/(m)an$/i' => '\1en', + '/(c)hild$/i' => '\1hildren', + '/(f)oot$/i' => '\1eet', + '/(buffal|her|potat|tomat|volcan)o$/i' => '\1\2oes', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|vir)us$/i' => '\1i', + '/us$/i' => 'uses', + '/(alias)$/i' => '\1es', + '/(analys|ax|cris|test|thes)is$/i' => '\1es', + '/s$/' => 's', + '/^$/' => '', + '/$/' => 's', + ), + 'uninflected' => array( + '.*[nrlm]ese', + '.*deer', + '.*fish', + '.*measles', + '.*ois', + '.*pox', + '.*sheep', + 'people', + 'cookie', + 'police', + ), + 'irregular' => array( + 'atlas' => 'atlases', + 'axe' => 'axes', + 'beef' => 'beefs', + 'brother' => 'brothers', + 'cafe' => 'cafes', + 'chateau' => 'chateaux', + 'niveau' => 'niveaux', + 'child' => 'children', + 'cookie' => 'cookies', + 'corpus' => 'corpuses', + 'cow' => 'cows', + 'criterion' => 'criteria', + 'curriculum' => 'curricula', + 'demo' => 'demos', + 'domino' => 'dominoes', + 'echo' => 'echoes', + 'foot' => 'feet', + 'fungus' => 'fungi', + 'ganglion' => 'ganglions', + 'genie' => 'genies', + 'genus' => 'genera', + 'goose' => 'geese', + 'graffito' => 'graffiti', + 'hippopotamus' => 'hippopotami', + 'hoof' => 'hoofs', + 'human' => 'humans', + 'iris' => 'irises', + 'larva' => 'larvae', + 'leaf' => 'leaves', + 'loaf' => 'loaves', + 'man' => 'men', + 'medium' => 'media', + 'memorandum' => 'memoranda', + 'money' => 'monies', + 'mongoose' => 'mongooses', + 'motto' => 'mottoes', + 'move' => 'moves', + 'mythos' => 'mythoi', + 'niche' => 'niches', + 'nucleus' => 'nuclei', + 'numen' => 'numina', + 'occiput' => 'occiputs', + 'octopus' => 'octopuses', + 'opus' => 'opuses', + 'ox' => 'oxen', + 'passerby' => 'passersby', + 'penis' => 'penises', + 'person' => 'people', + 'plateau' => 'plateaux', + 'runner-up' => 'runners-up', + 'sex' => 'sexes', + 'soliloquy' => 'soliloquies', + 'son-in-law' => 'sons-in-law', + 'syllabus' => 'syllabi', + 'testis' => 'testes', + 'thief' => 'thieves', + 'tooth' => 'teeth', + 'tornado' => 'tornadoes', + 'trilby' => 'trilbys', + 'turf' => 'turfs', + 'valve' => 'valves', + 'volcano' => 'volcanoes', + ) + ); + + /** + * Singular inflector rules. + * + * @var string[][] + */ + private static $singular = array( + 'rules' => array( + '/(s)tatuses$/i' => '\1\2tatus', + '/^(.*)(menu)s$/i' => '\1\2', + '/(quiz)zes$/i' => '\\1', + '/(matr)ices$/i' => '\1ix', + '/(vert|ind)ices$/i' => '\1ex', + '/^(ox)en/i' => '\1', + '/(alias)(es)*$/i' => '\1', + '/(buffal|her|potat|tomat|volcan)oes$/i' => '\1o', + '/(alumn|bacill|cact|foc|fung|nucle|radi|stimul|syllab|termin|viri?)i$/i' => '\1us', + '/([ftw]ax)es/i' => '\1', + '/(analys|ax|cris|test|thes)es$/i' => '\1is', + '/(shoe|slave)s$/i' => '\1', + '/(o)es$/i' => '\1', + '/ouses$/' => 'ouse', + '/([^a])uses$/' => '\1us', + '/([m|l])ice$/i' => '\1ouse', + '/(x|ch|ss|sh)es$/i' => '\1', + '/(m)ovies$/i' => '\1\2ovie', + '/(s)eries$/i' => '\1\2eries', + '/([^aeiouy]|qu)ies$/i' => '\1y', + '/([lr])ves$/i' => '\1f', + '/(tive)s$/i' => '\1', + '/(hive)s$/i' => '\1', + '/(drive)s$/i' => '\1', + '/(dive)s$/i' => '\1', + '/(olive)s$/i' => '\1', + '/([^fo])ves$/i' => '\1fe', + '/(^analy)ses$/i' => '\1sis', + '/(analy|diagno|^ba|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\1\2sis', + '/(c)riteria$/i' => '\1riterion', + '/([ti])a$/i' => '\1um', + '/(p)eople$/i' => '\1\2erson', + '/(m)en$/i' => '\1an', + '/(c)hildren$/i' => '\1\2hild', + '/(f)eet$/i' => '\1oot', + '/(n)ews$/i' => '\1\2ews', + '/eaus$/' => 'eau', + '/^(.*us)$/' => '\\1', + '/s$/i' => '', + ), + 'uninflected' => array( + '.*[nrlm]ese', + '.*deer', + '.*fish', + '.*measles', + '.*ois', + '.*pox', + '.*sheep', + '.*ss', + 'data', + 'police', + 'pants', + 'clothes', + ), + 'irregular' => array( + 'abuses' => 'abuse', + 'avalanches' => 'avalanche', + 'caches' => 'cache', + 'criteria' => 'criterion', + 'curves' => 'curve', + 'emphases' => 'emphasis', + 'foes' => 'foe', + 'geese' => 'goose', + 'graves' => 'grave', + 'hoaxes' => 'hoax', + 'media' => 'medium', + 'neuroses' => 'neurosis', + 'waves' => 'wave', + 'oases' => 'oasis', + 'valves' => 'valve', + ) + ); + + /** + * Words that should not be inflected. + * + * @var array + */ + private static $uninflected = array( + '.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches', + 'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese', + 'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland', + 'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese', + 'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold', + 'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi', + 'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata', + 'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring', + 'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese', + 'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass', + 'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic', + 'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese' + ); + + /** + * Method cache array. + * + * @var array + */ + private static $cache = array(); + + /** + * The initial state of Inflector so reset() works. + * + * @var array + */ + private static $initialState = array(); + + /** + * Converts a word into the format for a Doctrine table name. Converts 'ModelName' to 'model_name'. + */ + public static function tableize(string $word) : string + { + return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word)); + } + + /** + * Converts a word into the format for a Doctrine class name. Converts 'table_name' to 'TableName'. + */ + public static function classify(string $word) : string + { + return str_replace([' ', '_', '-'], '', ucwords($word, ' _-')); + } + + /** + * Camelizes a word. This uses the classify() method and turns the first character to lowercase. + */ + public static function camelize(string $word) : string + { + return lcfirst(self::classify($word)); + } + + /** + * Uppercases words with configurable delimeters between words. + * + * Takes a string and capitalizes all of the words, like PHP's built-in + * ucwords function. This extends that behavior, however, by allowing the + * word delimeters to be configured, rather than only separating on + * whitespace. + * + * Here is an example: + * + * + * + * + * @param string $string The string to operate on. + * @param string $delimiters A list of word separators. + * + * @return string The string with all delimeter-separated words capitalized. + */ + public static function ucwords(string $string, string $delimiters = " \n\t\r\0\x0B-") : string + { + return ucwords($string, $delimiters); + } + + /** + * Clears Inflectors inflected value caches, and resets the inflection + * rules to the initial values. + */ + public static function reset() : void + { + if (empty(self::$initialState)) { + self::$initialState = get_class_vars('Inflector'); + + return; + } + + foreach (self::$initialState as $key => $val) { + if ($key !== 'initialState') { + self::${$key} = $val; + } + } + } + + /** + * Adds custom inflection $rules, of either 'plural' or 'singular' $type. + * + * ### Usage: + * + * {{{ + * Inflector::rules('plural', array('/^(inflect)or$/i' => '\1ables')); + * Inflector::rules('plural', array( + * 'rules' => array('/^(inflect)ors$/i' => '\1ables'), + * 'uninflected' => array('dontinflectme'), + * 'irregular' => array('red' => 'redlings') + * )); + * }}} + * + * @param string $type The type of inflection, either 'plural' or 'singular' + * @param array|iterable $rules An array of rules to be added. + * @param boolean $reset If true, will unset default inflections for all + * new rules that are being defined in $rules. + * + * @return void + */ + public static function rules(string $type, iterable $rules, bool $reset = false) : void + { + foreach ($rules as $rule => $pattern) { + if ( ! is_array($pattern)) { + continue; + } + + if ($reset) { + self::${$type}[$rule] = $pattern; + } else { + self::${$type}[$rule] = ($rule === 'uninflected') + ? array_merge($pattern, self::${$type}[$rule]) + : $pattern + self::${$type}[$rule]; + } + + unset($rules[$rule], self::${$type}['cache' . ucfirst($rule)]); + + if (isset(self::${$type}['merged'][$rule])) { + unset(self::${$type}['merged'][$rule]); + } + + if ($type === 'plural') { + self::$cache['pluralize'] = self::$cache['tableize'] = array(); + } elseif ($type === 'singular') { + self::$cache['singularize'] = array(); + } + } + + self::${$type}['rules'] = $rules + self::${$type}['rules']; + } + + /** + * Returns a word in plural form. + * + * @param string $word The word in singular form. + * + * @return string The word in plural form. + */ + public static function pluralize(string $word) : string + { + if (isset(self::$cache['pluralize'][$word])) { + return self::$cache['pluralize'][$word]; + } + + if (!isset(self::$plural['merged']['irregular'])) { + self::$plural['merged']['irregular'] = self::$plural['irregular']; + } + + if (!isset(self::$plural['merged']['uninflected'])) { + self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected); + } + + if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) { + self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')'; + self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['pluralize'][$word] = $regs[1] . $word[0] . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1); + + return self::$cache['pluralize'][$word]; + } + + if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$cache['pluralize'][$word] = $word; + + return $word; + } + + foreach (self::$plural['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word); + + return self::$cache['pluralize'][$word]; + } + } + } + + /** + * Returns a word in singular form. + * + * @param string $word The word in plural form. + * + * @return string The word in singular form. + */ + public static function singularize(string $word) : string + { + if (isset(self::$cache['singularize'][$word])) { + return self::$cache['singularize'][$word]; + } + + if (!isset(self::$singular['merged']['uninflected'])) { + self::$singular['merged']['uninflected'] = array_merge( + self::$singular['uninflected'], + self::$uninflected + ); + } + + if (!isset(self::$singular['merged']['irregular'])) { + self::$singular['merged']['irregular'] = array_merge( + self::$singular['irregular'], + array_flip(self::$plural['irregular']) + ); + } + + if (!isset(self::$singular['cacheUninflected']) || !isset(self::$singular['cacheIrregular'])) { + self::$singular['cacheUninflected'] = '(?:' . implode('|', self::$singular['merged']['uninflected']) . ')'; + self::$singular['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$singular['merged']['irregular'])) . ')'; + } + + if (preg_match('/(.*)\\b(' . self::$singular['cacheIrregular'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $regs[1] . $word[0] . substr(self::$singular['merged']['irregular'][strtolower($regs[2])], 1); + + return self::$cache['singularize'][$word]; + } + + if (preg_match('/^(' . self::$singular['cacheUninflected'] . ')$/i', $word, $regs)) { + self::$cache['singularize'][$word] = $word; + + return $word; + } + + foreach (self::$singular['rules'] as $rule => $replacement) { + if (preg_match($rule, $word)) { + self::$cache['singularize'][$word] = preg_replace($rule, $replacement, $word); + + return self::$cache['singularize'][$word]; + } + } + + self::$cache['singularize'][$word] = $word; + + return $word; + } +} diff --git a/vendor/doctrine/lexer/LICENSE b/vendor/doctrine/lexer/LICENSE new file mode 100644 index 0000000..5e781fc --- /dev/null +++ b/vendor/doctrine/lexer/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2013 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/lexer/README.md b/vendor/doctrine/lexer/README.md new file mode 100644 index 0000000..66f4430 --- /dev/null +++ b/vendor/doctrine/lexer/README.md @@ -0,0 +1,5 @@ +# Doctrine Lexer + +Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers. + +This lexer is used in Doctrine Annotations and in Doctrine ORM (DQL). diff --git a/vendor/doctrine/lexer/composer.json b/vendor/doctrine/lexer/composer.json new file mode 100644 index 0000000..8cd694c --- /dev/null +++ b/vendor/doctrine/lexer/composer.json @@ -0,0 +1,24 @@ +{ + "name": "doctrine/lexer", + "type": "library", + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "keywords": ["lexer", "parser"], + "homepage": "http://www.doctrine-project.org", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"} + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Doctrine\\Common\\Lexer\\": "lib/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php new file mode 100644 index 0000000..399a552 --- /dev/null +++ b/vendor/doctrine/lexer/lib/Doctrine/Common/Lexer/AbstractLexer.php @@ -0,0 +1,327 @@ +. + */ + +namespace Doctrine\Common\Lexer; + +/** + * Base class for writing simple lexers, i.e. for creating small DSLs. + * + * @since 2.0 + * @author Guilherme Blanco + * @author Jonathan Wage + * @author Roman Borschel + */ +abstract class AbstractLexer +{ + /** + * Lexer original input string. + * + * @var string + */ + private $input; + + /** + * Array of scanned tokens. + * + * Each token is an associative array containing three items: + * - 'value' : the string value of the token in the input string + * - 'type' : the type of the token (identifier, numeric, string, input + * parameter, none) + * - 'position' : the position of the token in the input string + * + * @var array + */ + private $tokens = array(); + + /** + * Current lexer position in input string. + * + * @var integer + */ + private $position = 0; + + /** + * Current peek of current lexer position. + * + * @var integer + */ + private $peek = 0; + + /** + * The next token in the input. + * + * @var array + */ + public $lookahead; + + /** + * The last matched/seen token. + * + * @var array + */ + public $token; + + /** + * Sets the input data to be tokenized. + * + * The Lexer is immediately reset and the new input tokenized. + * Any unprocessed tokens from any previous input are lost. + * + * @param string $input The input to be tokenized. + * + * @return void + */ + public function setInput($input) + { + $this->input = $input; + $this->tokens = array(); + + $this->reset(); + $this->scan($input); + } + + /** + * Resets the lexer. + * + * @return void + */ + public function reset() + { + $this->lookahead = null; + $this->token = null; + $this->peek = 0; + $this->position = 0; + } + + /** + * Resets the peek pointer to 0. + * + * @return void + */ + public function resetPeek() + { + $this->peek = 0; + } + + /** + * Resets the lexer position on the input to the given position. + * + * @param integer $position Position to place the lexical scanner. + * + * @return void + */ + public function resetPosition($position = 0) + { + $this->position = $position; + } + + /** + * Retrieve the original lexer's input until a given position. + * + * @param integer $position + * + * @return string + */ + public function getInputUntilPosition($position) + { + return substr($this->input, 0, $position); + } + + /** + * Checks whether a given token matches the current lookahead. + * + * @param integer|string $token + * + * @return boolean + */ + public function isNextToken($token) + { + return null !== $this->lookahead && $this->lookahead['type'] === $token; + } + + /** + * Checks whether any of the given tokens matches the current lookahead. + * + * @param array $tokens + * + * @return boolean + */ + public function isNextTokenAny(array $tokens) + { + return null !== $this->lookahead && in_array($this->lookahead['type'], $tokens, true); + } + + /** + * Moves to the next token in the input string. + * + * @return boolean + */ + public function moveNext() + { + $this->peek = 0; + $this->token = $this->lookahead; + $this->lookahead = (isset($this->tokens[$this->position])) + ? $this->tokens[$this->position++] : null; + + return $this->lookahead !== null; + } + + /** + * Tells the lexer to skip input tokens until it sees a token with the given value. + * + * @param string $type The token type to skip until. + * + * @return void + */ + public function skipUntil($type) + { + while ($this->lookahead !== null && $this->lookahead['type'] !== $type) { + $this->moveNext(); + } + } + + /** + * Checks if given value is identical to the given token. + * + * @param mixed $value + * @param integer $token + * + * @return boolean + */ + public function isA($value, $token) + { + return $this->getType($value) === $token; + } + + /** + * Moves the lookahead token forward. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function peek() + { + if (isset($this->tokens[$this->position + $this->peek])) { + return $this->tokens[$this->position + $this->peek++]; + } else { + return null; + } + } + + /** + * Peeks at the next token, returns it and immediately resets the peek. + * + * @return array|null The next token or NULL if there are no more tokens ahead. + */ + public function glimpse() + { + $peek = $this->peek(); + $this->peek = 0; + return $peek; + } + + /** + * Scans the input string for tokens. + * + * @param string $input A query string. + * + * @return void + */ + protected function scan($input) + { + static $regex; + + if ( ! isset($regex)) { + $regex = sprintf( + '/(%s)|%s/%s', + implode(')|(', $this->getCatchablePatterns()), + implode('|', $this->getNonCatchablePatterns()), + $this->getModifiers() + ); + } + + $flags = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE; + $matches = preg_split($regex, $input, -1, $flags); + + foreach ($matches as $match) { + // Must remain before 'value' assignment since it can change content + $type = $this->getType($match[0]); + + $this->tokens[] = array( + 'value' => $match[0], + 'type' => $type, + 'position' => $match[1], + ); + } + } + + /** + * Gets the literal for a given token. + * + * @param integer $token + * + * @return string + */ + public function getLiteral($token) + { + $className = get_class($this); + $reflClass = new \ReflectionClass($className); + $constants = $reflClass->getConstants(); + + foreach ($constants as $name => $value) { + if ($value === $token) { + return $className . '::' . $name; + } + } + + return $token; + } + + /** + * Regex modifiers + * + * @return string + */ + protected function getModifiers() + { + return 'i'; + } + + /** + * Lexical catchable patterns. + * + * @return array + */ + abstract protected function getCatchablePatterns(); + + /** + * Lexical non-catchable patterns. + * + * @return array + */ + abstract protected function getNonCatchablePatterns(); + + /** + * Retrieve token type. Also processes the token value if necessary. + * + * @param string $value + * + * @return integer + */ + abstract protected function getType(&$value); +} diff --git a/vendor/doctrine/persistence/.scrutinizer.yml b/vendor/doctrine/persistence/.scrutinizer.yml new file mode 100644 index 0000000..626a478 --- /dev/null +++ b/vendor/doctrine/persistence/.scrutinizer.yml @@ -0,0 +1,29 @@ +build: + nodes: + analysis: + environment: + php: + version: 7.1 + cache: + disabled: false + directories: + - ~/.composer/cache + project_setup: + override: true + tests: + override: + - php-scrutinizer-run + - phpcs-run + dependencies: + override: + - composer install -noa + +tools: + external_code_coverage: + timeout: 600 + +build_failure_conditions: + - 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed + - 'issues.label("coding-style").new.exists' # No new coding style issues allowed + - 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity + - 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection diff --git a/vendor/doctrine/persistence/LICENSE b/vendor/doctrine/persistence/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/persistence/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/persistence/README.md b/vendor/doctrine/persistence/README.md new file mode 100644 index 0000000..b5d32d5 --- /dev/null +++ b/vendor/doctrine/persistence/README.md @@ -0,0 +1,13 @@ +# Doctrine Persistence + +[![Build Status](https://travis-ci.org/doctrine/persistence.svg)](https://travis-ci.org/doctrine/persistence) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/persistence/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/persistence/?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/persistence/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/persistence/?branch=master) + +The Doctrine Persistence project is a library that provides common abstractions for object mapper persistence. + +## More resources: + +* [Website](https://www.doctrine-project.org/) +* [Documentation](https://www.doctrine-project.org/projects/doctrine-persistence/en/latest/index.html) +* [Downloads](https://github.com/doctrine/persistence/releases) diff --git a/vendor/doctrine/persistence/composer.json b/vendor/doctrine/persistence/composer.json new file mode 100644 index 0000000..4fc2e59 --- /dev/null +++ b/vendor/doctrine/persistence/composer.json @@ -0,0 +1,47 @@ +{ + "name": "doctrine/persistence", + "type": "library", + "description": "Doctrine Persistence abstractions.", + "keywords": ["persistence"], + "homepage": "https://doctrine-project.org/projects/persistence.html", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}, + {"name": "Marco Pivetta", "email": "ocramius@gmail.com"} + ], + "require": { + "php": "^7.1", + "doctrine/annotations": "^1.0", + "doctrine/cache": "^1.0", + "doctrine/collections": "^1.0", + "doctrine/event-manager": "^1.0", + "doctrine/reflection": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.8", + "doctrine/coding-standard": "^4.0", + "phpunit/phpunit": "^7.0" + }, + "conflict": { + "doctrine/common": "<2.9@dev" + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "autoload-dev": { + "psr-4": { + "Doctrine\\Tests\\": "tests/Doctrine/Tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/persistence/docs/en/index.rst b/vendor/doctrine/persistence/docs/en/index.rst new file mode 100644 index 0000000..3efcebe --- /dev/null +++ b/vendor/doctrine/persistence/docs/en/index.rst @@ -0,0 +1,22 @@ +Persistence Documentation +========================= + +The Doctrine Persistence documentation is a reference guide to everything you need +to know about the project. + +Getting Help +------------ + +If this documentation is not helping to answer questions you have about the +Doctrine Persistence project, don't panic. You can get help from different sources: + +- Gitter chat room `#doctrine/persistence `_ +- On `Stack Overflow `_ +- The `Doctrine Mailing List `_ +- Report a bug on `GitHub `_. + +Getting Started +--------------- + +The best way to get started is with the :doc:`Introduction ` section +in the documentation. Use the sidebar to browse other documentation for the Doctrine Persistence project. diff --git a/vendor/doctrine/persistence/docs/en/reference/index.rst b/vendor/doctrine/persistence/docs/en/reference/index.rst new file mode 100644 index 0000000..e18a8f2 --- /dev/null +++ b/vendor/doctrine/persistence/docs/en/reference/index.rst @@ -0,0 +1,375 @@ +Introduction +============ + +The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine +object mappers share. You can use these interfaces and abstract classes to build your own mapper if you don't +want to use the full data mappers provided by Doctrine. + +Installation +============ + +The library can easily be installed with composer. + +.. code-block:: sh + + $ composer require doctrine/persistence + +Overview +======== + +The interfaces and functionality in this project evolved from building several different implementations of Doctrine +object mappers. The first implementation was the ORM_ then came the `MongoDB ODM`_. A set of common interfaces were +extracted from both projects and released in the `Doctrine Common`_ project. Over the years, more common functionality +was extracted and eventually moved to this standalone project. + +A Doctrine object mapper looks like this when implemented. + +.. code-block:: php + + final class User + { + /** @var string */ + private $username; + + public function __construct(string $username) + { + $this->username = $username; + } + + // ... + } + + $objectManager = new ObjectManager(); + $userRepository = $objectManager->getRepository(User::class); + + $newUser = new User('jwage'); + + $objectManager->persist($newUser); + $objectManager->flush(); + + $user = $objectManager->find(User::class, 1); + + $objectManager->remove($user); + $objectManager->flush(); + + $users = $userRepository->findAll(); + +To learn more about the full interfaces and functionality continue reading! + +Object Manager +============== + +The main public interface that an end user will use is the ``Doctrine\Common\Persistence\ObjectManager`` interface. + +.. code-block:: php + + namespace Doctrine\Common\Persistence; + + interface ObjectManager + { + public function find($className, $id); + public function persist($object); + public function remove($object); + public function merge($object); + public function clear($objectName = null); + public function detach($object); + public function refresh($object); + public function flush(); + public function getRepository($className); + public function getClassMetadata($className); + public function getMetadataFactory(); + public function initializeObject($obj); + public function contains($object); + } + +ObjectRepository +================ + +The object repository is used to retrieve instances of your mapped objects from the mapper. + +.. code-block:: php + + namespace Doctrine\Common\Persistence; + + interface ObjectRepository + { + public function find($id); + public function findAll(); + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null); + public function findOneBy(array $criteria); + public function getClassName(); + } + +Mapping +======= + +In order for Doctrine to be able to persist your objects to a data store, you have to map the classes and class +properties so they can be properly stored and retrieved while maintaining a consistent state. + +ClassMetadata +------------- + +.. code-block:: php + + namespace Doctrine\Common\Persistence\Mapping; + + interface ClassMetadata + { + public function getName(); + public function getIdentifier(); + public function getReflectionClass(); + public function isIdentifier($fieldName); + public function hasField($fieldName); + public function hasAssociation($fieldName); + public function isSingleValuedAssociation($fieldName); + public function isCollectionValuedAssociation($fieldName); + public function getFieldNames(); + public function getIdentifierFieldNames(); + public function getAssociationNames(); + public function getTypeOfField($fieldName); + public function getAssociationTargetClass($assocName); + public function isAssociationInverseSide($assocName); + public function getAssociationMappedByTargetField($assocName); + public function getIdentifierValues($object); + } + +ClassMetadataFactory +-------------------- + +The ``Doctrine\Common\Persistence\Mapping\ClassMetadataFactory`` class can be used to manage the instances for each of +your mapped PHP classes. + +.. code-block:: php + + namespace Doctrine\Common\Persistence\Mapping; + + interface ClassMetadataFactory + { + public function getAllMetadata(); + public function getMetadataFor($className); + public function hasMetadataFor($className); + public function setMetadataFor($className, $class); + public function isTransient($className); + } + +Mapping Driver +============== + +In order to load ``ClassMetadata`` instances you can use the ``Doctrine\Common\Persistence\Mapping\Driver\MappingDriver`` +interface. This is the interface that does the core loading of mapping information from wherever they are stored. +That may be in files, annotations, yaml, xml, etc. + +.. code-block:: php + + namespace Doctrine\Common\Persistence\Mapping\Driver; + + use Doctrine\Common\Persistence\Mapping\ClassMetadata; + + interface MappingDriver + { + public function loadMetadataForClass($className, ClassMetadata $metadata); + public function getAllClassNames(); + public function isTransient($className); + } + +The Doctrine Persistence project offers a few base implementations that make it easy to implement your own XML, +Annotations or YAML drivers. + +FileDriver +---------- + +The file driver operates in a mode where it loads the mapping files of individual classes on demand. This requires +the user to adhere to the convention of 1 mapping file per class and the file names of the mapping files must +correspond to the full class name, including namespace, with the namespace delimiters '\', replaced by dots '.'. + +Extend the ``Doctrine\Common\Persistence\Mapping\Driver\FileDriver`` class to implement your own file driver. +Here is an example JSON file driver implementation. + +.. code-block:: php + + use Doctrine\Common\Persistence\Mapping\Driver\FileDriver; + + final class JSONFileDriver extends FileDriver + { + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $mappingFileData = $this->getElement($className); + + // use the array of mapping information from the file to populate the $metadata instance + } + + protected function loadMappingFile($file) + { + return json_decode($file, true); + } + } + +Now you can use it like the following. + +.. code-block:: php + + use Doctrine\Common\Persistence\Mapping\Driver\DefaultFileLocator; + + $fileLocator = new DefaultFileLocator('/path/to/mapping/files', 'json'); + + $jsonFileDriver = new JSONFileDriver($fileLocator); + +Now if you have a class named ``App\Model\User`` and you can load the mapping information like this. + +.. code-block:: php + + use App\Model\User; + use Doctrine\Common\Persistence\Mapping\ClassMetadata; + + $classMetadata = new ClassMetadata(); + + // looks for a file at /path/to/mapping/files/App.Model.User.json + $jsonFileDriver->loadMetadataForClass(User::class, $classMetadata); + +AnnotationDriver +---------------- + +.. note:: + + This driver requires the ``doctrine/annotations`` project. You can install it with composer. + + .. code-block:: php + + composer require doctrine/annotations + +The AnnotationDriver reads the mapping metadata from docblock annotations. + +.. code-block:: php + + final class MyAnnotationDriver extends AnnotationDriver + { + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /** @var ClassMetadata $class */ + $reflClass = $class->getReflectionClass(); + + $classAnnotations = $this->reader->getClassAnnotations($reflClass); + + // Use the reader to read annotations from your classes to then populate the $metadata instance. + } + } + +Now you can use it like the following: + +.. code-block:: php + + use App\Model\User; + use Doctrine\Common\Annotations\AnnotationReader; + use Doctrine\Common\Persistence\Mapping\ClassMetadata; + + $annotationReader = new AnnotationReader(); + + $annotationDriver = new AnnotationDriver($annotationReader, '/path/to/classes/with/annotations'); + + $classMetadata = new ClassMetadata(); + + // looks for a PHP file at /path/to/classes/with/annotations/App/Model/User.php + $annotationDriver->loadMetadataForClass(User::class, $classMetadata); + +PHPDriver +--------- + +The PHPDriver includes PHP files which just populate ``ClassMetadata`` instances with plain PHP code. + +.. code-block:: php + + use Doctrine\Common\Persistence\Mapping\Driver\PHPDriver; + + $phpDriver = new PHPDriver('/path/to/mapping/files'); + +Now you can use it like the following: + +.. code-block:: php + + use App\Model\User; + use Doctrine\Common\Persistence\Mapping\ClassMetadata; + + $classMetadata = new ClassMetadata(); + + // looks for a PHP file at /path/to/mapping/files/App.Model.User.php + $phpDriver->loadMetadataForClass(User::class, $classMetadata); + +Inside the ``/path/to/mapping/files/App.Model.User.php`` file you can write raw PHP code to populate a ``ClassMetadata`` +instance. You will have access to a variable named ``$metadata`` inside the file that you can use to populate the +mapping metadata. + +.. code-block:: php + + use App\Model\User; + + $metadata->name = User::class; + + // ... + +StaticPHPDriver +-------------- + +The StaticPHPDriver calls a static ``loadMetadata()`` method on your model classes where you can manually populate the +``ClassMetadata`` instance. + +.. code-block:: php + + $staticPHPDriver = new StaticPHPDriver('/path/to/classes'); + + $classMetadata = new ClassMetadata(); + + // looks for a PHP file at /path/to/classes/App/Model/User.php + $phpDriver->loadMetadataForClass(User::class, $classMetadata); + +Your class in ``App\Model\User`` would look like the following. + +.. code-block:: php + + namespace App\Model; + + final class User + { + // ... + + public static function loadMetadata(ClassMetadata $metadata) + { + // populate the $metadata instance + } + } + +Reflection +========== + +Doctrine uses reflection to set and get the data inside your objects. The +``Doctrine\Common\Persistence\Mapping\ReflectionService`` is the primary interface needed for a Doctrine mapper. + +.. code-block:: php + + namespace Doctrine\Common\Persistence\Mapping; + + interface ReflectionService + { + public function getParentClasses($class); + public function getClassShortName($class); + public function getClassNamespace($class); + public function getClass($class); + public function getAccessibleProperty($class, $property); + public function hasPublicMethod($class, $method); + } + +Doctrine provides an implementation of this interface in the class named +``Doctrine\Common\Persistence\Mapping\RuntimeReflectionService``. + +Implementations +=============== + +There are several different implementations of the Doctrine Persistence APIs. + +- ORM_ - The Doctrine Object Relational Mapper is a data mapper for relational databases. +- `MongoDB ODM`_ - The Doctrine MongoDB ODM is a data mapper for MongoDB. +- `PHPCR ODM`_ - The Doctrine PHPCR ODM a data mapper built on top of the PHPCR API. + +.. _ORM: https://www.doctrine-project.org/projects/orm.html +.. _MongoDB ODM: https://www.doctrine-project.org/projects/mongodb-odm.html +.. _PHPCR ODM: https://www.doctrine-project.org/projects/phpcr-odm.html +.. _Doctrine Common: https://www.doctrine-project.org/projects/common.html diff --git a/vendor/doctrine/persistence/docs/en/sidebar.rst b/vendor/doctrine/persistence/docs/en/sidebar.rst new file mode 100644 index 0000000..0672d8d --- /dev/null +++ b/vendor/doctrine/persistence/docs/en/sidebar.rst @@ -0,0 +1,4 @@ +.. toctree:: + :depth: 3 + + reference/index diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php new file mode 100644 index 0000000..86b49c6 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/AbstractManagerRegistry.php @@ -0,0 +1,233 @@ +name = $name; + $this->connections = $connections; + $this->managers = $managers; + $this->defaultConnection = $defaultConnection; + $this->defaultManager = $defaultManager; + $this->proxyInterfaceName = $proxyInterfaceName; + } + + /** + * Fetches/creates the given services. + * + * A service in this context is connection or a manager instance. + * + * @param string $name The name of the service. + * + * @return object The instance of the given service. + */ + abstract protected function getService($name); + + /** + * Resets the given services. + * + * A service in this context is connection or a manager instance. + * + * @param string $name The name of the service. + * + * @return void + */ + abstract protected function resetService($name); + + /** + * Gets the name of the registry. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getConnection($name = null) + { + if ($name === null) { + $name = $this->defaultConnection; + } + + if (! isset($this->connections[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Connection named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->connections[$name]); + } + + /** + * {@inheritdoc} + */ + public function getConnectionNames() + { + return $this->connections; + } + + /** + * {@inheritdoc} + */ + public function getConnections() + { + $connections = []; + foreach ($this->connections as $name => $id) { + $connections[$name] = $this->getService($id); + } + + return $connections; + } + + /** + * {@inheritdoc} + */ + public function getDefaultConnectionName() + { + return $this->defaultConnection; + } + + /** + * {@inheritdoc} + */ + public function getDefaultManagerName() + { + return $this->defaultManager; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException + */ + public function getManager($name = null) + { + if ($name === null) { + $name = $this->defaultManager; + } + + if (! isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + return $this->getService($this->managers[$name]); + } + + /** + * {@inheritdoc} + */ + public function getManagerForClass($class) + { + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getAliasNamespace($namespaceAlias) . '\\' . $simpleClassName; + } + + $proxyClass = new \ReflectionClass($class); + + if ($proxyClass->implementsInterface($this->proxyInterfaceName)) { + $parentClass = $proxyClass->getParentClass(); + + if (! $parentClass) { + return null; + } + + $class = $parentClass->getName(); + } + + foreach ($this->managers as $id) { + $manager = $this->getService($id); + + if (! $manager->getMetadataFactory()->isTransient($class)) { + return $manager; + } + } + } + + /** + * {@inheritdoc} + */ + public function getManagerNames() + { + return $this->managers; + } + + /** + * {@inheritdoc} + */ + public function getManagers() + { + $dms = []; + foreach ($this->managers as $name => $id) { + $dms[$name] = $this->getService($id); + } + + return $dms; + } + + /** + * {@inheritdoc} + */ + public function getRepository($persistentObjectName, $persistentManagerName = null) + { + return $this->getManager($persistentManagerName)->getRepository($persistentObjectName); + } + + /** + * {@inheritdoc} + */ + public function resetManager($name = null) + { + if ($name === null) { + $name = $this->defaultManager; + } + + if (! isset($this->managers[$name])) { + throw new \InvalidArgumentException(sprintf('Doctrine %s Manager named "%s" does not exist.', $this->name, $name)); + } + + // force the creation of a new document manager + // if the current one is closed + $this->resetService($this->managers[$name]); + + return $this->getManager($name); + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ConnectionRegistry.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ConnectionRegistry.php new file mode 100644 index 0000000..59046d4 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ConnectionRegistry.php @@ -0,0 +1,39 @@ +object = $object; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated entity. + * + * @deprecated + * + * @return object + */ + public function getEntity() + { + return $this->object; + } + + /** + * Retrieves the associated object. + * + * @return object + */ + public function getObject() + { + return $this->object; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php new file mode 100644 index 0000000..3fcf1b8 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/LoadClassMetadataEventArgs.php @@ -0,0 +1,45 @@ +classMetadata = $classMetadata; + $this->objectManager = $objectManager; + } + + /** + * Retrieves the associated ClassMetadata. + * + * @return ClassMetadata + */ + public function getClassMetadata() + { + return $this->classMetadata; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php new file mode 100644 index 0000000..4ade991 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/ManagerEventArgs.php @@ -0,0 +1,30 @@ +objectManager = $objectManager; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php new file mode 100644 index 0000000..a32e35b --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/OnClearEventArgs.php @@ -0,0 +1,58 @@ +objectManager = $objectManager; + $this->entityClass = $entityClass; + } + + /** + * Retrieves the associated ObjectManager. + * + * @return ObjectManager + */ + public function getObjectManager() + { + return $this->objectManager; + } + + /** + * Returns the name of the entity class that is cleared, or null if all are cleared. + * + * @return string|null + */ + public function getEntityClass() + { + return $this->entityClass; + } + + /** + * Returns whether this event clears all entities. + * + * @return bool + */ + public function clearsAllEntities() + { + return $this->entityClass === null; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php new file mode 100644 index 0000000..25ac309 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Event/PreUpdateEventArgs.php @@ -0,0 +1,112 @@ +entityChangeSet = &$changeSet; + } + + /** + * Retrieves the entity changeset. + * + * @return mixed[][] + */ + public function getEntityChangeSet() + { + return $this->entityChangeSet; + } + + /** + * Checks if field has a changeset. + * + * @param string $field + * + * @return bool + */ + public function hasChangedField($field) + { + return isset($this->entityChangeSet[$field]); + } + + /** + * Gets the old value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getOldValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][0]; + } + + /** + * Gets the new value of the changeset of the changed field. + * + * @param string $field + * + * @return mixed + */ + public function getNewValue($field) + { + $this->assertValidField($field); + + return $this->entityChangeSet[$field][1]; + } + + /** + * Sets the new value of this field. + * + * @param string $field + * @param mixed $value + * + * @return void + */ + public function setNewValue($field, $value) + { + $this->assertValidField($field); + + $this->entityChangeSet[$field][1] = $value; + } + + /** + * Asserts the field exists in changeset. + * + * @param string $field + * + * @return void + * + * @throws \InvalidArgumentException + */ + private function assertValidField($field) + { + if (! isset($this->entityChangeSet[$field])) { + throw new \InvalidArgumentException(sprintf( + 'Field "%s" is not a valid field of the entity "%s" in PreUpdateEventArgs.', + $field, + get_class($this->getObject()) + )); + } + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ManagerRegistry.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ManagerRegistry.php new file mode 100644 index 0000000..fdfdf91 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ManagerRegistry.php @@ -0,0 +1,88 @@ +cacheDriver = $cacheDriver; + } + + /** + * Gets the cache driver used by the factory to cache ClassMetadata instances. + * + * @return Cache|null + */ + public function getCacheDriver() + { + return $this->cacheDriver; + } + + /** + * Returns an array of all the loaded metadata currently in memory. + * + * @return ClassMetadata[] + */ + public function getLoadedMetadata() + { + return $this->loadedMetadata; + } + + /** + * Forces the factory to load the metadata of all classes known to the underlying + * mapping driver. + * + * @return ClassMetadata[] The ClassMetadata instances of all mapped classes. + */ + public function getAllMetadata() + { + if (! $this->initialized) { + $this->initialize(); + } + + $driver = $this->getDriver(); + $metadata = []; + foreach ($driver->getAllClassNames() as $className) { + $metadata[] = $this->getMetadataFor($className); + } + + return $metadata; + } + + /** + * Lazy initialization of this stuff, especially the metadata driver, + * since these are not needed at all when a metadata cache is active. + * + * @return void + */ + abstract protected function initialize(); + + /** + * Gets the fully qualified class-name from the namespace alias. + * + * @param string $namespaceAlias + * @param string $simpleClassName + * + * @return string + */ + abstract protected function getFqcnFromAlias($namespaceAlias, $simpleClassName); + + /** + * Returns the mapping driver implementation. + * + * @return MappingDriver + */ + abstract protected function getDriver(); + + /** + * Wakes up reflection after ClassMetadata gets unserialized from cache. + * + * @return void + */ + abstract protected function wakeupReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Initializes Reflection after ClassMetadata was constructed. + * + * @return void + */ + abstract protected function initializeReflection(ClassMetadata $class, ReflectionService $reflService); + + /** + * Checks whether the class metadata is an entity. + * + * This method should return false for mapped superclasses or embedded classes. + * + * @return bool + */ + abstract protected function isEntity(ClassMetadata $class); + + /** + * Gets the class metadata descriptor for a class. + * + * @param string $className The name of the class. + * + * @return ClassMetadata + * + * @throws ReflectionException + * @throws MappingException + */ + public function getMetadataFor($className) + { + if (isset($this->loadedMetadata[$className])) { + return $this->loadedMetadata[$className]; + } + + // Check for namespace alias + if (strpos($className, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $className, 2); + + $realClassName = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } else { + $realClassName = $this->getRealClass($className); + } + + if (isset($this->loadedMetadata[$realClassName])) { + // We do not have the alias name in the map, include it + return $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + $loadingException = null; + + try { + if ($this->cacheDriver) { + $cached = $this->cacheDriver->fetch($realClassName . $this->cacheSalt); + if ($cached instanceof ClassMetadata) { + $this->loadedMetadata[$realClassName] = $cached; + + $this->wakeupReflection($cached, $this->getReflectionService()); + } else { + foreach ($this->loadMetadata($realClassName) as $loadedClassName) { + $this->cacheDriver->save( + $loadedClassName . $this->cacheSalt, + $this->loadedMetadata[$loadedClassName], + null + ); + } + } + } else { + $this->loadMetadata($realClassName); + } + } catch (MappingException $loadingException) { + $fallbackMetadataResponse = $this->onNotFoundMetadata($realClassName); + + if (! $fallbackMetadataResponse) { + throw $loadingException; + } + + $this->loadedMetadata[$realClassName] = $fallbackMetadataResponse; + } + + if ($className !== $realClassName) { + // We do not have the alias name in the map, include it + $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName]; + } + + return $this->loadedMetadata[$className]; + } + + /** + * Checks whether the factory has the metadata for a class loaded already. + * + * @param string $className + * + * @return bool TRUE if the metadata of the class in question is already loaded, FALSE otherwise. + */ + public function hasMetadataFor($className) + { + return isset($this->loadedMetadata[$className]); + } + + /** + * Sets the metadata descriptor for a specific class. + * + * NOTE: This is only useful in very special cases, like when generating proxy classes. + * + * @param string $className + * @param ClassMetadata $class + * + * @return void + */ + public function setMetadataFor($className, $class) + { + $this->loadedMetadata[$className] = $class; + } + + /** + * Gets an array of parent classes for the given entity class. + * + * @param string $name + * + * @return string[] + */ + protected function getParentClasses($name) + { + // Collect parent classes, ignoring transient (not-mapped) classes. + $parentClasses = []; + + foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) { + if ($this->getDriver()->isTransient($parentClass)) { + continue; + } + + $parentClasses[] = $parentClass; + } + + return $parentClasses; + } + + /** + * Loads the metadata of the class in question and all it's ancestors whose metadata + * is still not loaded. + * + * Important: The class $name does not necessarily exist at this point here. + * Scenarios in a code-generation setup might have access to XML/YAML + * Mapping files without the actual PHP code existing here. That is why the + * {@see Doctrine\Common\Persistence\Mapping\ReflectionService} interface + * should be used for reflection. + * + * @param string $name The name of the class for which the metadata should get loaded. + * + * @return string[] + */ + protected function loadMetadata($name) + { + if (! $this->initialized) { + $this->initialize(); + } + + $loaded = []; + + $parentClasses = $this->getParentClasses($name); + $parentClasses[] = $name; + + // Move down the hierarchy of parent classes, starting from the topmost class + $parent = null; + $rootEntityFound = false; + $visited = []; + $reflService = $this->getReflectionService(); + foreach ($parentClasses as $className) { + if (isset($this->loadedMetadata[$className])) { + $parent = $this->loadedMetadata[$className]; + if ($this->isEntity($parent)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + continue; + } + + $class = $this->newClassMetadataInstance($className); + $this->initializeReflection($class, $reflService); + + $this->doLoadMetadata($class, $parent, $rootEntityFound, $visited); + + $this->loadedMetadata[$className] = $class; + + $parent = $class; + + if ($this->isEntity($class)) { + $rootEntityFound = true; + array_unshift($visited, $className); + } + + $this->wakeupReflection($class, $reflService); + + $loaded[] = $className; + } + + return $loaded; + } + + /** + * Provides a fallback hook for loading metadata when loading failed due to reflection/mapping exceptions + * + * Override this method to implement a fallback strategy for failed metadata loading + * + * @param string $className + * + * @return ClassMetadata|null + */ + protected function onNotFoundMetadata($className) + { + return null; + } + + /** + * Actually loads the metadata from the underlying metadata. + * + * @param ClassMetadata $class + * @param ClassMetadata|null $parent + * @param bool $rootEntityFound + * @param string[] $nonSuperclassParents All parent class names + * that are not marked as mapped superclasses. + * @return void + */ + abstract protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents); + + /** + * Creates a new ClassMetadata instance for the given class name. + * + * @param string $className + * + * @return ClassMetadata + */ + abstract protected function newClassMetadataInstance($className); + + /** + * {@inheritDoc} + */ + public function isTransient($class) + { + if (! $this->initialized) { + $this->initialize(); + } + + // Check for namespace alias + if (strpos($class, ':') !== false) { + list($namespaceAlias, $simpleClassName) = explode(':', $class, 2); + $class = $this->getFqcnFromAlias($namespaceAlias, $simpleClassName); + } + + return $this->getDriver()->isTransient($class); + } + + /** + * Sets the reflectionService. + * + * @return void + */ + public function setReflectionService(ReflectionService $reflectionService) + { + $this->reflectionService = $reflectionService; + } + + /** + * Gets the reflection service associated with this metadata factory. + * + * @return ReflectionService + */ + public function getReflectionService() + { + if ($this->reflectionService === null) { + $this->reflectionService = new RuntimeReflectionService(); + } + return $this->reflectionService; + } + + /** + * Gets the real class name of a class name that could be a proxy. + */ + private function getRealClass(string $class) : string + { + $pos = strrpos($class, '\\' . Proxy::MARKER . '\\'); + + if ($pos === false) { + return $class; + } + + return substr($class, $pos + Proxy::MARKER_LENGTH + 2); + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php new file mode 100644 index 0000000..0fc6f69 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/ClassMetadata.php @@ -0,0 +1,152 @@ +reader = $reader; + if (! $paths) { + return; + } + + $this->addPaths((array) $paths); + } + + /** + * Appends lookup paths to metadata driver. + * + * @param string[] $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return string[] + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Append exclude lookup paths to metadata driver. + * + * @param string[] $paths + */ + public function addExcludePaths(array $paths) + { + $this->excludePaths = array_unique(array_merge($this->excludePaths, $paths)); + } + + /** + * Retrieve the defined metadata lookup exclude paths. + * + * @return string[] + */ + public function getExcludePaths() + { + return $this->excludePaths; + } + + /** + * Retrieve the current annotation reader + * + * @return Reader + */ + public function getReader() + { + return $this->reader; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * Returns whether the class with the specified name is transient. Only non-transient + * classes, that is entities and mapped superclasses, should have their metadata loaded. + * + * A class is non-transient if it is annotated with an annotation + * from the {@see AnnotationDriver::entityAnnotationClasses}. + * + * @param string $className + * + * @return bool + */ + public function isTransient($className) + { + $classAnnotations = $this->reader->getClassAnnotations(new \ReflectionClass($className)); + + foreach ($classAnnotations as $annot) { + if (isset($this->entityAnnotationClasses[get_class($annot)])) { + return false; + } + } + return true; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (! $this->paths) { + throw MappingException::pathRequired(); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RegexIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + '/^.+' . preg_quote($this->fileExtension) . '$/i', + \RecursiveRegexIterator::GET_MATCH + ); + + foreach ($iterator as $file) { + $sourceFile = $file[0]; + + if (! preg_match('(^phar:)i', $sourceFile)) { + $sourceFile = realpath($sourceFile); + } + + foreach ($this->excludePaths as $excludePath) { + $exclude = str_replace('\\', '/', realpath($excludePath)); + $current = str_replace('\\', '/', $sourceFile); + + if (strpos($current, $exclude) !== false) { + continue 2; + } + } + + require_once $sourceFile; + + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (! in_array($sourceFile, $includedFiles) || $this->isTransient($className)) { + continue; + } + + $classes[] = $className; + } + + $this->classNames = $classes; + + return $classes; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php new file mode 100644 index 0000000..71dd73f --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/DefaultFileLocator.php @@ -0,0 +1,159 @@ +addPaths((array) $paths); + $this->fileExtension = $fileExtension; + } + + /** + * Appends lookup paths to metadata driver. + * + * @param string[] $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * Retrieves the defined metadata lookup paths. + * + * @return string[] + */ + public function getPaths() + { + return $this->paths; + } + + /** + * Gets the file extension used to look for mapping files under. + * + * @return string|null + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string|null $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ($this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return $path . DIRECTORY_SEPARATOR . $fileName; + } + } + + throw MappingException::mappingFileNotFound($className, $fileName); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename) + { + $classes = []; + + if ($this->paths) { + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName === $file->getBasename() || $fileName === $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + $classes[] = str_replace('.', '\\', $fileName); + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $fileName = str_replace('\\', '.', $className) . $this->fileExtension; + + // Check whether file exists + foreach ((array) $this->paths as $path) { + if (is_file($path . DIRECTORY_SEPARATOR . $fileName)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php new file mode 100644 index 0000000..85b437e --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileDriver.php @@ -0,0 +1,193 @@ +locator = $locator; + } else { + $this->locator = new DefaultFileLocator((array) $locator, $fileExtension); + } + } + + /** + * Sets the global basename. + * + * @param string $file + * + * @return void + */ + public function setGlobalBasename($file) + { + $this->globalBasename = $file; + } + + /** + * Retrieves the global basename. + * + * @return string|null + */ + public function getGlobalBasename() + { + return $this->globalBasename; + } + + /** + * Gets the element of schema meta data for the class from the mapping file. + * This will lazily load the mapping file if it is not loaded yet. + * + * @param string $className + * + * @return ClassMetadata The element of schema meta data. + * + * @throws MappingException + */ + public function getElement($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return $this->classCache[$className]; + } + + $result = $this->loadMappingFile($this->locator->findMappingFile($className)); + if (! isset($result[$className])) { + throw MappingException::invalidMappingFile($className, str_replace('\\', '.', $className) . $this->locator->getFileExtension()); + } + + $this->classCache[$className] = $result[$className]; + + return $result[$className]; + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (isset($this->classCache[$className])) { + return false; + } + + return ! $this->locator->fileExists($className); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + if ($this->classCache === null) { + $this->initialize(); + } + + if (! $this->classCache) { + return (array) $this->locator->getAllClassNames($this->globalBasename); + } + + return array_merge( + array_keys($this->classCache), + (array) $this->locator->getAllClassNames($this->globalBasename) + ); + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding file driver elements. + * + * @param string $file The mapping file to load. + * + * @return ClassMetadata[] + */ + abstract protected function loadMappingFile($file); + + /** + * Initializes the class cache from all the global files. + * + * Using this feature adds a substantial performance hit to file drivers as + * more metadata has to be loaded into memory than might actually be + * necessary. This may not be relevant to scenarios where caching of + * metadata is in place, however hits very hard in scenarios where no + * caching is used. + * + * @return void + */ + protected function initialize() + { + $this->classCache = []; + if ($this->globalBasename === null) { + return; + } + + foreach ($this->locator->getPaths() as $path) { + $file = $path . '/' . $this->globalBasename . $this->locator->getFileExtension(); + if (! is_file($file)) { + continue; + } + + $this->classCache = array_merge( + $this->classCache, + $this->loadMappingFile($file) + ); + } + } + + /** + * Retrieves the locator used to discover mapping files by className. + * + * @return FileLocator + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Sets the locator used to discover mapping files by className. + */ + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php new file mode 100644 index 0000000..3b36741 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/FileLocator.php @@ -0,0 +1,53 @@ +defaultDriver; + } + + /** + * Set the default driver. + * + * @return void + */ + public function setDefaultDriver(MappingDriver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * Adds a nested driver. + * + * @param string $namespace + * + * @return void + */ + public function addDriver(MappingDriver $nestedDriver, $namespace) + { + $this->drivers[$namespace] = $nestedDriver; + } + + /** + * Gets the array of nested drivers. + * + * @return MappingDriver[] $drivers + */ + public function getDrivers() + { + return $this->drivers; + } + + /** + * {@inheritDoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + /** @var MappingDriver $driver */ + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + $driver->loadMetadataForClass($className, $metadata); + return; + } + } + + if ($this->defaultDriver !== null) { + $this->defaultDriver->loadMetadataForClass($className, $metadata); + return; + } + + throw MappingException::classNotFoundInNamespaces($className, array_keys($this->drivers)); + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames() + { + $classNames = []; + $driverClasses = []; + + /** @var MappingDriver $driver */ + foreach ($this->drivers as $namespace => $driver) { + $oid = spl_object_hash($driver); + + if (! isset($driverClasses[$oid])) { + $driverClasses[$oid] = $driver->getAllClassNames(); + } + + foreach ($driverClasses[$oid] as $className) { + if (strpos($className, $namespace) !== 0) { + continue; + } + + $classNames[$className] = true; + } + } + + if ($this->defaultDriver !== null) { + foreach ($this->defaultDriver->getAllClassNames() as $className) { + $classNames[$className] = true; + } + } + + return array_keys($classNames); + } + + /** + * {@inheritDoc} + */ + public function isTransient($className) + { + /** @var MappingDriver $driver */ + foreach ($this->drivers as $namespace => $driver) { + if (strpos($className, $namespace) === 0) { + return $driver->isTransient($className); + } + } + + if ($this->defaultDriver !== null) { + return $this->defaultDriver->isTransient($className); + } + + return true; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php new file mode 100644 index 0000000..03e1746 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/PHPDriver.php @@ -0,0 +1,44 @@ +metadata = $metadata; + + $this->loadMappingFile($this->locator->findMappingFile($className)); + } + + /** + * {@inheritDoc} + */ + protected function loadMappingFile($file) + { + $metadata = $this->metadata; + include $file; + + return [$metadata->getName() => $metadata]; + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php new file mode 100644 index 0000000..4267bf3 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/StaticPHPDriver.php @@ -0,0 +1,125 @@ +addPaths((array) $paths); + } + + /** + * Adds paths. + * + * @param string[] $paths + * + * @return void + */ + public function addPaths(array $paths) + { + $this->paths = array_unique(array_merge($this->paths, $paths)); + } + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadata $metadata) + { + $className::loadMetadata($metadata); + } + + /** + * {@inheritDoc} + * @todo Same code exists in AnnotationDriver, should we re-use it somehow or not worry about it? + */ + public function getAllClassNames() + { + if ($this->classNames !== null) { + return $this->classNames; + } + + if (! $this->paths) { + throw MappingException::pathRequired(); + } + + $classes = []; + $includedFiles = []; + + foreach ($this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + if ($file->getBasename('.php') === $file->getBasename()) { + continue; + } + + $sourceFile = realpath($file->getPathName()); + require_once $sourceFile; + $includedFiles[] = $sourceFile; + } + } + + $declared = get_declared_classes(); + + foreach ($declared as $className) { + $rc = new \ReflectionClass($className); + $sourceFile = $rc->getFileName(); + if (! in_array($sourceFile, $includedFiles) || $this->isTransient($className)) { + continue; + } + + $classes[] = $className; + } + + $this->classNames = $classes; + + return $classes; + } + + /** + * {@inheritdoc} + */ + public function isTransient($className) + { + return ! method_exists($className, 'loadMetadata'); + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php new file mode 100644 index 0000000..596a4ec --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/Driver/SymfonyFileLocator.php @@ -0,0 +1,227 @@ +addNamespacePrefixes($prefixes); + $this->fileExtension = $fileExtension; + + if (empty($nsSeparator)) { + throw new \InvalidArgumentException('Namespace separator should not be empty'); + } + + $this->nsSeparator = (string) $nsSeparator; + } + + /** + * Adds Namespace Prefixes. + * + * @param string[] $prefixes + * + * @return void + */ + public function addNamespacePrefixes(array $prefixes) + { + $this->prefixes = array_merge($this->prefixes, $prefixes); + $this->paths = array_merge($this->paths, array_keys($prefixes)); + } + + /** + * Gets Namespace Prefixes. + * + * @return string[] + */ + public function getNamespacePrefixes() + { + return $this->prefixes; + } + + /** + * {@inheritDoc} + */ + public function getPaths() + { + return $this->paths; + } + + /** + * {@inheritDoc} + */ + public function getFileExtension() + { + return $this->fileExtension; + } + + /** + * Sets the file extension used to look for mapping files under. + * + * @param string $fileExtension The file extension to set. + * + * @return void + */ + public function setFileExtension($fileExtension) + { + $this->fileExtension = $fileExtension; + } + + /** + * {@inheritDoc} + */ + public function fileExists($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension; + foreach ($this->paths as $path) { + if (! isset($this->prefixes[$path])) { + // global namespace class + if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) { + return true; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (strpos($className, $prefix . '\\') !== 0) { + continue; + } + + $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension; + if (is_file($filename)) { + return true; + } + } + + return false; + } + + /** + * {@inheritDoc} + */ + public function getAllClassNames($globalBasename = null) + { + $classes = []; + + if ($this->paths) { + foreach ((array) $this->paths as $path) { + if (! is_dir($path)) { + throw MappingException::fileMappingDriversRequireConfiguredDirectoryPath($path); + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($path), + \RecursiveIteratorIterator::LEAVES_ONLY + ); + + foreach ($iterator as $file) { + $fileName = $file->getBasename($this->fileExtension); + + if ($fileName === $file->getBasename() || $fileName === $globalBasename) { + continue; + } + + // NOTE: All files found here means classes are not transient! + if (isset($this->prefixes[$path])) { + // Calculate namespace suffix for given prefix as a relative path from basepath to file path + $nsSuffix = strtr( + substr(realpath($file->getPath()), strlen(realpath($path))), + $this->nsSeparator, + '\\' + ); + + $classes[] = $this->prefixes[$path] . str_replace(DIRECTORY_SEPARATOR, '\\', $nsSuffix) . '\\' . str_replace($this->nsSeparator, '\\', $fileName); + } else { + $classes[] = str_replace($this->nsSeparator, '\\', $fileName); + } + } + } + } + + return $classes; + } + + /** + * {@inheritDoc} + */ + public function findMappingFile($className) + { + $defaultFileName = str_replace('\\', $this->nsSeparator, $className) . $this->fileExtension; + foreach ($this->paths as $path) { + if (! isset($this->prefixes[$path])) { + if (is_file($path . DIRECTORY_SEPARATOR . $defaultFileName)) { + return $path . DIRECTORY_SEPARATOR . $defaultFileName; + } + + continue; + } + + $prefix = $this->prefixes[$path]; + + if (strpos($className, $prefix . '\\') !== 0) { + continue; + } + + $filename = $path . '/' . strtr(substr($className, strlen($prefix) + 1), '\\', $this->nsSeparator) . $this->fileExtension; + if (is_file($filename)) { + return $filename; + } + } + + throw MappingException::mappingFileNotFound($className, substr($className, strrpos($className, '\\') + 1) . $this->fileExtension); + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/MappingException.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/MappingException.php new file mode 100644 index 0000000..bae8ac5 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/MappingException.php @@ -0,0 +1,95 @@ +getShortName(); + } + + /** + * {@inheritDoc} + */ + public function getClassNamespace($class) + { + $reflectionClass = new ReflectionClass($class); + + return $reflectionClass->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getClass($class) + { + return new ReflectionClass($class); + } + + /** + * {@inheritDoc} + */ + public function getAccessibleProperty($class, $property) + { + $reflectionProperty = new ReflectionProperty($class, $property); + + if ($reflectionProperty->isPublic()) { + $reflectionProperty = new RuntimePublicReflectionProperty($class, $property); + } + + $reflectionProperty->setAccessible(true); + + return $reflectionProperty; + } + + /** + * {@inheritDoc} + */ + public function hasPublicMethod($class, $method) + { + try { + $reflectionMethod = new ReflectionMethod($class, $method); + } catch (ReflectionException $e) { + return false; + } + + return $reflectionMethod->isPublic(); + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php new file mode 100644 index 0000000..a866aec --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Mapping/StaticReflectionService.php @@ -0,0 +1,69 @@ +find($id). + * + * @param string $className The class name of the object to find. + * @param mixed $id The identity of the object to find. + * + * @return object|null The found object. + */ + public function find($className, $id); + + /** + * Tells the ObjectManager to make an instance managed and persistent. + * + * The object will be entered into the database as a result of the flush operation. + * + * NOTE: The persist operation always considers objects that are not yet known to + * this ObjectManager as NEW. Do not pass detached objects to the persist operation. + * + * @param object $object The instance to make managed and persistent. + * + * @return void + */ + public function persist($object); + + /** + * Removes an object instance. + * + * A removed object will be removed from the database as a result of the flush operation. + * + * @param object $object The object instance to remove. + * + * @return void + */ + public function remove($object); + + /** + * Merges the state of a detached object into the persistence context + * of this ObjectManager and returns the managed copy of the object. + * The object passed to merge will not become associated/managed with this ObjectManager. + * + * @param object $object + * + * @return object + */ + public function merge($object); + + /** + * Clears the ObjectManager. All objects that are currently managed + * by this ObjectManager become detached. + * + * @param string|null $objectName if given, only objects of this type will get detached. + * + * @return void + */ + public function clear($objectName = null); + + /** + * Detaches an object from the ObjectManager, causing a managed object to + * become detached. Unflushed changes made to the object if any + * (including removal of the object), will not be synchronized to the database. + * Objects which previously referenced the detached object will continue to + * reference it. + * + * @param object $object The object to detach. + * + * @return void + */ + public function detach($object); + + /** + * Refreshes the persistent state of an object from the database, + * overriding any local changes that have not yet been persisted. + * + * @param object $object The object to refresh. + * + * @return void + */ + public function refresh($object); + + /** + * Flushes all changes to objects that have been queued up to now to the database. + * This effectively synchronizes the in-memory state of managed objects with the + * database. + * + * @return void + */ + public function flush(); + + /** + * Gets the repository for a class. + * + * @param string $className + * + * @return ObjectRepository + */ + public function getRepository($className); + + /** + * Returns the ClassMetadata descriptor for a class. + * + * The class name must be the fully-qualified class name without a leading backslash + * (as it is returned by get_class($obj)). + * + * @param string $className + * + * @return ClassMetadata + */ + public function getClassMetadata($className); + + /** + * Gets the metadata factory used to gather the metadata of classes. + * + * @return ClassMetadataFactory + */ + public function getMetadataFactory(); + + /** + * Helper method to initialize a lazy loading proxy or persistent collection. + * + * This method is a no-op for other objects. + * + * @param object $obj + * + * @return void + */ + public function initializeObject($obj); + + /** + * Checks if the object is part of the current UnitOfWork and therefore managed. + * + * @param object $object + * + * @return bool + */ + public function contains($object); +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectManagerAware.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectManagerAware.php new file mode 100644 index 0000000..7d3aaf0 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectManagerAware.php @@ -0,0 +1,29 @@ +wrapped->find($className, $id); + } + + /** + * {@inheritdoc} + */ + public function persist($object) + { + $this->wrapped->persist($object); + } + + /** + * {@inheritdoc} + */ + public function remove($object) + { + $this->wrapped->remove($object); + } + + /** + * {@inheritdoc} + */ + public function merge($object) + { + return $this->wrapped->merge($object); + } + + /** + * {@inheritdoc} + */ + public function clear($objectName = null) + { + $this->wrapped->clear($objectName); + } + + /** + * {@inheritdoc} + */ + public function detach($object) + { + $this->wrapped->detach($object); + } + + /** + * {@inheritdoc} + */ + public function refresh($object) + { + $this->wrapped->refresh($object); + } + + /** + * {@inheritdoc} + */ + public function flush() + { + $this->wrapped->flush(); + } + + /** + * {@inheritdoc} + */ + public function getRepository($className) + { + return $this->wrapped->getRepository($className); + } + + /** + * {@inheritdoc} + */ + public function getClassMetadata($className) + { + return $this->wrapped->getClassMetadata($className); + } + + /** + * {@inheritdoc} + */ + public function getMetadataFactory() + { + return $this->wrapped->getMetadataFactory(); + } + + /** + * {@inheritdoc} + */ + public function initializeObject($obj) + { + $this->wrapped->initializeObject($obj); + } + + /** + * {@inheritdoc} + */ + public function contains($object) + { + return $this->wrapped->contains($object); + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectRepository.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectRepository.php new file mode 100644 index 0000000..cc06b2b --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/ObjectRepository.php @@ -0,0 +1,59 @@ +getId(); // method exists through __call + */ +abstract class PersistentObject implements ObjectManagerAware +{ + /** @var ObjectManager|null */ + private static $objectManager = null; + + /** @var ClassMetadata|null */ + private $cm = null; + + /** + * Sets the object manager responsible for all persistent object base classes. + * + * @return void + */ + public static function setObjectManager(?ObjectManager $objectManager = null) + { + self::$objectManager = $objectManager; + } + + /** + * @return ObjectManager|null + */ + public static function getObjectManager() + { + return self::$objectManager; + } + + /** + * Injects the Doctrine Object Manager. + * + * @return void + * + * @throws \RuntimeException + */ + public function injectObjectManager(ObjectManager $objectManager, ClassMetadata $classMetadata) + { + if ($objectManager !== self::$objectManager) { + throw new \RuntimeException('Trying to use PersistentObject with different ObjectManager instances. ' . + 'Was PersistentObject::setObjectManager() called?'); + } + + $this->cm = $classMetadata; + } + + /** + * Sets a persistent fields value. + * + * @param string $field + * @param mixed[] $args + * + * @return void + * + * @throws \BadMethodCallException When no persistent field exists by that name. + * @throws \InvalidArgumentException When the wrong target object type is passed to an association. + */ + private function set($field, $args) + { + if ($this->cm->hasField($field) && ! $this->cm->isIdentifier($field)) { + $this->$field = $args[0]; + } elseif ($this->cm->hasAssociation($field) && $this->cm->isSingleValuedAssociation($field)) { + $targetClass = $this->cm->getAssociationTargetClass($field); + if (! ($args[0] instanceof $targetClass) && $args[0] !== null) { + throw new \InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); + } + $this->$field = $args[0]; + $this->completeOwningSide($field, $targetClass, $args[0]); + } else { + throw new \BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm->getName() . "'"); + } + } + + /** + * Gets a persistent field value. + * + * @param string $field + * + * @return mixed + * + * @throws \BadMethodCallException When no persistent field exists by that name. + */ + private function get($field) + { + if ($this->cm->hasField($field) || $this->cm->hasAssociation($field)) { + return $this->$field; + } + + throw new \BadMethodCallException("no field with name '" . $field . "' exists on '" . $this->cm->getName() . "'"); + } + + /** + * If this is an inverse side association, completes the owning side. + * + * @param string $field + * @param ClassMetadata $targetClass + * @param object $targetObject + * + * @return void + */ + private function completeOwningSide($field, $targetClass, $targetObject) + { + // add this object on the owning side as well, for obvious infinite recursion + // reasons this is only done when called on the inverse side. + if (! $this->cm->isAssociationInverseSide($field)) { + return; + } + + $mappedByField = $this->cm->getAssociationMappedByTargetField($field); + $targetMetadata = self::$objectManager->getClassMetadata($targetClass); + + $setter = ($targetMetadata->isCollectionValuedAssociation($mappedByField) ? 'add' : 'set') . $mappedByField; + $targetObject->$setter($this); + } + + /** + * Adds an object to a collection. + * + * @param string $field + * @param mixed[] $args + * + * @return void + * + * @throws \BadMethodCallException + * @throws \InvalidArgumentException + */ + private function add($field, $args) + { + if (! $this->cm->hasAssociation($field) || ! $this->cm->isCollectionValuedAssociation($field)) { + throw new \BadMethodCallException('There is no method add' . $field . '() on ' . $this->cm->getName()); + } + + $targetClass = $this->cm->getAssociationTargetClass($field); + if (! ($args[0] instanceof $targetClass)) { + throw new \InvalidArgumentException("Expected persistent object of type '" . $targetClass . "'"); + } + if (! ($this->$field instanceof Collection)) { + $this->$field = new ArrayCollection($this->$field ?: []); + } + $this->$field->add($args[0]); + $this->completeOwningSide($field, $targetClass, $args[0]); + } + + /** + * Initializes Doctrine Metadata for this class. + * + * @return void + * + * @throws \RuntimeException + */ + private function initializeDoctrine() + { + if ($this->cm !== null) { + return; + } + + if (! self::$objectManager) { + throw new \RuntimeException('No runtime object manager set. Call PersistentObject#setObjectManager().'); + } + + $this->cm = self::$objectManager->getClassMetadata(get_class($this)); + } + + /** + * Magic methods. + * + * @param string $method + * @param mixed[] $args + * + * @return mixed + * + * @throws \BadMethodCallException + */ + public function __call($method, $args) + { + $this->initializeDoctrine(); + + $command = substr($method, 0, 3); + $field = lcfirst(substr($method, 3)); + if ($command === 'set') { + $this->set($field, $args); + } elseif ($command === 'get') { + return $this->get($field); + } elseif ($command === 'add') { + $this->add($field, $args); + } else { + throw new \BadMethodCallException('There is no method ' . $method . ' on ' . $this->cm->getName()); + } + } +} diff --git a/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Proxy.php b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Proxy.php new file mode 100644 index 0000000..2eb9843 --- /dev/null +++ b/vendor/doctrine/persistence/lib/Doctrine/Common/Persistence/Proxy.php @@ -0,0 +1,39 @@ += MAJOR).new.exists' # New issues of major or higher severity + - 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection diff --git a/vendor/doctrine/reflection/LICENSE b/vendor/doctrine/reflection/LICENSE new file mode 100644 index 0000000..8c38cc1 --- /dev/null +++ b/vendor/doctrine/reflection/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2006-2015 Doctrine Project + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/doctrine/reflection/README.md b/vendor/doctrine/reflection/README.md new file mode 100644 index 0000000..756c55b --- /dev/null +++ b/vendor/doctrine/reflection/README.md @@ -0,0 +1,13 @@ +# Doctrine Reflection + +[![Build Status](https://travis-ci.org/doctrine/reflection.svg)](https://travis-ci.org/doctrine/reflection) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/doctrine/reflection/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/doctrine/reflection/?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/doctrine/reflection/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/doctrine/reflection/?branch=master) + +The Doctrine Reflection project is a simple library used by the various Doctrine projects which adds some additional functionality on top of the reflection functionality that comes with PHP. It allows you to get the reflection information about classes, methods and properties statically. + +## More resources: + +* [Website](https://www.doctrine-project.org/) +* [Documentation](https://www.doctrine-project.org/projects/doctrine-reflection/en/latest/) +* [Downloads](https://github.com/doctrine/reflection/releases) diff --git a/vendor/doctrine/reflection/composer.json b/vendor/doctrine/reflection/composer.json new file mode 100644 index 0000000..ad31969 --- /dev/null +++ b/vendor/doctrine/reflection/composer.json @@ -0,0 +1,44 @@ +{ + "name": "doctrine/reflection", + "type": "library", + "description": "Doctrine Reflection component", + "keywords": ["reflection"], + "homepage": "https://www.doctrine-project.org/projects/reflection.html", + "license": "MIT", + "authors": [ + {"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"}, + {"name": "Roman Borschel", "email": "roman@code-factory.org"}, + {"name": "Benjamin Eberlei", "email": "kontakt@beberlei.de"}, + {"name": "Jonathan Wage", "email": "jonwage@gmail.com"}, + {"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}, + {"name": "Marco Pivetta", "email": "ocramius@gmail.com"} + ], + "require": { + "php": "^7.1", + "ext-tokenizer": "*", + "doctrine/annotations": "^1.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.9.2", + "phpstan/phpstan-phpunit": "^0.9.4", + "phpunit/phpunit": "^7.0", + "doctrine/coding-standard": "^4.0", + "doctrine/common": "^2.8", + "squizlabs/php_codesniffer": "^3.0" + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "autoload-dev": { + "psr-4": { + "Doctrine\\Tests\\": "tests/Doctrine/Tests" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/doctrine/reflection/docs/en/index.rst b/vendor/doctrine/reflection/docs/en/index.rst new file mode 100644 index 0000000..ae85870 --- /dev/null +++ b/vendor/doctrine/reflection/docs/en/index.rst @@ -0,0 +1,22 @@ +Reflection Documentation +================= + +The Doctrine Reflection documentation is a reference guide to everything you need +to know about the reflection project. + +Getting Help +------------ + +If this documentation is not helping to answer questions you have about +Doctrine Reflection don't panic. You can get help from different sources: + +- The `Doctrine Mailing List `_ +- Gitter chat room `#doctrine/reflection `_ +- Report a bug on `GitHub `_. +- On `StackOverflow `_ + +Getting Started +--------------- + +The best way to get started is with the :doc:`Introduction ` section. +Use the sidebar to browse other documentation for the Doctrine PHP Reflection project. diff --git a/vendor/doctrine/reflection/docs/en/reference/index.rst b/vendor/doctrine/reflection/docs/en/reference/index.rst new file mode 100644 index 0000000..e383a17 --- /dev/null +++ b/vendor/doctrine/reflection/docs/en/reference/index.rst @@ -0,0 +1,85 @@ +Introduction +============ + +The Doctrine Reflection project is a simple library used by the various Doctrine projects which adds some additional +functionality on top of the reflection functionality that comes with PHP. It allows you to get the reflection information +about classes, methods and properties statically. + +Installation +============ + +The library can easily be installed with composer. + +.. code-block:: sh + + $ composer require doctrine/reflection + +Setup +===== + +.. code-block:: php + + use Doctrine\Common\Reflection\Psr0FindFile; + use Doctrine\Common\Reflection\StaticReflectionParser; + use App\Model\User; + + $finder = new Psr0FindFile(['App' => [ + '/path/to/project/src/App' + ]]); + + $staticReflectionParser = new StaticReflectionParser(User::class, $finder); + +Usage +===== + +.. code-block:: php + + echo $staticReflectionParser->getClassName(); + echo $staticReflectionParser->getNamespaceName(); + +StaticReflectionClass +===================== + +.. code-block:: php + + $staticReflectionClass = $staticReflectionParser->getReflectionClass(); + + echo $staticReflectionClass->getName(); + + echo $staticReflectionClass->getDocComment(); + + echo $staticReflectionClass->getNamespaceName(); + + print_r($staticReflectionClass->getUseStatements()); + +StaticReflectionMethod +====================== + +.. code-block:: php + + $staticReflectionMethod = $staticReflectionParser->getReflectionMethod('getSomething'); + + echo $staticReflectionMethod->getName(); + + echo $staticReflectionMethod->getDeclaringClass(); + + echo $staticReflectionMethod->getNamespaceName(); + + echo $staticReflectionMethod->getDocComment(); + + print_r($staticReflectionMethod->getUseStatements()); + +StaticReflectionProperty +======================== + +.. code-block:: php + + $staticReflectionProperty = $staticReflectionParser->getReflectionProperty('something'); + + echo $staticReflectionProperty->getName(); + + echo $staticReflectionProperty->getDeclaringClass(); + + echo $staticReflectionProperty->getDocComment(); + + print_r($staticReflectionProperty->getUseStatements()); diff --git a/vendor/doctrine/reflection/docs/en/sidebar.rst b/vendor/doctrine/reflection/docs/en/sidebar.rst new file mode 100644 index 0000000..0672d8d --- /dev/null +++ b/vendor/doctrine/reflection/docs/en/sidebar.rst @@ -0,0 +1,4 @@ +.. toctree:: + :depth: 3 + + reference/index diff --git a/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ClassFinderInterface.php b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ClassFinderInterface.php new file mode 100644 index 0000000..8f4ed00 --- /dev/null +++ b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ClassFinderInterface.php @@ -0,0 +1,18 @@ +prefixes = $prefixes; + } + + /** + * {@inheritDoc} + */ + public function findFile($class) + { + if ($class[0] === '\\') { + $class = substr($class, 1); + } + + $lastNsPos = strrpos($class, '\\'); + + if ($lastNsPos !== false) { + // namespaced class name + $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $lastNsPos)) . DIRECTORY_SEPARATOR; + $className = substr($class, $lastNsPos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if (strpos($class, $prefix) !== 0) { + continue; + } + + foreach ($dirs as $dir) { + if (is_file($dir . DIRECTORY_SEPARATOR . $classPath)) { + return $dir . DIRECTORY_SEPARATOR . $classPath; + } + } + } + + return null; + } +} diff --git a/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php new file mode 100644 index 0000000..473325b --- /dev/null +++ b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/ReflectionProviderInterface.php @@ -0,0 +1,31 @@ +getName(); + + if ($object instanceof Proxy && ! $object->__isInitialized()) { + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + $val = $object->$name ?? null; + $object->__setInitializer($originalInitializer); + + return $val; + } + + return isset($object->$name) ? parent::getValue($object) : null; + } + + /** + * {@inheritDoc} + * + * Avoids triggering lazy loading via `__set` if the provided object + * is a {@see \Doctrine\Common\Proxy\Proxy}. + * @link https://bugs.php.net/bug.php?id=63463 + */ + public function setValue($object, $value = null) + { + if (! ($object instanceof Proxy && ! $object->__isInitialized())) { + parent::setValue($object, $value); + + return; + } + + $originalInitializer = $object->__getInitializer(); + $object->__setInitializer(null); + parent::setValue($object, $value); + $object->__setInitializer($originalInitializer); + } +} diff --git a/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionClass.php b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionClass.php new file mode 100644 index 0000000..180f5a6 --- /dev/null +++ b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionClass.php @@ -0,0 +1,413 @@ +staticReflectionParser = $staticReflectionParser; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->staticReflectionParser->getClassName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->staticReflectionParser->getDocComment(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->staticReflectionParser->getNamespaceName(); + } + + /** + * @return string[] + */ + public function getUseStatements() + { + return $this->staticReflectionParser->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public function getMethod($name) + { + return $this->staticReflectionParser->getReflectionMethod($name); + } + + /** + * {@inheritDoc} + */ + public function getProperty($name) + { + return $this->staticReflectionParser->getReflectionProperty($name); + } + + /** + * {@inheritDoc} + */ + public static function export($argument, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstants() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getDefaultProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaceNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getInterfaces() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getMethods($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParentClass() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getProperties($filter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticProperties() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticPropertyValue($name, $default = '') + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitAliases() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraitNames() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getTraits() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasConstant($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasMethod($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function hasProperty($name) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function implementsInterface($interface) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isCloneable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstance($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInstantiable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInterface() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isIterateable() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isSubclassOf($class) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isTrait() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstance($args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceArgs(array $args = []) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function newInstanceWithoutConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setStaticPropertyValue($name, $value) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php new file mode 100644 index 0000000..67bd998 --- /dev/null +++ b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionMethod.php @@ -0,0 +1,344 @@ +staticReflectionParser = $staticReflectionParser; + $this->methodName = $methodName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->methodName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('method', $this->methodName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getNamespaceName() + { + return $this->getStaticReflectionParser()->getNamespaceName(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('method', $this->methodName); + } + + /** + * @return string[] + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosure($object) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getPrototype() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invoke($object, $parameter = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function invokeArgs($object, array $args) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isAbstract() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isConstructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDestructor() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isFinal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getClosureThis() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getEndLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtension() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getExtensionName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getFileName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getNumberOfRequiredParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getParameters() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getShortName() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStartLine() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getStaticVariables() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function inNamespace() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isClosure() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDeprecated() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isInternal() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isUserDefined() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function returnsReference() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionParser.php b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionParser.php new file mode 100644 index 0000000..94e0b07 --- /dev/null +++ b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionParser.php @@ -0,0 +1,328 @@ + '', + 'property' => [], + 'method' => [], + ]; + + /** + * The name of the class this class extends, if any. + * + * @var string + */ + protected $parentClassName = ''; + + /** + * The parent PSR-0 Parser. + * + * @var \Doctrine\Common\Reflection\StaticReflectionParser + */ + protected $parentStaticReflectionParser; + + /** + * Parses a class residing in a PSR-0 hierarchy. + * + * @param string $className The full, namespaced class name. + * @param ClassFinderInterface $finder A ClassFinder object which finds the class. + * @param bool $classAnnotationOptimize Only retrieve the class docComment. + * Presumes there is only one statement per line. + */ + public function __construct($className, $finder, $classAnnotationOptimize = false) + { + $this->className = ltrim($className, '\\'); + $lastNsPos = strrpos($this->className, '\\'); + + if ($lastNsPos !== false) { + $this->namespace = substr($this->className, 0, $lastNsPos); + $this->shortClassName = substr($this->className, $lastNsPos + 1); + } else { + $this->shortClassName = $this->className; + } + + $this->finder = $finder; + $this->classAnnotationOptimize = $classAnnotationOptimize; + } + + /** + * @return void + */ + protected function parse() + { + $fileName = $this->finder->findFile($this->className); + + if ($this->parsed || ! $fileName) { + return; + } + $this->parsed = true; + $contents = file_get_contents($fileName); + if ($this->classAnnotationOptimize) { + $regex = sprintf('/\A.*^\s*((abstract|final)\s+)?class\s+%s\s+/sm', $this->shortClassName); + + if (preg_match($regex, $contents, $matches)) { + $contents = $matches[0]; + } + } + $tokenParser = new TokenParser($contents); + $docComment = ''; + $last_token = false; + + while ($token = $tokenParser->next(false)) { + switch ($token[0]) { + case T_USE: + $this->useStatements = array_merge($this->useStatements, $tokenParser->parseUseStatement()); + break; + case T_DOC_COMMENT: + $docComment = $token[1]; + break; + case T_CLASS: + if ($last_token !== T_PAAMAYIM_NEKUDOTAYIM) { + $this->docComment['class'] = $docComment; + $docComment = ''; + } + break; + case T_VAR: + case T_PRIVATE: + case T_PROTECTED: + case T_PUBLIC: + $token = $tokenParser->next(); + if ($token[0] === T_VARIABLE) { + $propertyName = substr($token[1], 1); + $this->docComment['property'][$propertyName] = $docComment; + continue 2; + } + if ($token[0] !== T_FUNCTION) { + // For example, it can be T_FINAL. + continue 2; + } + // No break. + case T_FUNCTION: + // The next string after function is the name, but + // there can be & before the function name so find the + // string. + while (($token = $tokenParser->next()) && $token[0] !== T_STRING) { + continue; + } + $methodName = $token[1]; + $this->docComment['method'][$methodName] = $docComment; + $docComment = ''; + break; + case T_EXTENDS: + $this->parentClassName = $tokenParser->parseClass(); + $nsPos = strpos($this->parentClassName, '\\'); + $fullySpecified = false; + if ($nsPos === 0) { + $fullySpecified = true; + } else { + if ($nsPos) { + $prefix = strtolower(substr($this->parentClassName, 0, $nsPos)); + $postfix = substr($this->parentClassName, $nsPos); + } else { + $prefix = strtolower($this->parentClassName); + $postfix = ''; + } + foreach ($this->useStatements as $alias => $use) { + if ($alias !== $prefix) { + continue; + } + + $this->parentClassName = '\\' . $use . $postfix; + $fullySpecified = true; + } + } + if (! $fullySpecified) { + $this->parentClassName = '\\' . $this->namespace . '\\' . $this->parentClassName; + } + break; + } + + $last_token = $token[0]; + } + } + + /** + * @return StaticReflectionParser + */ + protected function getParentStaticReflectionParser() + { + if (empty($this->parentStaticReflectionParser)) { + $this->parentStaticReflectionParser = new static($this->parentClassName, $this->finder); + } + + return $this->parentStaticReflectionParser; + } + + /** + * @return string + */ + public function getClassName() + { + return $this->className; + } + + /** + * @return string + */ + public function getNamespaceName() + { + return $this->namespace; + } + + /** + * {@inheritDoc} + */ + public function getReflectionClass() + { + return new StaticReflectionClass($this); + } + + /** + * {@inheritDoc} + */ + public function getReflectionMethod($methodName) + { + return new StaticReflectionMethod($this, $methodName); + } + + /** + * {@inheritDoc} + */ + public function getReflectionProperty($propertyName) + { + return new StaticReflectionProperty($this, $propertyName); + } + + /** + * Gets the use statements from this file. + * + * @return string[] + */ + public function getUseStatements() + { + $this->parse(); + + return $this->useStatements; + } + + /** + * Gets the doc comment. + * + * @param string $type The type: 'class', 'property' or 'method'. + * @param string $name The name of the property or method, not needed for 'class'. + * + * @return string The doc comment, empty string if none. + */ + public function getDocComment($type = 'class', $name = '') + { + $this->parse(); + + return $name ? $this->docComment[$type][$name] : $this->docComment[$type]; + } + + /** + * Gets the PSR-0 parser for the declaring class. + * + * @param string $type The type: 'property' or 'method'. + * @param string $name The name of the property or method. + * + * @return StaticReflectionParser A static reflection parser for the declaring class. + * + * @throws ReflectionException + */ + public function getStaticReflectionParserForDeclaringClass($type, $name) + { + $this->parse(); + if (isset($this->docComment[$type][$name])) { + return $this; + } + if (! empty($this->parentClassName)) { + return $this->getParentStaticReflectionParser()->getStaticReflectionParserForDeclaringClass($type, $name); + } + throw new ReflectionException('Invalid ' . $type . ' "' . $name . '"'); + } +} diff --git a/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php new file mode 100644 index 0000000..b94fda3 --- /dev/null +++ b/vendor/doctrine/reflection/lib/Doctrine/Common/Reflection/StaticReflectionProperty.php @@ -0,0 +1,160 @@ +staticReflectionParser = $staticReflectionParser; + $this->propertyName = $propertyName; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return $this->propertyName; + } + + /** + * @return StaticReflectionParser + */ + protected function getStaticReflectionParser() + { + return $this->staticReflectionParser->getStaticReflectionParserForDeclaringClass('property', $this->propertyName); + } + + /** + * {@inheritDoc} + */ + public function getDeclaringClass() + { + return $this->getStaticReflectionParser()->getReflectionClass(); + } + + /** + * {@inheritDoc} + */ + public function getDocComment() + { + return $this->getStaticReflectionParser()->getDocComment('property', $this->propertyName); + } + + /** + * @return string[] + */ + public function getUseStatements() + { + return $this->getStaticReflectionParser()->getUseStatements(); + } + + /** + * {@inheritDoc} + */ + public static function export($class, $name, $return = false) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getModifiers() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function getValue($object = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isDefault() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPrivate() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isProtected() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isPublic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function isStatic() + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setAccessible($accessible) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function setValue($object, $value = null) + { + throw new ReflectionException('Method not implemented'); + } + + /** + * {@inheritDoc} + */ + public function __toString() + { + throw new ReflectionException('Method not implemented'); + } +} diff --git a/vendor/doctrine/reflection/phpstan.neon b/vendor/doctrine/reflection/phpstan.neon new file mode 100644 index 0000000..4c8102b --- /dev/null +++ b/vendor/doctrine/reflection/phpstan.neon @@ -0,0 +1,6 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + +parameters: + ignoreErrors: + - '#Doctrine\\Common\\Reflection\\StaticReflection[a-zA-Z0-9_]+::__construct\(\) does not call parent constructor from Reflection[a-zA-Z0-9_]+#' diff --git a/vendor/gedmo/doctrine-extensions/.gitattributes b/vendor/gedmo/doctrine-extensions/.gitattributes new file mode 100644 index 0000000..18e14aa --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/.gitattributes @@ -0,0 +1 @@ +/tests export-ignore diff --git a/vendor/gedmo/doctrine-extensions/.gitignore b/vendor/gedmo/doctrine-extensions/.gitignore new file mode 100644 index 0000000..26ae9ed --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/.gitignore @@ -0,0 +1,8 @@ +tests/phpunit.xml +tests/temp/*.php +tests/temp/*.log +/vendor +/bin +/composer.lock +/composer.phar +.idea diff --git a/vendor/gedmo/doctrine-extensions/.travis.yml b/vendor/gedmo/doctrine-extensions/.travis.yml new file mode 100644 index 0000000..8ad9d2b --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/.travis.yml @@ -0,0 +1,48 @@ +language: php + +sudo: false + +matrix: + include: + - php: 5.4 + env: phpunit_exclude_groups=datetimeinterface dependencies=lowest + - php: 5.5 + - php: 5.6 + - php: 7.0 + - php: 7.1 + - php: 7.1 + env: dependencies=lowest + - php: 7.2 + env: dependencies=lowest + +cache: + directories: + - $HOME/.composer/cache + +services: mongodb + +before_install: + - if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo 'extension=mongo.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi + - if [[ "$TRAVIS_PHP_VERSION" != 5.* ]]; then echo 'extension=mongodb.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi + - if [[ "$TRAVIS_PHP_VERSION" != 5.* ]]; then cp composer7.json composer.json; fi + +install: + - | + if [[ "$dependencies" = "lowest" ]]; then + composer update --prefer-lowest --prefer-stable -n + else + composer install --prefer-dist + fi + +script: + - | + if [[ ! $phpunit_exclude_groups ]]; then + bin/phpunit -c tests/ + else + bin/phpunit -c tests/ --exclude-group $phpunit_exclude_groups + fi + +notifications: + email: + - gediminas.morkevicius@gmail.com + - developers@atlantic18.com diff --git a/vendor/gedmo/doctrine-extensions/LICENSE b/vendor/gedmo/doctrine-extensions/LICENSE new file mode 100644 index 0000000..f526026 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2011-2015 Gediminas Morkevičius + +The MIT license, reference http://www.opensource.org/licenses/mit-license.php + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/gedmo/doctrine-extensions/README.md b/vendor/gedmo/doctrine-extensions/README.md new file mode 100644 index 0000000..efe1c50 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/README.md @@ -0,0 +1,136 @@ +# Doctrine2 behavioral extensions + +[![Build Status](https://secure.travis-ci.org/Atlantic18/DoctrineExtensions.png)](http://travis-ci.org/Atlantic18/DoctrineExtensions) +[![Latest Stable Version](https://poser.pugx.org/gedmo/doctrine-extensions/version)](https://packagist.org/packages/gedmo/doctrine-extensions) + +**Note:** extensions might not evolve more after **2.4.x** it will remain stable and backward compatible. Unless +new interested maintainers will take over the development and continue with **3.x** versions onward. + +**Note:** Extensions **2.4.x** are compatible with ORM and doctrine common library versions from **2.2.x** to **2.5.x**. +ORM 2.5.x versions require **PHP 5.4** or higher. + +**Note:** Extensions **2.3.x** are compatible with ORM and doctrine common library versions from **2.2.x** to **2.4.x** +**Note:** If you are setting up entity manager without a framework, see the [example](/example/em.php) to prevent issues like #1310 + +### Latest updates + +**2016-01-27** + +- Nested tree now allows root field as association. + +**2015-05-01** + +- Reverted back [1272](https://github.com/Atlantic18/DoctrineExtensions/pull/1272) and see [1263](https://github.com/Atlantic18/DoctrineExtensions/issues/1263). Use [naming strategy](http://stackoverflow.com/questions/12702657/how-to-configure-naming-strategy-in-doctrine-2) for your use cases. +- Fixed bug for sortable [1279](https://github.com/Atlantic18/DoctrineExtensions/pull/1279) + +**2015-03-26** + +Support for ORM and Common library **2.5.0**. A minor version bump, because of trait column changes. + +**2015-01-28** + +Fixed the issue for all mappings, which caused related class mapping failures, when a relation or class name +was in the same namespace, but extensions required it to be mapped as full classname. + +**2015-01-21** + +Fixed memory leak issue with entity or document wrappers for convenient metadata retrieval. + +### Summary and features + +This package contains extensions for Doctrine2 that hook into the facilities of Doctrine and +offer new functionality or tools to use Doctrine2 more efficiently. This package contains mostly +used behaviors which can be easily attached to your event system of Doctrine2 and handle the +records being flushed in the behavioral way. List of extensions: + +- [**Tree**](/doc/tree.md) - this extension automates the tree handling process and adds some tree specific functions on repository. +(**closure**, **nestedset** or **materialized path**) +- [**Translatable**](/doc/translatable.md) - gives you a very handy solution for translating records into different languages. Easy to setup, easier to use. +- [**Sluggable**](/doc/sluggable.md) - urlizes your specified fields into single unique slug +- [**Timestampable**](/doc/timestampable.md) - updates date fields on create, update and even property change. +- [**Blameable**](/doc/blameable.md) - updates string or reference fields on create, update and even property change with a string or object (e.g. user). +- [**Loggable**](/doc/loggable.md) - helps tracking changes and history of objects, also supports version management. +- [**Sortable**](/doc/sortable.md) - makes any document or entity sortable +- [**Translator**](/doc/translatable.md) - explicit way to handle translations +- [**SoftDeleteable**](/doc/softdeleteable.md) - allows to implicitly remove records +- [**Uploadable**](/doc/uploadable.md) - provides file upload handling in entity fields +- [**References**](/doc/references.md) - supports linking Entities in Documents and vice versa +- [**ReferenceIntegrity**](/doc/reference_integrity.md) - constrains ODM MongoDB Document references +- [**IpTraceable**](/doc/ip_traceable.md) - inherited from Timestampable, sets IP address instead of timestamp + +Currently these extensions support **Yaml**, **Annotation** and **Xml** mapping. Additional mapping drivers +can be easily implemented using Mapping extension to handle the additional metadata mapping. + +**Note:** Please note, that xml mapping needs to be in a different namespace, the declared namespace for +Doctrine extensions is http://gediminasm.org/schemas/orm/doctrine-extensions-mapping +So root node now looks like this: + +**Note:** Use 2.1.x tag in order to use extensions based on Doctrine2.1.x versions. Currently +master branch is based on 2.2.x versions and may not work with 2.1.x + +```xml + +... + +``` + +XML mapping xsd schemas are also versioned and can be used by version suffix: + +- Latest version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping** +- 2.2.x version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping-2-2** +- 2.1.x version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping-2-1** + +### ODM MongoDB support + +List of extensions which support ODM + +- Translatable +- Sluggable +- Timestampable +- Blameable +- Loggable +- Translator +- Tree (Materialized Path strategy for now) +- References +- ReferenceIntegrity + +All these extensions can be nested together and mapped in traditional ways - **annotations**, +**xml** or **yaml** + +### Running the tests: + +**pdo-sqlite** extension is necessary. +To setup and run tests follow these steps: + +- go to the root directory of extensions +- download composer: `wget https://getcomposer.org/composer.phar` +- install dev libraries: `php composer.phar install` +- run: `bin/phpunit -c tests` +- optional - run mongodb service if targeting mongo tests + +### Running the example: + +To setup and run example follow these steps: + +- go to the root directory of extensions +- download composer: `wget https://getcomposer.org/composer.phar` +- install dev libraries: `php composer.phar install` +- edit `example/em.php` and configure your database on top of the file +- run: `./example/bin/console` or `php example/bin/console` for console commands +- run: `./example/bin/console orm:schema-tool:create` to create schema +- run: `php example/run.php` to run example + +### Contributors: + +Thanks to [everyone participating](http://github.com/l3pp4rd/DoctrineExtensions/contributors) in +the development of these great Doctrine2 extensions! + +And especially ones who create and maintain new extensions: + +- Lukas Botsch [lbotsch](http://github.com/lbotsch) +- Gustavo Adrian [comfortablynumb](http://github.com/comfortablynumb) +- Boussekeyt Jules [gordonslondon](http://github.com/gordonslondon) +- Kudryashov Konstantin [everzet](http://github.com/everzet) +- David Buchmann [dbu](https://github.com/dbu) + diff --git a/vendor/gedmo/doctrine-extensions/composer.json b/vendor/gedmo/doctrine-extensions/composer.json new file mode 100644 index 0000000..f8db0bd --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/composer.json @@ -0,0 +1,70 @@ +{ + "name": "gedmo/doctrine-extensions", + "type": "library", + "description": "Doctrine2 behavioral extensions", + "keywords": [ + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "sluggable", + "loggable", + "translatable", + "tree", + "nestedset", + "sortable", + "timestampable", + "blameable", + "uploadable" + ], + "homepage": "http://gediminasm.org/", + "license": "MIT", + "authors": [ + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + }, + { + "name": "David Buchmann", + "email": "david@liip.ch" + } + ], + "support": { + "email": "gediminas.morkevicius@gmail.com", + "wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc" + }, + "require": { + "php": ">=5.3.2", + "behat/transliterator": "~1.2", + "doctrine/common": "~2.4" + }, + "require-dev": { + "doctrine/mongodb-odm": ">=1.0.2", + "doctrine/orm": ">=2.5.0", + "doctrine/common": ">=2.5.0", + "symfony/yaml": "~2.6|~3.0|~4.0", + "phpunit/phpunit": "^4.8.35|^5.7|^6.5" + }, + "conflict": { + "doctrine/annotations": "<1.2" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "autoload": { + "psr-4": { "Gedmo\\": "lib/Gedmo" } + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/composer7.json b/vendor/gedmo/doctrine-extensions/composer7.json new file mode 100644 index 0000000..0b1f0a9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/composer7.json @@ -0,0 +1,76 @@ +{ + "name": "gedmo/doctrine-extensions", + "type": "library", + "description": "Doctrine2 behavioral extensions", + "keywords": [ + "behaviors", + "doctrine2", + "extensions", + "gedmo", + "sluggable", + "loggable", + "translatable", + "tree", + "nestedset", + "sortable", + "timestampable", + "blameable", + "uploadable" + ], + "homepage": "http://gediminasm.org/", + "license": "MIT", + "authors": [ + { + "name": "Gediminas Morkevicius", + "email": "gediminas.morkevicius@gmail.com" + }, + { + "name": "Gustavo Falco", + "email": "comfortablynumb84@gmail.com" + }, + { + "name": "David Buchmann", + "email": "david@liip.ch" + } + ], + "support": { + "email": "gediminas.morkevicius@gmail.com", + "wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc" + }, + "require": { + "php": ">=5.4", + "behat/transliterator": "~1.2", + "doctrine/common": "~2.4" + }, + "provide": { + "ext-mongo": "1.6.12" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "~1.0.4", + "doctrine/mongodb-odm": ">=1.0.2", + "doctrine/orm": ">=2.5.0", + "doctrine/common": ">=2.5.0", + "symfony/yaml": "~2.6|~3.0|~4.0", + "phpunit/phpunit": "^5.7|^6.5" + }, + "conflict": { + "doctrine/annotations": "<1.2", + "doctrine/mongodb": "<1.3", + "sebastian/comparator": "<2.0" + }, + "suggest": { + "doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM", + "doctrine/orm": "to use the extensions with the ORM" + }, + "autoload": { + "psr-4": { "Gedmo\\": "lib/Gedmo" } + }, + "config": { + "bin-dir": "bin" + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/doc/annotations.md b/vendor/gedmo/doctrine-extensions/doc/annotations.md new file mode 100644 index 0000000..9516362 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/annotations.md @@ -0,0 +1,566 @@ +# Annotation reference + +Bellow you will find all annotation descriptions used in these extensions. +There will be introduction on usage with examples. For more detailed usage +on extensions, refer to their specific documentation. + +Content: + +- Best [practices](#em-setup) for setting up +- [Tree](#gedmo-tree) +- [Translatable](#gedmo-translatable) +- [Sluggable](#gedmo-sluggable) +- [Timestampable](#gedmo-timestampable) +- [Loggable](#gedmo-loggable) + +## Annotation mapping + +Starting from **doctrine2.1.x** versions you have to import all used annotations +by an **use** statement, see example bellow: + +``` php +namespace MyApp\Entity; + +use Gedmo\Mapping\Annotation as Gedmo; // this will be like an alias for Gedmo extensions annotations +use Doctrine\ORM\Mapping\Id; // includes single annotation +use Doctrine\ORM\Mapping as ORM; // alias for doctrine ORM annotations + +/** + * @ORM\Entity + * @Gedmo\TranslationEntity(class="something") + */ +class Article +{ + /** + * @Id + * @Gedmo\Slug(fields={"title"}, updatable=false, separator="_") + * @ORM\Column(length=32, unique=true) + */ + private $id; + + /** + * @Gedmo\Translatable + * @ORM\Column(length=64) + */ + private $title; + + /** + * @Gedmo\Timestampable(on="create") + * @ORM\Column(type="datetime") + */ + private $created; +} +``` + +**Note:** this mapping applies only if you use **doctrine-common** library at version **2.1.x** or higher, +extension library still supports old mapping styles if you manually set the mapping drivers + + + +## Best practices for setting up with annotations + +New annotation reader does not depend on any namespaces, for that reason you can use +single reader instance for whole project. The example bellow shows how to setup the +mapping and listeners: + +**Note:** using this repository you can test and check the [example demo configuration](https://github.com/Atlantic18/DoctrineExtensions/blob/master/example/em.php) + +``` php +addDriver($annotationDriver, 'Entity'); + +// general ORM configuration +$config = new Doctrine\ORM\Configuration; +$config->setProxyDir(sys_get_temp_dir()); +$config->setProxyNamespace('Proxy'); +$config->setAutoGenerateProxyClasses(false); // this can be based on production config. +// register metadata driver +$config->setMetadataDriverImpl($driverChain); +// use our already initialized cache driver +$config->setMetadataCacheImpl($cache); +$config->setQueryCacheImpl($cache); + +// create event manager and hook preferred extension listeners +$evm = new Doctrine\Common\EventManager(); +// gedmo extension listeners, remove which are not used + +// sluggable +$sluggableListener = new Gedmo\Sluggable\SluggableListener; +// you should set the used annotation reader to listener, to avoid creating new one for mapping drivers +$sluggableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($sluggableListener); + +// tree +$treeListener = new Gedmo\Tree\TreeListener; +$treeListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($treeListener); + +// loggable, not used in example +$loggableListener = new Gedmo\Loggable\LoggableListener; +$loggableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($loggableListener); + +// timestampable +$timestampableListener = new Gedmo\Timestampable\TimestampableListener; +$timestampableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($timestampableListener); + +// translatable +$translatableListener = new Gedmo\Translatable\TranslatableListener; +// current translation locale should be set from session or hook later into the listener +// most important, before entity manager is flushed +$translatableListener->setTranslatableLocale('en'); +$translatableListener->setDefaultLocale('en'); +$translatableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($translatableListener); + +// sortable, not used in example +$sortableListener = new Gedmo\Sortable\SortableListener; +$sortableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($sortableListener); + +// mysql set names UTF-8 if required +$evm->addEventSubscriber(new Doctrine\DBAL\Event\Listeners\MysqlSessionInit()); +// DBAL connection +$connection = array( + 'driver' => 'pdo_mysql', + 'host' => '127.0.0.1', + 'dbname' => 'test', + 'user' => 'root', + 'password' => '' +); +// Finally, create entity manager +$em = Doctrine\ORM\EntityManager::create($connection, $config, $evm); +``` + +**Note:** that symfony2 StofDoctrineExtensionsBundle does it automatically this +way you will maintain a single instance of annotation reader. It relates only +to doctrine-common-2.1.x branch and newer. + + + +## Tree annotations + +Tree can use different adapters. Currently **Tree** extension supports **NestedSet** +and **Closure** strategies which has a difference for annotations used. Note, that +tree will automatically map indexes which are considered necessary for best performance. + +### @Gedmo\Mapping\Annotation\Tree (required for all tree strategies) + +**class** annotation + +Is the main identificator of tree used for domain object which should **act as Tree**. + +**options:** + +- **type** - (string) _optional_ default: **nested** + +example: + +``` php + + +## Translatable annotations + +Translatable additionally can have unmapped property, which would override the +locale used by listener. + +### @Gedmo\Mapping\Annotation\TranslationEntity (optional) + +**class** annotation + +This class annotation can force translatable to use **personal Entity** to store +translations. In large tables this can be very handy. + +**options:** + +- **class** - (string) _required_ + +example: + +``` php + + +## Sluggable annotations + +Sluggable ensures unique slugs and correct length of the slug. It also uses utf8 to ascii +table map to create correct ascii slugs. + +### @Gedmo\Mapping\Annotation\Slug (required) + +**property** annotation + +It will use this **string** property to store the generated slug. + +**options:** + +- **fields** - (array) _required_, must at least contain one field +- **updatable** - (boolean) _optional_ default: **true** +- **separator** - (string) _optional_ default: **-** +- **unique** - (boolean) _optional_ default: **true** +- **style** - (string) _optional_ default: **default** lowercase, can be **camel** also +- **handlers** - (array) _optional_ default: empty array, refer to the documentation below, possible elements: **Gedmo\Mapping\Annotation\SlugHandler** + +### Slug handlers: + +- Gedmo\Sluggable\Handler\TreeSlugHandler - transforms a tree slug into path based, example "food/fruits/apricots/king-apricots" +- Gedmo\Sluggable\Handler\RelativeSlugHandler - takes a relation slug and prefixes the slug, example "singers/michael-jackson" +in order to synchronize updates regarding the relation changes, you will need to hood **InversedRelativeSlugHandler** to the relation mentioned. +- Gedmo\Sluggable\Handler\InversedRelativeSlugHandler - updates prefixed slug for an inversed relation which is mapped by **RelativeSlugHandler** + +examples: + +``` php + + +## Timestampable annotations + +Timestampable will update date fields on create, update or property change. If you set/force +date manually it will not update it. + +### @Gedmo\Mapping\Annotation\Timestampable (required) + +**property** annotation + +Marks a **date, datetime or time** field as timestampable. + +**options:** + +- **on** - (string) _optional_ default: **update**, other choice is **create** or **change** +- **field** - (string) _conditional_ required only if it triggers on **change**, name of the **field** +or if it is a relation **property.field** +- **value** - (mixed) _conditional_ required only if it triggers on **change**, value of property +which would trigger an update. + +example: + +``` php + + +## Loggable annotations + +Loggable is used to log all actions made on annotated object class, it logs insert, update +and remove actions for a username which currently is logged in for instance. +Further more, it stores all **Versioned** property changes in the log which allows +a version management implementation for this object. + +### @Gedmo\Mapping\Annotation\Loggable (required) + +**class** annotation + +This class annotation marks object as being loggable and logs all actions being done to +this class records. + +**options:** + +- **logEntryClass** - (string) _optional_ personal log storage class + +example: + +``` php + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## Blameable Entity example: + +### Blameable annotations: +- **@Gedmo\Mapping\Annotation\Blameable** this annotation tells that this column is blameable +by default it updates this column on update. If column is not a string field or an association +it will trigger an exception. + +Available configuration options: + +- **on** - is main option and can be **create, update, change** this tells when it +should be updated +- **field** - only valid if **on="change"** is specified, tracks property or a list of properties for changes +- **value** - only valid if **on="change"** is specified and the tracked field is a single field (not an array), if the tracked field has this **value** +then it updates the blame + +**Note:** that Blameable interface is not necessary, except in cases there +you need to identify entity as being Blameable. The metadata is loaded only once then +cache is activated + +Column is a string field: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setBody($body) + { + $this->body = $body; + } + + public function getBody() + { + return $this->body; + } + + public function getCreatedBy() + { + return $this->createdBy; + } + + public function getUpdatedBy() + { + return $this->updatedBy; + } + + public function getContentChangedBy() + { + return $this->contentChangedBy; + } +} +``` + +Column is an association: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setBody($body) + { + $this->body = $body; + } + + public function getBody() + { + return $this->body; + } + + public function getCreatedBy() + { + return $this->createdBy; + } + + public function getUpdatedBy() + { + return $this->updatedBy; + } + + public function getContentChangedBy() + { + return $this->contentChangedBy; + } +} +``` + + + +## Blameable Document example: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getCreatedBy() + { + return $this->createdBy; + } + + public function getUpdatedBy() + { + return $this->updatedBy; + } +} +``` + +Now on update and creation these annotated fields will be automatically updated + + + +## Yaml mapping example: + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + createdBy: + type: string + gedmo: + blameable: + on: create + updatedBy: + type: string + gedmo: + blameable: + on: update +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Advanced examples: + +### Using dependency of property changes + +Add another entity which would represent Article Type: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } +} +``` + +Now update the Article Entity to reflect publishedBy on Type change: + +``` php +type = $type; + } + + public function getId() + { + return $this->id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getCreatedBy() + { + return $this->createdBy; + } + + public function getUpdatedBy() + { + return $this->updatedBy; + } + + public function getPublishedBy() + { + return $this->publishedBy; + } +} +``` + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + createdBy: + type: string + gedmo: + blameable: + on: create + updatedBy: + type: string + gedmo: + blameable: + on: update + publishedBy: + type: string + gedmo: + blameable: + on: change + field: type.title + value: Published + manyToOne: + type: + targetEntity: Entity\Type + inversedBy: articles +``` + +Now few operations to get it all done: + +``` php +setTitle('My Article'); + +$em->persist($article); +$em->flush(); +// article: $createdBy, $updatedBy were set + +$type = new Type; +$type->setTitle('Published'); + +$article = $em->getRepository('Entity\Article')->findByTitle('My Article'); +$article->setType($type); + +$em->persist($article); +$em->persist($type); +$em->flush(); +// article: $publishedBy, $updatedBy were set + +$article->getPublishedBy(); // the user that published this article +``` + +Easy like that, any suggestions on improvements are very welcome + + + + +## Traits + +You can use blameable traits for quick **createdBy** **updatedBy** string definitions +when using annotation mapping. +There is also a trait without annotations for easy integration purposes. + +**Note:** this feature is only available since php **5.4.0**. And you are not required +to use the Traits provided by extensions. + +``` php + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## IpTraceable Entity example: + +### IpTraceable annotations: +- **@Gedmo\Mapping\Annotation\IpTraceable** this annotation tells that this column is ipTraceable +by default it updates this column on update. If column is not a string field it will trigger an exception. + +Available configuration options: + +- **on** - is main option and can be **create, update, change** this tells when it +should be updated +- **field** - only valid if **on="change"** is specified, tracks property or a list of properties for changes +- **value** - only valid if **on="change"** is specified and the tracked field is a single field (not an array), if the tracked field has this **value** +then it updates the trace + +**Note:** that IpTraceable interface is not necessary, except in cases there +you need to identify entity as being IpTraceable. The metadata is loaded only once then +cache is activated + +Column is a string field: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setBody($body) + { + $this->body = $body; + } + + public function getBody() + { + return $this->body; + } + + public function getCreatedFromIp() + { + return $this->createdFromIp; + } + + public function getUpdatedFromIp() + { + return $this->updatedFromIp; + } + + public function getContentChangedFromIp() + { + return $this->contentChangedFromIp; + } +} +``` + + + + +## IpTraceable Document example: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getCreatedFromIp() + { + return $this->createdFromIp; + } + + public function getUpdatedFromIp() + { + return $this->updatedFromIp; + } +} +``` + +Now on update and creation these annotated fields will be automatically updated + + + +## Yaml mapping example: + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + createdFromIp: + type: string + length: 45 + nullable: true + gedmo: + ipTraceable: + on: create + updatedFromIp: + type: string + length: 45 + nullable: true + gedmo: + ipTraceable: + on: update +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Advanced examples: + +### Using dependency of property changes + +Add another entity which would represent Article Type: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } +} +``` + +Now update the Article Entity to reflect publishedFromIp on Type change: + +``` php +type = $type; + } + + public function getId() + { + return $this->id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getCreatedFromIp() + { + return $this->createdFromIp; + } + + public function getUpdatedFromIp() + { + return $this->updatedFromIp; + } + + public function getPublishedFromIp() + { + return $this->publishedFromIp; + } +} +``` + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + createdFromIp: + type: string + length: 45 + nullable: true + gedmo: + ipTraceable: + on: create + updatedFromIp: + type: string + length: 45 + nullable: true + gedmo: + ipTraceable: + on: update + publishedFromIp: + type: string + length: 45 + nullable: true + gedmo: + ipTraceable: + on: change + field: type.title + value: Published + manyToOne: + type: + targetEntity: Entity\Type + inversedBy: articles +``` + +Now few operations to get it all done: + +``` php +setTitle('My Article'); + +$em->persist($article); +$em->flush(); +// article: $createdFromIp, $updatedFromIp were set + +$type = new Type; +$type->setTitle('Published'); + +$article = $em->getRepository('Entity\Article')->findByTitle('My Article'); +$article->setType($type); + +$em->persist($article); +$em->persist($type); +$em->flush(); +// article: $publishedFromIp, $updatedFromIp were set + +$article->getPublishedFromIp(); // the IP that published this article +``` + +Easy like that, any suggestions on improvements are very welcome + + + + +## Traits + +You can use IpTraceable traits for quick **createdFromIp** **updatedFromIp** string definitions +when using annotation mapping. +There is also a trait without annotations for easy integration purposes. + +**Note:** this feature is only available since php **5.4.0**. And you are not required +to use the Traits provided by extensions. + +``` php +ipTraceableListener = $ipTraceableListener; + $this->request = $request; + } + + /** + * Set the username from the security context by listening on core.request + * + * @param GetResponseEvent $event + */ + public function onKernelRequest(GetResponseEvent $event) + { + if (null === $this->request) { + return; + } + + // If you use a cache like Varnish, you may want to set a proxy to Request::getClientIp() method + // $this->request->setTrustedProxies(array('127.0.0.1')); + + // $ip = $_SERVER['REMOTE_ADDR']; + $ip = $this->request->getClientIp(); + + if (null !== $ip) { + $this->ipTraceableListener->setIpValue($ip); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => 'onKernelRequest', + ); + } +} + +``` + +### Configuration for services.xml + +``` xml + + + + + + Acme\DemoBundle\EventListener\IpTraceListener + + + + + ... + + + + + + + + + + + + + + + + + +``` diff --git a/vendor/gedmo/doctrine-extensions/doc/loggable.md b/vendor/gedmo/doctrine-extensions/doc/loggable.md new file mode 100644 index 0000000..9b6d5db --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/loggable.md @@ -0,0 +1,266 @@ +# Loggable behavioral extension for Doctrine2 + +**Loggable** behavior tracks your record changes and is able to +manage versions. + +Features: + +- Automatic storage of log entries in database +- ORM and ODM support using same listener +- Can be nested with other behaviors +- Objects can be reverted to previous versions +- Annotation, Yaml and Xml mapping support for extensions + +Update **2011-04-04** + +- Made single listener, one instance can be used for any object manager +and any number of them + +**Portability:** + +- **Loggable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) +ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions + +This article will cover the basic installation and functionality of **Loggable** +behavior + +Content: + +- [Including](#including-extension) the extension +- Entity [example](#entity-mapping) +- Document [example](#document-mapping) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Basic usage [examples](#basic-examples) + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + +### Loggable annotations: + +- **@Gedmo\Mapping\Annotation\Loggable(logEntryClass="my\class")** this class annotation +will store logs to optionally specified **logEntryClass**. You will still need to specify versioned fields with the following annotation. +- **@Gedmo\Mapping\Annotation\Versioned** tracks annotated property for changes + +### Loggable username: + +In order to set the username, when adding the loggable listener you need to set it this way: + +``` php +$loggableListener = new Gedmo\Loggable\LoggableListener; +$loggableListener->setAnnotationReader($cachedAnnotationReader); +$loggableListener->setUsername('admin'); +$evm->addEventSubscriber($loggableListener); +``` + + +## Loggable Entity example: + +**Note:** that Loggable interface is not necessary, except in cases there +you need to identify entity as being Loggable. The metadata is loaded only once when +cache is active + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } +} +``` + + + +## Loggable Document example: + +``` php +title; + } + + public function getId() + { + return $this->id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } +} +``` + + + +## Yaml mapping example + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + gedmo: + loggable: +# using specific personal LogEntryClass class: + logEntryClass: My\LogEntry +# without specifying the LogEntryClass class: +# loggable: true + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + gedmo: + - versioned + content: + type: text +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Basic usage examples: + +``` php +setTitle('my title'); +$em->persist($article); +$em->flush(); +``` + +This inserted an article and inserted the logEntry for it, which contains +all new changeset. In case if there is **OneToOne or ManyToOne** relation, +it will store only identifier of that object to avoid storing proxies + +Now lets update our article: + +``` php +find('Entity\Article', 1 /*article id*/); +$article->setTitle('my new title'); +$em->persist($article); +$em->flush(); +``` + +This updated an article and inserted the logEntry for update action with new changeset +Now lets revert it to previous version: + +``` php +getRepository('Gedmo\Loggable\Entity\LogEntry'); // we use default log entry class +$article = $em->find('Entity\Article', 1 /*article id*/); +$logs = $repo->getLogEntries($article); +/* $logs contains 2 logEntries */ +// lets revert to first version +$repo->revert($article, 1/*version*/); +// notice article is not persisted yet, you need to persist and flush it +echo $article->getTitle(); // prints "my title" +$em->persist($article); +$em->flush(); +// if article had changed relation, it would be reverted also. +``` + +Easy like that, any suggestions on improvements are very welcome diff --git a/vendor/gedmo/doctrine-extensions/doc/mapping.md b/vendor/gedmo/doctrine-extensions/doc/mapping.md new file mode 100644 index 0000000..94e7fd7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/mapping.md @@ -0,0 +1,491 @@ +# Mapping extension for Doctrine2 + +**Mapping** extension makes it easy to map additional metadata for event listeners. +It supports **Yaml**, **Xml** and **Annotation** drivers which will be chosen depending on +currently used mapping driver for your domain objects. **Mapping** extension also +provides abstraction layer of **EventArgs** to make it possible to use single listener +for different object managers like **ODM** and **ORM**. + +Features: + +- Mapping drivers for annotation and yaml +- Conventional extension points for metadata extraction and object manager abstraction + +- Public [Mapping repository](http://github.com/Atlantic18/DoctrineExtensions "Mapping extension on Github") is available on github +- Last update date: **2012-01-02** + +This article will cover the basic installation and usage of **Mapping** extension + +Content: + +- [Including](#including-extension) the extension +- [Creating](#create-extension) an extension +- Defining [annotations](#annotations) +- Creating [listener](#create-listener) +- Attaching our [listener](#attach-listener) to the event manager +- [Entity](#entity-mapping) with some fields to encode +- Adapting listener to support [different](#different-managers) object managers +- [Customizing](#event-adapter-customize) event adapter for specific functions + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## Tutorial on creation of mapped extension + +First, lets assume we will use **Extension** namespace for our additional +extension library. You should create an **Extension** directory in your library +or vendor directory. After some changes your project might look like: + +``` +project + ... + bootstrap.php + vendor + Extension + ... +... +``` + +Now you can use any namespace autoloader class and register this namespace. We +will use Doctrine\Common\ClassLoader for instance: + +``` php +register(); +``` + +Now lets create some files which are necessary for our extension: + +``` +project + ... + bootstrap.php + vendor + Extension + Encoder + Mapping + Driver + Annotation.php + Annotations.php + EncoderListener.php +... +``` + +**Note:** that extension will look for mapping in **ExtensionNamespace/Mapping** +directory. And **Driver** directory should be named as Driver. These are the conventions +of **Mapping** extension. + +That is all we will need for now. As you may noticed we will create an encoding +listener which could encode your fields by specified annotations. In real life it +may not be useful since object will not know how to match the value. + + + +## Now lets define available annotations and setup drivers + +Edit **Annotations.php** file: + +``` php +getReflectionClass(); + // check only property annotations + foreach ($class->getProperties() as $property) { + // skip inherited properties + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + // now lets check if property has our annotation + if ($encode = $reader->getPropertyAnnotation($property, 'Extension\Encoder\Mapping\Encode')) { + $field = $property->getName(); + // check if field is mapped + if (!$meta->hasField($field)) { + throw new \Exception("Field is not mapped as object property"); + } + // allow encoding only strings + if (!in_array($encode->type, array('sha1', 'md5'))) { + throw new \Exception("Invalid encoding type supplied"); + } + // validate encoding type + $mapping = $meta->getFieldMapping($field); + if ($mapping['type'] != 'string') { + throw new \Exception("Only strings can be encoded"); + } + // store the metadata + $config['encode'][$field] = array( + 'type' => $encode->type, + 'secret' => $encode->secret + ); + } + } + } +} +``` + + + +## Finally, lets create the listener + +**Note:** this version of listener will support only ORM Entities + +``` php +loadMetadataForObjectClass( + $args->getEntityManager(), + $args->getClassMetadata() + ); + } + + public function onFlush(EventArgs $args) + { + $em = $args->getEntityManager(); + $uow = $em->getUnitOfWork(); + + // check all pending updates + foreach ($uow->getScheduledEntityUpdates() as $object) { + $meta = $em->getClassMetadata(get_class($object)); + // if it has our metadata lets encode the properties + if ($config = $this->getConfiguration($em, $meta->name)) { + $this->encode($em, $object, $config); + } + } + // check all pending insertions + foreach ($uow->getScheduledEntityInsertions() as $object) { + $meta = $em->getClassMetadata(get_class($object)); + // if it has our metadata lets encode the properties + if ($config = $this->getConfiguration($em, $meta->name)) { + $this->encode($em, $object, $config); + } + // recalculate changeset + $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $object); + } + } + + protected function getNamespace() + { + // mapper must know the namespace of extension + return __NAMESPACE__; + } + + private function encode($em, $object, $config) + { + $meta = $em->getClassMetadata(get_class($object)); + foreach ($config['encode'] as $field => $options) { + $value = $meta->getReflectionProperty($field)->getValue($object); + $method = $options['type']; + $encoded = $method($options['secret'].$value); + $meta->getReflectionProperty($field)->setValue($object, $encoded); + } + // recalculate changeset + $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $object); + } +} +``` + +Our **Encoder** extension is ready, now if we want to test it, we need +to attach our **EncoderListener** to the EventManager and create an entity +with some fields to encode. + + + +### Attaching the EncoderListener + +``` php +addEventSubscriber($encoderListener); +// now this event manager should be passed to entity manager constructor +``` + + + +### Create an entity with some fields to encode + +``` php +name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setPassword($password) + { + $this->password = $password; + } + + public function getPassword() + { + return $this->password; + } +} +``` + +If you will try to create a new **User** you will get encoded fields in database. + + + +## Adapting listener to support other object managers + +Now the event adapter comes into play, lets slightly modify our listener: + +``` php +getEventAdapter($args); + // this will check for our metadata + $this->loadMetadataForObjectClass( + $ea->getObjectManager(), + $args->getClassMetadata() + ); + } + + public function onFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + // check all pending updates + foreach ($ea->getScheduledObjectUpdates($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + // if it has our metadata lets encode the properties + if ($config = $this->getConfiguration($om, $meta->name)) { + $this->encode($ea, $object, $config); + } + } + // check all pending insertions + foreach ($ea->getScheduledObjectInsertions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + // if it has our metadata lets encode the properties + if ($config = $this->getConfiguration($om, $meta->name)) { + $this->encode($ea, $object, $config); + } + // recalculate changeset + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + } + } + + protected function getNamespace() + { + // mapper must know the namespace of extension + return __NAMESPACE__; + } + + private function encode(EventAdapterInterface $ea, $object, $config) + { + $om = $ea->getObjectManager(); + $meta = $om->getClassMetadata(get_class($object)); + $uow = $om->getUnitOfWork(); + foreach ($config['encode'] as $field => $options) { + $value = $meta->getReflectionProperty($field)->getValue($object); + $method = $options['type']; + $encoded = $method($options['secret'].$value); + $meta->getReflectionProperty($field)->setValue($object, $encoded); + } + // recalculate changeset + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + } +} +``` + +**Note:** event adapter uses **EventArgs** to recognize with which manager +we are dealing with. It also uses event arguments to retrieve manager and transforms +the method call in its way. You can extend the event adapter in order to add some +specific methods for each manager. + +That's it, now it will work on ORM and ODM object managers. + + + +## Customizing event adapter for specific functions + +In most cases event listener will need specific functionality which will differ +for every object manager. For instance, a query to load users will differ. The +example bellow will illustrate how to handle such situations. You will need to +extend default ORM and ODM event adapters to implement specific functions which +will be available through the event adapter. First we will need to follow the +mapping convention to use those extension points. + +### Extending default event adapters + +Update your directory structure: + +``` +project + ... + bootstrap.php + vendor + Extension + Encoder + Mapping + Driver + Annotation.php + Event + Adapter + ORM.php + ODM.php + Annotations.php + EncoderListener.php +... +``` + +Now **Mapping** extension will automatically create event adapter instances +from the extended ones. + +Create extended ORM event adapter: + +``` php + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## ReferenceIntegrity Document example: + +``` php + + +## Yaml mapping example: + +Yaml mapped Article: **/mapping/yaml/Documents.Article.dcm.yml** + +``` +--- +Document\Type: + type: document + collection: types + fields: + id: + id: true + title: + type: string + article: + reference: true + type: one + mappedBy: type + targetDocument: Document\Article + gedmo: + referenceIntegrity: nullify # or pull or restrict + +``` + +It is necessary to have the 'mappedBy' option set, to be able to access the referenced documents. + + + +## Usage examples: + +Few operations to see 'nullify' in action: + +``` php +setTitle('My Article'); + +$type = new Type; +$type->setTitle('Published'); + +$article->setType($type); + +$em->persist($article); +$em->persist($type); +$em->flush(); + +$type = $em->getRepository('Document\Type')->findByTitle('Published'); +$em->remove($type); +$em->flush(); + +$article = $em->getRepository('Document\Article')->findByTitle('My Article'); +$article->getType(); // won't be referenced to Type anymore +``` + +Few operations to see 'pull' in action: + +``` php +setTitle('My Article'); + +$type1 = new Type; +$type1->setTitle('Published'); + +$type2 = new Type; +$type2->setTitle('Info'); + +$article->addType($type1); +$article->addType($type2); + +$em->persist($article); +$em->persist($type1); +$em->persist($type2); +$em->flush(); + +$type2 = $em->getRepository('Document\Type')->findByTitle('Info'); +$em->remove($type2); +$em->flush(); + +$article = $em->getRepository('Document\Article')->findByTitle('My Article'); +$article->getTypes(); // will only contain $type1 ('Published') +``` + +When 'ReferenceIntegrity' is set to 'restrict' a `ReferenceIntegrityStrictException` will be thrown, only when there +is a referenced document. + +Easy like that, any suggestions on improvements are very welcome diff --git a/vendor/gedmo/doctrine-extensions/doc/references.md b/vendor/gedmo/doctrine-extensions/doc/references.md new file mode 100644 index 0000000..9bdecd3 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/references.md @@ -0,0 +1,219 @@ +# Cross Object Mapper References behavior extension for Doctrine 2 + +Create documents and entities that contain references to each other. + +## Options + +The following options are possible on reference one and many associations: + +**Owning Side** + +- **type** - The type of association. +- **class** - The associated class name. +- **inversedBy** - The property name for the inverse side of this association. +- **identifier** - The property name to store the associated object id in. + +**Inverse Side** + +- **type** - The type of association. +- **class** - The associated class name. +- **mappedBy** - The property name for the owning side of this association. + +## Annotations + +**@Gedmo\ReferenceMany** + +``` php +id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getStockItems() + { + return $this->stockItems; + } + + public function setStockItems(Collection $stockItems) + { + $this->stockItems = $stockItems; + } +} +``` + +The `StockItem` has a reference to the `Product` as well. + +``` php +id; + } + + public function setId($id) + { + $this->id = $id; + } + + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getSku() + { + return $this->sku; + } + + public function setSku($sku) + { + $this->sku = $sku; + } + + public function getQuantity() + { + return $this->quantity; + } + + public function setQuantity($quantity) + { + $this->quantity = $quantity; + } + + public function setProduct(Product $product) + { + $this->product = $product; + } + + public function getProduct() + { + return $this->product; + } + + public function setProductId($productId) + { + $this->productId = $productId; + } + + public function getProductId() + { + return $this->productId; + } +} +``` diff --git a/vendor/gedmo/doctrine-extensions/doc/sluggable.md b/vendor/gedmo/doctrine-extensions/doc/sluggable.md new file mode 100644 index 0000000..d0fcd6d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/sluggable.md @@ -0,0 +1,810 @@ +# Sluggable behavior extension for Doctrine 2 + +**Sluggable** behavior will build the slug of predefined fields on a given field +which should store the slug + +Features: + +- Automatic predefined field transformation into slug +- ORM and ODM support using same listener +- Slugs can be unique and styled, even with prefixes and/or suffixes +- Can be nested with other behaviors +- Annotation, Yaml and Xml mapping support for extensions +- Multiple slugs, different slugs can link to same fields + +Update **2013-10-26** + +- Datetime support with default dateFormat Y-m-d-H:i + +Update **2013-08-23** + +- Added 'prefix' and 'suffix' configuration parameter #812 + +Update **2013-08-19** + +- allow empty slug #807 regenerate slug only if set to `null` + +Update **2013-03-10** + +- Added 'unique_base' configuration parameter to the Sluggable behaviour + +Update **2012-11-30** + +- Recreated slug handlers, as they are used by many people + +Update **2012-02-26** + +- Remove slug handlers were removed because of complications it brought together + + +Update **2011-09-11** + +- Refactored sluggable for doctrine2.2 by specifying slug fields directly in slug annotation +- Slug handler functionality, possibility to create custom ones or use built-in +tree path handler or linked slug through single valued association +- Updated documentation mapping examples for 2.1.x version or higher + +Update **2011-04-04** + +- Made single listener, one instance can be used for any object manager and any number of them + +Update **2010-12-23** + +- Full support for unique index on slug field, +no more exceptions during concurrent flushes. + +**Note:** + +- There is a reported [issue](https://github.com/Atlantic18/DoctrineExtensions/issues/254) that sluggable transliterator +does not work on OSX 10.6 its ok starting again from 10.7 version. To overcome the problem +you can use your [custom transliterator](#transliterator) +- Public [Sluggable repository](http://github.com/Atlantic18/DoctrineExtensions "Sluggable extension on Github") is available on github +- Last update date: **2012-02-26** +- For usage together with **SoftDeleteable** in order to take into account softdeleted entities while generating unique +slug, you must explicitly call **addManagedFilter** with a name of softdeleteable filter, so it can be disabled during +slug updates. The best place to do it, is when initializing sluggable listener. That will be automated in the future. + +**Portability:** + +- **Sluggable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) +ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions + +This article will cover the basic installation and functionality of **Sluggable** +behavior + +Content: + +- [Including](#including-extension) the extension +- Entity [example](#entity-mapping) +- Document [example](#document-mapping) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Basic usage [examples](#basic-examples) +- Custom [transliterator](#transliterator) +- Advanced usage [examples](#advanced-examples) +- Using [slug handlers](#slug-handlers) + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## Sluggable Entity example: + +### Sluggable annotations: + +- **@Gedmo\Mapping\Annotation\Slug** it will use this column to store **slug** generated +**fields** option must be specified, an array of field names to slug + +**Note:** that Sluggable interface is not necessary, except in cases there +you need to identify entity as being Sluggable. The metadata is loaded only once then +cache is activated + +**Note:** 2.0.x version of extensions used @Gedmo\Mapping\Annotation\Sluggable to identify +the field for slug + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function getCode() + { + return $this->code; + } + + public function getSlug() + { + return $this->slug; + } +} +``` + + + +## Sluggable Document example: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function getCode() + { + return $this->code; + } + + public function getSlug() + { + return $this->slug; + } +} +``` + + + +## Yaml mapping example + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + code: + type: string + length: 16 + slug: + type: string + length: 128 + gedmo: + slug: + separator: _ + style: camel + fields: + - title + - code + indexes: + search_idx: + columns: slug +``` + + + +## Xml mapping example + +**Note:** xml driver is not yet adapted for single slug mapping + +``` xml + + + + + + + + + + + + + + + +``` + + + +## Basic usage examples: + +### To save **Article** and generate slug simply use: + +``` php +setTitle('the title'); +$article->setCode('my code'); +$this->em->persist($article); +$this->em->flush(); + +echo $article->getSlug(); +// prints: the-title-my-code +``` + +### Some other configuration options for **slug** annotation: + +- **fields** (required, default=[]) - list of fields for slug +- **updatable** (optional, default=true) - **true** to update the slug on sluggable field changes, **false** - otherwise +- **unique** (optional, default=true) - **true** if slug should be unique and if identical it will be prefixed, **false** - otherwise +- **unique_base** (optional, default=null) - used in conjunction with **unique**. The name of the entity property that should be used as a key when doing a uniqueness check. +- **separator** (optional, default="-") - separator which will separate words in slug +- **prefix** (optional, default="") - prefix which will be added to the generated slug +- **suffix** (optional, default="") - suffix which will be added to the generated slug +- **style** (optional, default="default") - **"default"** all letters will be lowercase, **"camel"** - first word letter will be uppercase, **"upper"**- all word letter will be uppercase and **"lower"**- all word letter will be lowercase +- **handlers** (optional, default=[]) - list of slug handlers, like tree path slug, or customized, for example see bellow + +**Note**: handlers are totally optional + +**TreeSlugHandler** + +``` php +createdAt = new \DateTime; + } +} +``` + +And now test the result: + +``` php +setTitle('the title'); +$article->setCode('my code'); +$this->em->persist($article); +$this->em->flush(); + +echo $article->getSlug(); +// prints: The_Title_My_Code +``` + + + +## Custom transliterator + +To set your own custom transliterator, which would be used to generate the slug, use: + +``` php +setTransliterator($callable); + +// or use a closure + +$callable = function($text, $separatorUsed, $objectBeingSlugged) { + // ... + return $transliteratedText; +}; +$sluggableListener->setTransliterator($callable); +``` + + + +## Advanced examples: + +### Regenerating slug + +In case if you want the slug to regenerate itself based on sluggable fields, set the slug to **null**. + +*Note: in previous versions empty strings would also cause the slug to be regenerated. This behaviour was changed in v2.3.8.* + +``` php +find('Entity\Something', $id); +$entity->setSlug(null); + +$em->persist($entity); +$em->flush(); +``` + +### Setting the slug manually + +Sometimes you might need to set it manually, etc if generated one does not look satisfying enough. +Sluggable will ensure uniqueness of the slug. + +``` php +setSluggableField('won\'t be taken into account'); +$entity->setSlug('the required slug, set manually'); + +$em->persist($entity); +$em->flush(); + +echo $entity->getSlug(); // outputs: "the-required-slug-set-manually" +``` + +### Using TranslatableListener to translate our slug + +If you want to attach **TranslatableListener** also add it to EventManager after +the **SluggableListener**. It is important because slug must be generated first +before the creation of it`s translation. + +``` php +addEventSubscriber($sluggableListener); +$translatableListener = new \Gedmo\Translatable\TranslatableListener(); +$translatableListener->setTranslatableLocale('en_us'); +$evm->addEventSubscriber($translatableListener); +// now this event manager should be passed to entity manager constructor +``` + +And the Entity should look like: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setCode($code) + { + $this->code = $code; + } + + public function getCode() + { + return $this->code; + } + + public function getSlug() + { + return $this->slug; + } + + public function getUniqueSlug() + { + return $this->uniqueSlug; + } +} +``` + +Now the generated slug will be translated by Translatable behavior + + + +## Using slug handlers: + +There are built-in slug handlers like described in configuration options of slug, but there +can be also customized slug handlers depending on use cases. Usually the most logic use case +is for related slug. For instance if user has a **ManyToOne relation to a **Company** we +would like to have a url like **http://example.com/knplabs/gedi where **KnpLabs** +is a company and user name is **Gedi**. In this case relation has a path separator **/** + +User entity example: + +``` php +company = $company; + } + + public function getCompany() + { + return $this->company; + } + + public function getId() + { + return $this->id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getSlug() + { + return $this->slug; + } +} +``` + +Company entity example: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getAlias() + { + return $this->alias; + } +} +``` + +For other mapping drivers see +[xml](https://github.com/Atlantic18/DoctrineExtensions/blob/master/tests/Gedmo/Mapping/Driver/Xml/Mapping.Fixture.Xml.Sluggable.dcm.xml) or [yaml](https://github.com/Atlantic18/DoctrineExtensions/blob/master/tests/Gedmo/Mapping/Driver/Yaml/Mapping.Fixture.Yaml.Category.dcm.yml) examples from tests + +And the example usage: + +``` php +setTitle('KnpLabs'); +$em->persist($company); + +$gedi = new User; +$gedi->setUsername('Gedi'); +$gedi->setCompany($company); +$em->persist($gedi); + +$em->flush(); + +echo $gedi->getSlug(); // outputs "knplabs/gedi" + +$company->setTitle('KnpLabs Nantes'); +$em->persist($company); +$em->flush(); + +echo $gedi->getSlug(); // outputs "knplabs-nantes/gedi" +``` + +**Note:** tree slug handler, takes a parent relation to build slug recursively. + +Any suggestions on improvements are very welcome diff --git a/vendor/gedmo/doctrine-extensions/doc/softdeleteable.md b/vendor/gedmo/doctrine-extensions/doc/softdeleteable.md new file mode 100644 index 0000000..f49cb89 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/softdeleteable.md @@ -0,0 +1,286 @@ +# SoftDeleteable behavior extension for Doctrine 2 + +**SoftDeleteable** behavior allows to "soft delete" objects, filtering them +at SELECT time by marking them deleted as with a timestamp, but not explicitly removing them from the database. + +Features: + +- Works with DQL DELETE queries (using a Query Hint). +- All SELECT queries will be filtered, not matter from where they are executed (Repositories, DQL SELECT queries, etc). +- For now, it works only with the ORM +- Can be nested with other behaviors +- Annotation, Yaml and Xml mapping support for extensions +- Support for 'timeAware' option: When creating an entity set a date of deletion in the future and never worry about cleaning up at expiration time. +- Support for 'hardDelete' option: When deleting a second time it allows to disable hard delete. + +Content: + +- [Including](#including-extension) the extension +- Entity [example](#entity-mapping) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Usage [examples](#usage) +- Using [Traits](#traits) + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + +With SoftDeleteable there's one more step you need to do. You need to add the filter to your configuration: + +``` php + +$config = new Doctrine\ORM\Configuration; + +// Your configs.. + +$config->addFilter('soft-deleteable', 'Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter'); +``` + +And then you can access the filter from your EntityManager to enable or disable it with the following code: + +``` php +// This will enable the SoftDeleteable filter, so entities which were "soft-deleted" will not appear +// in results +// You should adapt the filter name to your configuration (ex: softdeleteable) +$em->getFilters()->enable('soft-deleteable'); + +// This will disable the SoftDeleteable filter, so entities which were "soft-deleted" will appear in results +$em->getFilters()->disable('soft-deleteable'); +``` + +Or from your DocumentManager (ODM): + +``` php +// This will enable the SoftDeleteable filter, so entities which were "soft-deleted" will not appear +// in results +// You should adapt the filter name to your configuration (ex: softdeleteable) +$em->getFilterCollection()->enable('soft-deleteable'); + +// This will disable the SoftDeleteable filter, so entities which were "soft-deleted" will appear in results +$em->getFilterCollection()->disable('soft-deleteable'); +``` + +**NOTE:** by default all filters are disabled, so you must explicitly enable **soft-deleteable** filter in your setup +or whenever you need it. + + + +## SoftDeleteable Entity example: + +### SoftDeleteable annotations: +- **@Gedmo\Mapping\Annotation\SoftDeleteable** this class annotation tells if a class is SoftDeleteable. It has a +mandatory parameter "fieldName", which is the name of the field to be used to hold the known "deletedAt" field. It +must be of any of the date types. + +Available configuration options: +- **fieldName** - The name of the field that will be used to determine if the object is removed or not (NULL means +it's not removed. A date value means it was removed). NOTE: The field MUST be nullable. + +- **hardDelete** - A boolean to enable or disable hard delete after soft delete has already been done. NOTE: Set to true by default. + +**Note:** that SoftDeleteable interface is not necessary, except in cases where +you need to identify entity as being SoftDeleteable. The metadata is loaded only once then +cache is activated. + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getDeletedAt() + { + return $this->deletedAt; + } + + public function setDeletedAt($deletedAt) + { + $this->deletedAt = $deletedAt; + } +} +``` + + + +## Yaml mapping example: + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + gedmo: + soft_deleteable: + field_name: deletedAt + time_aware: false + hard_delete: true + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + deletedAt: + type: date + nullable: true +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + +``` + + + +## Usage: + +``` php +setTitle('My Article'); + +$em->persist($article); +$em->flush(); + +// Now if we remove it, it will set the deletedAt field to the actual date +$em->remove($article); +$em->flush(); + +$repo = $em->getRepository('Article'); +$art = $repo->findOneBy(array('title' => 'My Article')); + +// It should NOT return the article now +$this->assertNull($art); + +// But if we disable the filter, the article should appear now +$em->getFilters()->disable('soft-deleteable'); + +$art = $repo->findOneBy(array('title' => 'My Article')); + +$this->assertTrue(is_object($art)); + +// Enable / Disable filter filter, for specified entity (default is enabled for all) +$filter = $em->getFilters()->enable('soft-deleteable'); +$filter->disableForEntity('Entity\Article'); +$filter->enableForEntity('Entity\Article'); + +// Undelete the entity by setting the deletedAt field to null +$article->setDeletedAt(null); +``` + +Easy like that, any suggestions on improvements are very welcome. + + + +## Traits + +You can use softDeleteable traits for quick **deletedAt** timestamp definitions +when using annotation mapping. +There is also a trait without annotations for easy integration purposes. + +**Note:** this feature is only available since php **5.4.0**. And you are not required +to use the Traits provided by extensions. + +``` php + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## Sortable Entity example: + +### Sortable annotations: + +- **@Gedmo\Mapping\Annotation\SortableGroup** it will use this field for **grouping** +- **@Gedmo\Mapping\Annotation\SortablePosition** it will use this column to store **position** index + +**Note:** that Sortable interface is not necessary, except in cases there +you need to identify entity as being Sortable. The metadata is loaded only once then +cache is activated + +**Note:** that you should register SortableRepository (or a subclass) as the repository in the Entity +annotation to benefit from its query methods. + +``` php +id; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setPosition($position) + { + $this->position = $position; + } + + public function getPosition() + { + return $this->position; + } + + public function setCategory($category) + { + $this->category = $category; + } + + public function getCategory() + { + return $this->category; + } +} +``` + + + +## Yaml mapping example + +Yaml mapped Item: **/mapping/yaml/Entity.Item.dcm.yml** + +``` +--- +Entity\Item: + type: entity + table: items + id: + id: + type: integer + generator: + strategy: AUTO + fields: + name: + type: string + length: 64 + position: + type: integer + gedmo: + - sortablePosition + category: + type: string + length: 128 + gedmo: + - sortableGroup +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + +``` + + + +## Basic usage examples: + +### To save **Items** at the end of the sorting list simply do: + +``` php +setName('item 1'); +$item1->setCategory('category 1'); +$this->em->persist($item1); + +$item2 = new Item(); +$item2->setName('item 2'); +$item2->setCategory('category 1'); +$this->em->persist($item2); + +$this->em->flush(); + +echo $item1->getPosition(); +// prints: 0 +echo $item2->getPosition(); +// prints: 1 +``` + +### Save **Item** at a given position: + +``` php +setName('item 1'); +$item1->setCategory('category 1'); +$this->em->persist($item1); + +$item2 = new Item(); +$item2->setName('item 2'); +$item2->setCategory('category 1'); +$this->em->persist($item2); + +$item0 = new Item(); +$item0->setName('item 0'); +$item0->setCategory('category 1'); +$item0->setPosition(0); +$this->em->persist($item0); + +$this->em->flush(); + +$repo = $this->em->getRepository('Entity\\Item'); +$items = $repo->getBySortableGroupsQuery(array('category' => 'category 1'))->getResult(); +foreach ($items as $item) { + echo "{$item->getPosition()}: {$item->getName()}\n"; +} +// prints: +// 0: item 0 +// 1: item 1 +// 2: item 2 +``` + +### Reordering the sorted list: + +``` php +setName('item 1'); +$item1->setCategory('category 1'); +$this->em->persist($item1); + +$item2 = new Item(); +$item2->setName('item 2'); +$item2->setCategory('category 1'); +$this->em->persist($item2); + +$this->em->flush(); + +// Update the position of item2 +$item2->setPosition(0); +$this->em->persist($item2); + +$this->em->flush(); + +$repo = $this->em->getRepository('Entity\\Item'); +$items = $repo->getBySortableGroupsQuery(array('category' => 'category 1'))->getResult(); +foreach ($items as $item) { + echo "{$item->getPosition()}: {$item->getName()}\n"; +} +// prints: +// 0: item 2 +// 1: item 1 + +``` + +### Using a foreign_key / relation as SortableGroup + +If you want to use a foreign key / relation as sortable group, you have to put @Gedmo\SortableGroup annotation on ManyToOne annotation: + +``` +/** + * @Gedmo\SortableGroup + * @ORM\ManyToOne(targetEntity="Item", inversedBy="children") + * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="SET NULL") + */ +private $parent; +``` + + +To move an item at the end of the list, you can set the position to `-1`: + +``` +$item2->setPosition(-1); +``` + + + +## Custom comparison: + +Sortable works by comparing objects in the same group to see how they should be positioned. From time to time you may want to customize the way these +objects are compared by simply implementing the Doctrine\Common\Comparable interface + +``` php + + +## Symfony2 application + +First of all, we will need a symfony2 startup application, let's say [symfony-standard edition +with composer](http://github.com/KnpLabs/symfony-with-composer). Follow the standard setup: + +- `git clone git://github.com/KnpLabs/symfony-with-composer.git example` +- `cd example && rm -rf .git && php bin/vendors install` +- ensure your application loads and meets requirements, by following the url: **http://your_virtual_host/app_dev.php** + +Now let's add the **gedmo/doctrine-extensions** into **composer.json** + +```json +{ + "require": { + "php": ">=5.3.2", + "symfony/symfony": ">=2.0.9,<2.1.0-dev", + "doctrine/orm": ">=2.1.0,<2.2.0-dev", + "twig/extensions": "*", + + "symfony/assetic-bundle": "*", + "sensio/generator-bundle": "2.0.*", + "sensio/framework-extra-bundle": "2.0.*", + "sensio/distribution-bundle": "2.0.*", + "jms/security-extra-bundle": "1.0.*", + "gedmo/doctrine-extensions": "dev-master" + }, + + "autoload": { + "psr-0": { + "Acme": "src/" + } + } +} +``` + +Update vendors, run: **php composer.phar update gedmo/doctrine-extensions** +Initially in this package you have **doctrine2 orm** included, so we will base our setup +and configuration for this specific connection. Do not forget to configure your database +connection parameters, edit **app/config/parameters.yml** + + + +## Mapping + +Let's start from the mapping. In case you use the **translatable**, **tree** or **loggable** +extension you will need to map those abstract mapped superclasses for your ORM to be aware of. +To do so, add some mapping info to your **doctrine.orm** configuration, edit **app/config/config.yml**: + +```yaml +doctrine: + dbal: +# your dbal config here + + orm: + auto_generate_proxy_classes: %kernel.debug% + auto_mapping: true +# only these lines are added additionally + mappings: + translatable: + type: annotation + alias: Gedmo + prefix: Gedmo\Translatable\Entity + # make sure vendor library location is correct + dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity" +``` + +After that, running **php app/console doctrine:mapping:info** you should see the output: + +``` +Found 3 entities mapped in entity manager default: +[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation +[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation +[OK] Gedmo\Translatable\Entity\Translation +``` +Well, we mapped only **translatable** for now, it really depends on your needs, which extensions +your application uses. + +**Note:** there is **Gedmo\Translatable\Entity\Translation** which is not a super class, in that case +if you create a doctrine schema, it will add **ext_translations** table, which might not be useful +to you also. To skip mapping of these entities, you can map **only superclasses** + +```yaml +mappings: + translatable: + type: annotation + alias: Gedmo + prefix: Gedmo\Translatable\Entity + # make sure vendor library location is correct + dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass" +``` + +The configuration above, adds a **/MappedSuperclass** into directory depth, after running +**php app/console doctrine:mapping:info** you should only see now: + +``` +Found 2 entities mapped in entity manager default: +[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation +[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation +``` + +This is very useful for advanced requirements and quite simple to understand. So now let's map +everything the extensions provide: + +```yaml +# only orm config branch of doctrine +orm: + auto_generate_proxy_classes: %kernel.debug% + auto_mapping: true +# only these lines are added additionally + mappings: + translatable: + type: annotation + alias: Gedmo + prefix: Gedmo\Translatable\Entity + # make sure vendor library location is correct + dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity" + loggable: + type: annotation + alias: Gedmo + prefix: Gedmo\Loggable\Entity + dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity" + tree: + type: annotation + alias: Gedmo + prefix: Gedmo\Tree\Entity + dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity" +``` + + + +## Doctrine extension listener services + +Next, the heart of extensions are behavioral listeners which pours all the sugar. We will +create a **yml** service file in our config directory. The setup can be different, your config could be located +in the bundle, it depends on your preferences. Edit **app/config/doctrine_extensions.yml** + +```yaml +# services to handle doctrine extensions +# import it in config.yml +services: + # KernelRequest listener + extension.listener: + class: Acme\DemoBundle\Listener\DoctrineExtensionListener + calls: + - [ setContainer, [ @service_container ] ] + tags: + # translatable sets locale after router processing + - { name: kernel.event_listener, event: kernel.request, method: onLateKernelRequest, priority: -10 } + # loggable hooks user username if one is in security context + - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + # translatable sets locale such as default application locale before command execute + - { name: kernel.event_listener, event: console.command, method: onConsoleCommand, priority: -10 } + + + # Doctrine Extension listeners to handle behaviors + gedmo.listener.tree: + class: Gedmo\Tree\TreeListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + + gedmo.listener.translatable: + class: Gedmo\Translatable\TranslatableListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + - [ setDefaultLocale, [ %locale% ] ] + - [ setTranslationFallback, [ false ] ] + + gedmo.listener.timestampable: + class: Gedmo\Timestampable\TimestampableListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + + gedmo.listener.sluggable: + class: Gedmo\Sluggable\SluggableListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + + gedmo.listener.sortable: + class: Gedmo\Sortable\SortableListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + + gedmo.listener.loggable: + class: Gedmo\Loggable\LoggableListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + + gedmo.listener.blameable: + class: Gedmo\Blameable\BlameableListener + tags: + - { name: doctrine.event_subscriber, connection: default } + calls: + - [ setAnnotationReader, [ "@annotation_reader" ] ] + +``` + +So what does it include in general? Well, it creates services for all extension listeners. +You can remove some which you do not use, or change them as you need. **Translatable** for instance, +sets the default locale to the value of your `%locale%` parameter, you can configure it differently. + +**Note:** In case you noticed, there is **Acme\DemoBundle\Listener\DoctrineExtensionListener**. +You will need to create this listener class if you use **loggable** or **translatable** +behaviors. This listener will set the **locale used** from request and **username** to +loggable. So, to finish the setup create **Acme\DemoBundle\Listener\DoctrineExtensionListener** + +```php +container = $container; + } + + public function onLateKernelRequest(GetResponseEvent $event) + { + $translatable = $this->container->get('gedmo.listener.translatable'); + $translatable->setTranslatableLocale($event->getRequest()->getLocale()); + } + + public function onConsoleCommand() + { + $this->container->get('gedmo.listener.translatable') + ->setTranslatableLocale($this->container->get('translator')->getLocale()); + } + + public function onKernelRequest(GetResponseEvent $event) + { + if (Kernel::MAJOR_VERSION == 2 && Kernel::MINOR_VERSION < 6) { + $securityContext = $this->container->get('security.context', ContainerInterface::NULL_ON_INVALID_REFERENCE); + if (null !== $securityContext && null !== $securityContext->getToken() && $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) { + # for loggable behavior + $loggable = $this->container->get('gedmo.listener.loggable'); + $loggable->setUsername($securityContext->getToken()->getUsername()); + + # for blameable behavior + $blameable = $this->container->get('gedmo.listener.blameable'); + $blameable->setUserValue($securityContext->getToken()->getUser()); + } + } + else { + $tokenStorage = $this->container->get('security.token_storage')->getToken(); + $authorizationChecker = $this->container->get('security.authorization_checker'); + if (null !== $tokenStorage && $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) { + # for loggable behavior + $loggable = $this->container->get('gedmo.listener.loggable'); + $loggable->setUsername($tokenStorage->getUser()); + + # for blameable behavior + $blameable = $this->container->get('gedmo.listener.blameable'); + $blameable->setUserValue($tokenStorage->getUser()); + } + } + } +} +``` +Do not forget to import **doctrine_extensions.yml** in your **app/config/config.yml**: + +```yaml +# file: app/config/config.yml +imports: + - { resource: parameters.yml } + - { resource: security.yml } + - { resource: doctrine_extensions.yml } + +# ... configuration follows +``` + + + +## Example + +After that, you have your extensions set up and ready to be used! Too easy right? Well, +if you do not believe me, let's create a simple entity in our **Acme** project: + +```php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getCreated() + { + return $this->created; + } + + public function getUpdated() + { + return $this->updated; + } +} +``` + +Now, let's have some fun: + +- if you have not created the database yet, run `php app/console doctrine:database:create` +- create the schema `php app/console doctrine:schema:create` + +Everything will work just fine, you can modify the **Acme\DemoBundle\Controller\DemoController** +and add an action to test how it works: + +```php +// file: src/Acme/DemoBundle/Controller/DemoController.php +// include this code portion + +/** + * @Route("/posts", name="_demo_posts") + */ +public function postsAction() +{ + $em = $this->getDoctrine()->getEntityManager(); + $repository = $em->getRepository('AcmeDemoBundle:BlogPost'); + // create some posts in case if there aren't any + if (!$repository->findOneById('hello_world')) { + $post = new \Acme\DemoBundle\Entity\BlogPost(); + $post->setTitle('Hello world'); + + $next = new \Acme\DemoBundle\Entity\BlogPost(); + $next->setTitle('Doctrine extensions'); + + $em->persist($post); + $em->persist($next); + $em->flush(); + } + $posts = $em + ->createQuery('SELECT p FROM AcmeDemoBundle:BlogPost p') + ->getArrayResult() + ; + die(var_dump($posts)); +} +``` + +Now if you follow the url: **http://your_virtual_host/app_dev.php/demo/posts** you +should see a print of posts, this is only an extension demo, we will not create a template. + + + +## More tips + +Regarding, the setup, I do not think it's too complicated to use, in general it is simple +enough, and lets you understand at least small parts on how you can hook mappings into doctrine, and +how easily extension services are added. This configuration does not hide anything behind +curtains and allows you to modify the configuration as you require. + +### Multiple entity managers + +If you use more than one entity manager, you can simply tag the listener +with other the manager name: + +```yaml +services: + # tree behavior + gedmo.listener.tree: + class: Gedmo\Tree\TreeListener + tags: + - { name: doctrine.event_subscriber, connection: default } + # additional ORM subscriber + - { name: doctrine.event_subscriber, connection: other_connection } + # ODM MongoDb subscriber, where **default** is manager name + - { name: doctrine_mongodb.odm.event_subscriber } + calls: + - [ setAnnotationReader, [ @annotation_reader ] ] +``` + +Regarding, mapping of ODM mongodb, it's basically the same: + +```yaml +doctrine_mongodb: + default_database: 'my_database' + default_connection: 'default' + default_document_manager: 'default' + connections: + default: ~ + document_managers: + default: + connection: 'default' + auto_mapping: true + mappings: + translatable: + type: annotation + alias: GedmoDocument + prefix: Gedmo\Translatable\Document + # make sure vendor library location is correct + dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document" +``` + +This also shows, how to make mappings based on single manager. All what differs is that **Document** +instead of **Entity** is used. I haven't tested it with mongo though. + +**Note:** [extension repository](http://github.com/Atlantic18/DoctrineExtensions) contains all +[documentation](http://github.com/Atlantic18/DoctrineExtensions/tree/master/doc) you may need +to understand how you can use it in your projects. + + + +## Alternative over configuration + +You can use [StofDoctrineExtensionsBundle](http://github.com/stof/StofDoctrineExtensionsBundle) which is a wrapper of these extensions + +## Troubleshooting + +- Make sure there are no *.orm.yml or *.orm.xml files for your Entities in your bundles Resources/config/doctrine directory. With those files in place the annotations won't be taken into account. diff --git a/vendor/gedmo/doctrine-extensions/doc/timestampable.md b/vendor/gedmo/doctrine-extensions/doc/timestampable.md new file mode 100644 index 0000000..881aeca --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/timestampable.md @@ -0,0 +1,674 @@ +# Timestampable behavior extension for Doctrine 2 + +**Timestampable** behavior will automate the update of date fields +on your Entities or Documents. It works through annotations and can update +fields on creation, update, property subset update, or even on specific property value change. + +Features: + +- Automatic predefined date field update on creation, update, property subset update, and even on record property changes +- ORM and ODM support using same listener +- Specific annotations for properties, and no interface required +- Can react to specific property or relation changes to specific value +- Can be nested with other behaviors +- Annotation, Yaml and Xml mapping support for extensions + +Update **2012-06-26** + +- Allow multiple values for on="change" + +Update **2012-03-10** + +- Add [Timestampable traits](#traits) + +Update **2011-04-04** + +- Made single listener, one instance can be used for any object manager +and any number of them + +**Note:** +- Last update date: **2012-01-02** + +**Portability:** + +- **Timestampable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) +ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions + +This article will cover the basic installation and functionality of **Timestampable** behavior + +Content: + +- [Including](#including-extension) the extension +- Entity [example](#entity-mapping) +- Document [example](#document-mapping) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Advanced usage [examples](#advanced-examples) +- Using [Traits](#traits) + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + +## Timestampable Entity example: + +### Timestampable annotations: +- **@Gedmo\Mapping\Annotation\Timestampable** this annotation tells that this column is timestampable +by default it updates this column on update. If column is not date, datetime or time +type it will trigger an exception. + +Available configuration options: + +- **on** - is main option and can be **create, update, change** this tells when it +should be updated +- **field** - only valid if **on="change"** is specified, tracks property or a list of properties for changes +- **value** - only valid if **on="change"** is specified and the tracked field is a single field (not an array), if the tracked field has this **value** + +**Note:** that Timestampable interface is not necessary, except in cases where +you need to identify entity as being Timestampable. The metadata is loaded only once then +cache is activated + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setBody($body) + { + $this->body = $body; + } + + public function getBody() + { + return $this->body; + } + + public function getCreated() + { + return $this->created; + } + + public function getUpdated() + { + return $this->updated; + } + + public function getContentChanged() + { + return $this->contentChanged; + } +} +``` + + + +## Timestampable Document example: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setBody($body) + { + $this->body = $body; + } + + public function getBody() + { + return $this->body; + } + + public function getCreated() + { + return $this->created; + } + + public function getUpdated() + { + return $this->updated; + } + + public function getContentChanged() + { + return $this->contentChanged; + } +} +``` + +Now on update and creation these annotated fields will be automatically updated + + + +## Yaml mapping example: + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +```yaml +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + created: + type: date + gedmo: + timestampable: + on: create + updated: + type: datetime + gedmo: + timestampable: + on: update +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Advanced examples: + +### Using dependency of property changes + +Add another entity which would represent Article Type: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } +} +``` + +Now update the Article Entity to reflect published date on Type change: + +``` php +type = $type; + } + + public function getId() + { + return $this->id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getCreated() + { + return $this->created; + } + + public function getUpdated() + { + return $this->updated; + } + + public function getPublished() + { + return $this->published; + } +} +``` + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` yaml +--- +Entity\Article: + type: entity + table: articles + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + created: + type: date + gedmo: + timestampable: + on: create + updated: + type: datetime + gedmo: + timestampable: + on: update + published: + type: datetime + gedmo: + timestampable: + on: change + field: type.title + value: Published + manyToOne: + type: + targetEntity: Entity\Type + inversedBy: articles +``` + +Now few operations to get it all done: + +``` php +setTitle('My Article'); + +$em->persist($article); +$em->flush(); +// article: $created, $updated were set + +$type = new Type; +$type->setTitle('Published'); + +$article = $em->getRepository('Entity\Article')->findByTitle('My Article'); +$article->setType($type); + +$em->persist($article); +$em->persist($type); +$em->flush(); +// article: $published, $updated were set + +$article->getPublished()->format('Y-m-d'); // the date article type changed to published +``` + +Easy like that, any suggestions on improvements are very welcome + +### Creating a UTC DateTime type that stores your datetimes in UTC + +First, we define our custom data type (note the type name is datetime and the type extends DateTimeType which simply overrides the default Doctrine type): + +``` php +setTimeZone(self::$utc); + + return $value->format($platform->getDateTimeFormatString()); + } + + public function convertToPHPValue($value, AbstractPlatform $platform) + { + if ($value === null) { + return null; + } + + if (is_null(self::$utc)) { + self::$utc = new \DateTimeZone('UTC'); + } + + $val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value, self::$utc); + + if (!$val) { + throw ConversionException::conversionFailed($value, $this->getName()); + } + + return $val; + } +} +``` + +Now in Symfony2, we register and override the **datetime** type. **WARNING:** this will override the **datetime** type for all your entities and for all entities in external bundles or extensions, so if you have some entities that require the standard **datetime** type from Doctrine, you must modify the above type and use a different name (such as **utcdatetime**). Additionally, you'll need to modify **Timestampable** so that it includes **utcdatetime** as a valid type. + +``` yaml +doctrine: + dbal: + types: + datetime: Acme\DoctrineExtensions\DBAL\Types\UTCDateTimeType +``` + +And our Entity properties look as expected: + +``` php + + +## Traits + +You can use timestampable traits for quick **createdAt** **updatedAt** timestamp definitions +when using annotation mapping. +There is also a trait without annotations for easy integration purposes. + +**Note:** this feature is only available since php **5.4.0**. And you are not required +to use the Traits provided by extensions. + +``` php +name = $name; + return $this; + } + + public function getName() + { + return $this->name; + } +} +``` + +It should have owner and so many more attributes, but lets keep it simple. Here follows Category: + +``` php +title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setParent(Category $parent = null) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } + + public function getLevel() + { + return $this->level; + } + + public function setShop(Shop $shop) + { + $this->shop = $shop; + return $this; + } + + public function getShop() + { + return $this->shop; + } +} +``` + +**NOTE:** it would be perfect if we could use tree root as a shop relation. But it is not currently supported and +might be available only in next versions. + +Now everytime we do **insert**, **move** or **remove** actions for Category: + +``` php +getEntityManager(); + $conn = $em->getConnection(); + $categoryRepository = $em->getRepository("App\Entity\Category"); + // start transaction + $conn->beginTransaction(); + try { + // select shop for update - locks it for any read attempts until this transaction ends + $shop = $em->find("App\Entity\Shop", $currentShopId, LockMode::PESSIMISTIC_WRITE); + + // create a new category + $category = new Category; + $category->setTitle($_POST["title"]); + $category->setShop($shop); + $parent = $categoryRepository->findOneById($_POST["parent_id"]); + + // persist and flush + $categoryRepository->persistAsFirstChildOf($category, $parent); + $em->flush(); + + $conn->commit(); + } catch (Exception $e) { + $conn->rollback(); + throw $e; + } + + // if all went well, we can set flash message or whatever + // other operations which attempts to select in lock mode, will wait till this transaction ends. + } +} +``` + +You may separate locking transaction to run callback function and make it as a service to abstract and prevent +code duplication. Anyway, my advice would be to use only one transaction per request and best inside controller +directly, where you would ensure that all operations performed during the action can be safely rolled back. + +Also to use this kind of locking, you need an entity which is necessary to read on concurrent request which attempts +to update the same tree. In this example, **Shop** entity fits the bill perfectly. Otherwise you need to find a way to +safely lock the tree table. + +The point of this example is: that concurrently atomic updates, might cause other parallel actions to use outdated +information, based on which it may perform falsely calculated consequent updates. And you need to prevent this from +happening in order to maintain your data. Extensions and ORM cannot perform such actions automatically. + diff --git a/vendor/gedmo/doctrine-extensions/doc/translatable.md b/vendor/gedmo/doctrine-extensions/doc/translatable.md new file mode 100644 index 0000000..451a8f0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/translatable.md @@ -0,0 +1,894 @@ +# Translatable behavior extension for Doctrine 2 + +**Translatable** behavior offers a very handy solution for translating specific record fields +in different languages. Further more, it loads the translations automatically for a locale +currently used, which can be set to **Translatable Listener** on it`s initialization or later +for other cases through the **Entity** itself + +Features: + +- Automatic storage of translations in database +- ORM and ODM support using same listener +- Automatic translation of Entity or Document fields when loaded +- ORM query can use **hint** to translate all records without issuing additional queries +- Can be nested with other behaviors +- Annotation, Yaml and Xml mapping support for extensions + +**2012-01-28** + +- Created personal translation which maps through real foreign key +constraint. This dramatically improves the management of translations + +**2012-01-04** + +- Refactored translatable to be able to persist, update many translations +using repository, [issue #224](https://github.com/Atlantic18/DoctrineExtensions/issues/224) + +**2011-12-11** + +- Added more useful translation query hints: Override translatable locale, inner join +translations instead left join, override translation fallback + +**2011-11-08** + +- Thanks to [@acasademont](https://github.com/acasademont) Translatable now does not store translations for default locale. It is always left as original record value. +So be sure you do not change your default locale per project or per data migration. This way +it is more rational and unnecessary to store it additionally in translation table. + +Update **2011-04-21** + +- Implemented multiple translation persistence through repository + +Update **2011-04-16** + +- Made an ORM query **hint** to hook into any select type query, which will join the translations +and let you **filter, order or search** by translated fields directly. It also will translate +all selected **collections or simple components** without issuing additional queries. It also +supports translation fallbacks +- For performance reasons, translation fallbacks are disabled by default + +Update **2011-04-04** + +- Made single listener, one instance can be used for any object manager +and any number of them + +**Note list:** + +- Public [Translatable repository](http://github.com/Atlantic18/DoctrineExtensions "Translatable extension on Github") is available on github +- Using other extensions on the same Entity fields may result in unexpected way +- May impact your application performance since it does an additional query for translation if loaded without query hint +- Last update date: **2012-02-15** + +**Portability:** + +- **Translatable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) +ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions + +This article will cover the basic installation and functionality of **Translatable** behavior + +Content: + +- [Including](#including-extension) the extension +- Entity [example](#entity-domain-object) +- Document [example](#document-domain-object) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Basic usage [examples](#basic-examples) +- [Persisting](#multi-translations) multiple translations +- Using ORM query [hint](#orm-query-hint) +- Advanced usage [examples](#advanced-examples) +- Personal [translations](#personal-translations) + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + +### Translatable annotations: +- **@Gedmo\Mapping\Annotation\Translatable** it will **translate** this field +- **@Gedmo\Mapping\Annotation\TranslationEntity(class="my\class")** it will use this class to store **translations** generated +- **@Gedmo\Mapping\Annotation\Locale or @Gedmo\Mapping\Annotation\Language** this will identify this column as **locale** or **language** +used to override the global locale + + + +## Translatable Entity example: + +**Note:** that Translatable interface is not necessary, except in cases where +you need to identify an entity as being Translatable. The metadata is loaded only once when +cache is activated + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getContent() + { + return $this->content; + } + + public function setTranslatableLocale($locale) + { + $this->locale = $locale; + } +} +``` + + + +## Translatable Document example: + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getContent() + { + return $this->content; + } + + public function setTranslatableLocale($locale) + { + $this->locale = $locale; + } +} +``` + + + +## Yaml mapping example + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\Article: + type: entity + table: articles + gedmo: + translation: + locale: localeField +# using specific personal translation class: +# entity: Translatable\Fixture\CategoryTranslation + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + gedmo: + - translatable + content: + type: text + gedmo: + - translatable +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Basic usage examples: + +Currently a global locale used for translations is "en_us" which was +set in **TranslationListener** globally. To save article with its translations: + +``` php +setTitle('my title in en'); +$article->setContent('my content in en'); +$em->persist($article); +$em->flush(); +``` + +This inserted an article and inserted the translations for it in "en_us" locale +only if **en_us** is not the [default locale](#advanced-examples) in case if default locale +matches current locale - it uses original record value as translation + +Now lets update our article in different locale: + +``` php +find('Entity\Article', 1 /*article id*/); +$article->setTitle('my title in de'); +$article->setContent('my content in de'); +$article->setTranslatableLocale('de_de'); // change locale +$em->persist($article); +$em->flush(); +``` + +This updated an article and inserted the translations for it in "de_de" locale +To see and load all translations of **Translatable** Entity: + +``` php +find('Entity\Article', 1 /*article id*/); +$article->setLocale('ru_ru'); +$em->refresh($article); + +$article = $em->find('Entity\Article', 1 /*article id*/); +$repository = $em->getRepository('Gedmo\Translatable\Entity\Translation'); +$translations = $repository->findTranslations($article); +/* $translations contains: +Array ( + [de_de] => Array + ( + [title] => my title in de + [content] => my content in de + ) + + [en_us] => Array + ( + [title] => my title in en + [content] => my content in en + ) +)*/ +``` + +As far as our global locale is now "en_us" and updated article has "de_de" values. +Lets try to load it and it should be translated in English + +``` php +getRepository('Entity\Article')->find(1/* id of article */); +echo $article->getTitle(); +// prints: "my title in en" +echo $article->getContent(); +// prints: "my content in en" +``` + + + +## Persisting multiple translations + +Usually it is more convenient to persist more translations when creating +or updating a record. **Translatable** allows to do that through translation repository. +All additional translations will be tracked by listener and when the flush will be executed, +it will update or persist all additional translations. + +**Note:** these translations will not be processed as ordinary fields of your object, +in case if you translate a **slug** additional translation will not know how to generate +the slug, so the value as an additional translation should be processed when creating it. + +### Example of multiple translations: + +``` php +getRepository('Gedmo\\Translatable\\Entity\\Translation'); +// it works for ODM also +$article = new Article; +$article->setTitle('My article en'); +$article->setContent('content en'); + +$repository->translate($article, 'title', 'de', 'my article de') + ->translate($article, 'content', 'de', 'content de') + ->translate($article, 'title', 'ru', 'my article ru') + ->translate($article, 'content', 'ru', 'content ru') +; + +$em->persist($article); +$em->flush(); + +// updating same article also having one new translation + +$repo + ->translate($article, 'title', 'lt', 'title lt') + ->translate($article, 'content', 'lt', 'content lt') + ->translate($article, 'title', 'ru', 'title ru change') + ->translate($article, 'content', 'ru', 'content ru change') + ->translate($article, 'title', 'en', 'title en (default locale) update') + ->translate($article, 'content', 'en', 'content en (default locale) update') +; +$em->flush(); +``` + + + +## Using ORM query hint + +By default, behind the scenes, when you load a record - translatable hooks into **postLoad** +event and issues additional query to translate all fields. Imagine that, when you load a collection, +it may issue a lot of queries just to translate those fields. Including array hydration, +it is not possible to hook any **postLoad** event since it is not an +entity being hydrated. These are the main reasons why **TranslationWalker** was created. + +**TranslationWalker** uses a query **hint** to hook into any **select type query**, +and when you execute the query, no matter which hydration method you use, it automatically +joins the translations for all fields, so you could use ordering filtering or whatever you +want on **translated fields** instead of original record fields. + +And in result there is only one query for all this happiness. + +If you use translation [fallbacks](#advanced-examples) it will be also in the same single +query and during the hydration process it will replace the empty fields in case if they +do not have a translation in currently used locale. + +Now enough talking, here is an example: + +``` php +createQuery($dql); +// set the translation query hint +$query->setHint( + \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, + 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' +); + +$articles = $query->getResult(); // object hydration +$articles = $query->getArrayResult(); // array hydration +``` + +And even a subselect: + +``` php +createQuery($dql); +$query->setHint( + \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, + 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' +); +``` + +**NOTE:** if you use memcache or apc. You should set locale and other options like fallbacks +to query through hints. Otherwise the query will be cached with a first used locale + +``` php +setHint( + \Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE, + 'en' // take locale from session or request etc. +); +// fallback +$query->setHint( + \Gedmo\Translatable\TranslatableListener::HINT_FALLBACK, + 1 // fallback to default values in case if record is not translated +); + +$articles = $query->getResult(); // object hydration +``` + +There's no need for any words anymore.. right? +I recommend you to use it extensively since it is a way better performance, even in +cases where you need to load single translated entity. + +**Note**: Even in **COUNT** select statements translations are joined to leave a +possibility to filter by translated field, if you do not need it, just do not set +the **hint**. Also take into account that it is not possible to translate components +in **JOIN WITH** statement, example + +``` +JOIN a.comments c WITH c.message LIKE '%will_not_be_translated%'` +``` + +**Note**: any **find** related method calls cannot hook this hint automagically, we +will use a different approach when **persister overriding feature** will be +available in **Doctrine** + +In case if **translation query walker** is used, you can additionally override: + +### Overriding translation fallback + +``` php +setHint(\Gedmo\Translatable\TranslatableListener::HINT_FALLBACK, 1); +``` + +will fallback to default locale translations instead of empty values if used. +And will override the translation listener setting for fallback. + +``` php +setHint(\Gedmo\Translatable\TranslatableListener::HINT_FALLBACK, 0); +``` + +will do the opposite. + +### Using inner join strategy + +``` php +setHint(\Gedmo\Translatable\TranslatableListener::HINT_INNER_JOIN, true); +``` + +will use **INNER** joins +for translations instead of **LEFT** joins, so that in case if you do not want untranslated +records in your result set for instance. + +### Overriding translatable locale + +``` php +setHint(\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'en'); +``` + +would override the translation locale used to translate the resultset. + +**Note:** all these query hints lasts only for the specific query. + + + +## Advanced examples: + +### Default locale + +In some cases we need a default translation as a fallback if record does not have +a translation on globally used locale. In that case Translation Listener takes the +current value of Entity. So if **default locale** is specified and it matches the +locale in which record is being translated - it will not create extra translation +but use original values instead. If translation fallback is set to **false** it +will fill untranslated values as blanks + +To set the default locale: + +``` php +setDefaultLocale('en_us'); +``` + +To set translation fallback: + +``` php +setTranslationFallback(true); // default is false +``` + +**Note**: Default locale should be set on the **TranslatableListener** initialization +once, since it can impact your current records if it will be changed. As it +will not store extra record in translation table by default. + +If you need to store translation in default locale, set: + +``` php +setPersistDefaultLocaleTranslation(true); // default is false +``` + +This would always store translations in all locales, also keeping original record +translated field values in default locale set. + +### Translation Entity + +In some cases if there are thousands of records or even more.. we would like to +have a single table for translations of this Entity in order to increase the performance +on translation loading speed. This example will show how to specify a different Entity for +your translations by extending the mapped superclass. + +ArticleTranslation Entity: + +``` php + + +## Personal translations + +Translatable has **AbstractPersonalTranslation** mapped superclass, which must +be extended and mapped based on your **entity** which you want to translate. +Note: translations are not automapped because of user preference based on cascades +or other possible choices, which user can make. +Personal translations uses foreign key constraint which is fully managed by ORM and +allows to have a collection of related translations. User can use it anyway he likes, etc.: +implementing array access on entity, using left join to fill collection and so on. + +Note: that [query hint](#orm-query-hint) will work on personal translations the same way. +You can always use a left join like for standard doctrine collections. + +Usage example: + +``` php +translations = new ArrayCollection(); + } + + public function getTranslations() + { + return $this->translations; + } + + public function addTranslation(CategoryTranslation $t) + { + if (!$this->translations->contains($t)) { + $this->translations[] = $t; + $t->setObject($this); + } + } + + public function getId() + { + return $this->id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function getDescription() + { + return $this->description; + } + + public function __toString() + { + return $this->getTitle(); + } +} +``` + +Now the translation entity for the Category: + +``` php +setLocale($locale); + $this->setField($field); + $this->setContent($value); + } + + /** + * @ORM\ManyToOne(targetEntity="Category", inversedBy="translations") + * @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE") + */ + protected $object; +} +``` + +Some example code to persist with translations: + +``` php +setTitle('Food'); +$food->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Maistas')); + +$fruits = new Entity\Category; +$fruits->setParent($food); +$fruits->setTitle('Fruits'); +$fruits->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Vaisiai')); +$fruits->addTranslation(new Entity\CategoryTranslation('ru', 'title', 'rus trans')); + +$em->persist($food); +$em->persist($fruits); +$em->flush(); +``` + +This would create translations for english and lithuanian, and for fruits, **ru** additionally. + +Easy like that, any suggestions on improvements are very welcome + + +### Example code to use Personal Translations with (Symfony2 Sonata) i18n Forms: + +Suppose you have a Sonata Backend with a simple form like: + +``` php +with('General') + ->add('title', 'text') + ->end() + ; +} +``` + +Then you can turn it into an i18n Form by providing the following changes. + +``` php +with('General') + ->add('title', 'translatable_field', array( + 'field' => 'title', + 'personal_translation' => 'ExampleBundle\Entity\Translation\ProductTranslation', + 'property_path' => 'translations', + )) + ->end() + ; +} + +``` + +To accomplish this you can add the following code in your bundle: + +https://gist.github.com/2437078 + +/Form/TranslatedFieldType.php +/Form/EventListener/addTranslatedFieldSubscriber.php +/Resources/services.yml + +Then you can change to your needs: + +``` php + 'field' => 'title', //you need to provide which field you wish to translate + 'personal_translation' => 'ExampleBundle\Entity\Translation\ProductTranslation', //the personal translation entity + +``` + + +### Translations field type using Personal Translations with Symfony2: + +You can use [A2lixTranslationFormBundle](https://github.com/a2lix/TranslationFormBundle) to facilitate your translations. diff --git a/vendor/gedmo/doctrine-extensions/doc/tree.md b/vendor/gedmo/doctrine-extensions/doc/tree.md new file mode 100644 index 0000000..2a1f26c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/tree.md @@ -0,0 +1,1339 @@ +# Tree - Nestedset behavior extension for Doctrine 2 + +**Tree** nested behavior will implement the standard Nested-Set behavior +on your Entity. Tree supports different strategies. Currently it supports +**nested-set**, **closure-table** and **materialized-path**. Also this behavior can be nested +with other extensions to translate or generated slugs of your tree nodes. + +Features: + +- Materialized Path strategy for ORM and ODM (MongoDB) +- Closure tree strategy, may be faster in some cases where ordering does not matter +- Support for multiple roots in nested-set +- No need for other managers, implementation is through event listener +- Synchronization of left, right values is automatic +- Can support concurrent flush with many objects being persisted and updated +- Can be nested with other extensions +- Annotation, Yaml and Xml mapping support for extensions + +Thanks for contributions to: + +- **[comfortablynumb](http://github.com/comfortablynumb) Gustavo Falco** for Closure and Materialized Path strategy +- **[everzet](http://github.com/everzet) Kudryashov Konstantin** for TreeLevel implementation +- **[stof](http://github.com/stof) Christophe Coevoet** for getTreeLeafs function + +Update **2018-02-26** + +- Nodes with no Parent can now be sorted based on a tree root id being an id from another table. Existing behaviour + is unchanged unless you add properties to the `@TreeRoot` annotation. Example: You have two categories with no parent, + horror and comedy, which are actually categories of 'Movie', which is in another table. Usually calling `moveUp()` or + `moveDown()` would be impossible, but now you can add `@TreeRoot(identifierMethod="getRoot")`, where `getRoot` is the + name of your class method returning the root id/entity. + + +Update **2017-04-22** + +- Added the `TreeObjectHydrator` class for building trees from entities + +Update **2012-06-28** + +- Added "buildTree" functionality support for Closure and Materialized Path strategies + +Update **2012-02-23** + +- Added a new strategy to support the "Materialized Path" tree model. It works with ODM (MongoDB) and ORM. + +Update **2011-05-07** + +- Tree is now able to act as **closure** tree, this strategy was refactored +and now fully functional. It is much faster for file-folder trees for instance +where you do not care about tree ordering. + +Update **2011-04-11** + +- Made in memory node synchronization, this change does not require clearing the cached nodes after any updates +to nodes, except **recover, verify and removeFromTree** operations. + +Update **2011-02-08** + +- Refactored to support multiple roots +- Changed the repository name, relevant to strategy used +- New [annotations](#annotations) were added + + +Update **2011-02-02** + +- Refactored the Tree to the ability on supporting different tree models +- Changed the repository location in order to support future updates + +**Note:** + +- After using a NestedTreeRepository functions: **verify, recover, removeFromTree** it is recommended to clear the EntityManager cache +because nodes may have changed values in database but not in memory. Flushing dirty nodes can lead to unexpected behaviour. +- Closure tree implementation is experimental and not fully functional, so far not documented either +- Public [Tree repository](http://github.com/Atlantic18/DoctrineExtensions "Tree extension on Github") is available on github +- Last update date: **2012-02-23** + +**Portability:** + +- **Tree** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) +ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions + +This article will cover the basic installation and functionality of **Tree** behavior + +Content: + +- [Including](#including-extension) the extension +- Tree [annotations](#annotations) +- Entity [example](#entity-mapping) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Basic usage [examples](#basic-examples) +- Build [html tree](#html-tree) +- Advanced usage [examples](#advanced-examples) +- [Materialized Path](#materialized-path) +- [Closure Table](#closure-table) +- [Repository methods (all strategies)](#repository-methods) + + + +## Setup and autoloading + +Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in the most optimized way. + + + +## Tree Entity example: + +**Note:** Node interface is not necessary, except in cases where +you need to identify and entity as being a Tree Node. The metadata is loaded only once when the +cache is activated + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getRoot() + { + return $this->root; + } + + public function setParent(Category $parent = null) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } +} +``` + + + +### Tree annotations: + +- **@Gedmo\Mapping\Annotation\Tree(type="strategy")** this **class annotation** sets the tree strategy by using the **type** parameter. +Currently **nested**, **closure** or **materializedPath** strategies are supported. An additional "activateLocking" parameter +is available if you use the "Materialized Path" strategy with MongoDB. It's used to activate the locking mechanism (more on that +in the corresponding section). +- **@Gedmo\Mapping\Annotation\TreeLeft** field is used to store the tree **left** value +- **@Gedmo\Mapping\Annotation\TreeRight** field is used to store the tree **right** value +- **@Gedmo\Mapping\Annotation\TreeParent** will identify the column as the relation to **parent node** +- **@Gedmo\Mapping\Annotation\TreeLevel** field is used to store the tree **level** +- **@Gedmo\Mapping\Annotation\TreeRoot** field is used to store the tree **root** id value or identify the column as the relation to **root node** +- **@Gedmo\Mapping\Annotation\TreePath** (Materialized Path only) field is used to store the **path**. It has an +optional parameter "separator" to define the separator used in the path. +- **@Gedmo\Mapping\Annotation\TreePathSource** (Materialized Path only) field is used as the source to + construct the "path" +- **@Gedmo\Mapping\Annotation\TreeLockTime** (Materialized Path - ODM MongoDB only) field is used if you need to +use the locking mechanism with MongoDB. It persists the lock time if a root node is locked (more on that in the corresponding +section). + + + +## Yaml mapping example + +Yaml mapped Category: **/mapping/yaml/Entity.Category.dcm.yml** + +``` +--- +Entity\Category: + type: entity + repositoryClass: Gedmo\Tree\Entity\Repository\NestedTreeRepository + table: categories + gedmo: + tree: + type: nested + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + lft: + type: integer + gedmo: + - treeLeft + rgt: + type: integer + gedmo: + - treeRight + lvl: + type: integer + gedmo: + - treeLevel + manyToOne: + root: + targetEntity: Entity\Category + joinColumn: + name: tree_root + referencedColumnName: id + onDelete: CASCADE + gedmo: + - treeRoot + parent: + targetEntity: Entity\Category + inversedBy: children + joinColumn: + name: parent_id + referencedColumnName: id + onDelete: CASCADE + gedmo: + - treeParent + oneToMany: + children: + targetEntity: Entity\Category + mappedBy: parent + orderBy: + lft: ASC +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Basic usage examples: + +### To save some **Categories** and generate tree: + +``` php +setTitle('Food'); + +$fruits = new Category(); +$fruits->setTitle('Fruits'); +$fruits->setParent($food); + +$vegetables = new Category(); +$vegetables->setTitle('Vegetables'); +$vegetables->setParent($food); + +$carrots = new Category(); +$carrots->setTitle('Carrots'); +$carrots->setParent($vegetables); + +$this->em->persist($food); +$this->em->persist($fruits); +$this->em->persist($vegetables); +$this->em->persist($carrots); +$this->em->flush(); +``` + +The result after flush will generate the food tree: + +``` +/food (1-8) + /fruits (2-3) + /vegetables (4-7) + /carrots (5-6) +``` + +### Using repository functions + +``` php +getRepository('Entity\Category'); + +$food = $repo->findOneByTitle('Food'); +echo $repo->childCount($food); +// prints: 3 +echo $repo->childCount($food, true/*direct*/); +// prints: 2 +$children = $repo->children($food); +// $children contains: +// 3 nodes +$children = $repo->children($food, false, 'title'); +// will sort the children by title +$carrots = $repo->findOneByTitle('Carrots'); +$path = $repo->getPath($carrots); +/* $path contains: + 0 => Food + 1 => Vegetables + 2 => Carrots +*/ + +// verification and recovery of tree +$repo->verify(); +// can return TRUE if tree is valid, or array of errors found on tree +$repo->recover(); +$em->flush(); // important: flush recovered nodes +// if tree has errors it will try to fix all tree nodes + +// UNSAFE: be sure to backup before running this method when necessary, if you can use $em->remove($node); +// which would cascade to children +// single node removal +$vegies = $repo->findOneByTitle('Vegetables'); +$repo->removeFromTree($vegies); +$em->clear(); // clear cached nodes +// it will remove this node from tree and reparent all children + +// reordering the tree +$food = $repo->findOneByTitle('Food'); +$repo->reorder($food, 'title'); +// it will reorder all "Food" tree node left-right values by the title +``` + +### Inserting node in different positions + +``` php +setTitle('Food'); + +$fruits = new Category(); +$fruits->setTitle('Fruits'); + +$vegetables = new Category(); +$vegetables->setTitle('Vegetables'); + +$carrots = new Category(); +$carrots->setTitle('Carrots'); + +$treeRepository + ->persistAsFirstChild($food) + ->persistAsFirstChildOf($fruits, $food) + ->persistAsLastChildOf($vegetables, $food) + ->persistAsNextSiblingOf($carrots, $fruits); + +$em->flush(); +``` + +For more details you can check the **NestedTreeRepository** __call function + +Moving up and down the nodes in same level: + +Tree example: + +``` +/Food + /Vegetables + /Onions + /Carrots + /Cabbages + /Potatoes + /Fruits +``` + +Now move **carrots** up by one position + +``` php +getRepository('Entity\Category'); +$carrots = $repo->findOneByTitle('Carrots'); +// move it up by one position +$repo->moveUp($carrots, 1); +``` + +Tree after moving the Carrots up: + +``` +/Food + /Vegetables + /Carrots <- moved up + /Onions + /Cabbages + /Potatoes + /Fruits +``` + +Moving **carrots** down to the last position + +``` php +getRepository('Entity\Category'); +$carrots = $repo->findOneByTitle('Carrots'); +// move it down to the end +$repo->moveDown($carrots, true); +``` + +Tree after moving the Carrots down as last child: + +``` +/Food + /Vegetables + /Onions + /Cabbages + /Potatoes + /Carrots <- moved down to the end + /Fruits +``` + +**Note:** the tree repository functions **verify, recover, removeFromTree** +will require you to clear the cache of the Entity Manager because left-right values will differ. +So after that use **$em->clear();** if you will continue using the nodes after these operations. + +### If you need a repository for your TreeNode Entity simply extend it + +``` php + + +## Create html tree: + +### Retrieving the whole tree as an array + +If you would like to load the whole tree as a node array hierarchy use: + +``` php +getRepository('Entity\Category'); +$arrayTree = $repo->childrenHierarchy(); +``` + +All node children are stored under the **__children** key for each node. + +### Retrieving as html tree + +To load a tree as a **ul - li** html tree use: + +``` php +getRepository('Entity\Category'); +$htmlTree = $repo->childrenHierarchy( + null, /* starting from root nodes */ + false, /* true: load all children, false: only direct */ + array( + 'decorate' => true, + 'representationField' => 'slug', + 'html' => true + ) +); +``` + +### Customize html tree output + +``` php +getRepository('Entity\Category'); +$options = array( + 'decorate' => true, + 'rootOpen' => '
    ', + 'rootClose' => '
', + 'childOpen' => '
  • ', + 'childClose' => '
  • ', + 'nodeDecorator' => function($node) { + return ''.$node[$field].''; + } +); +$htmlTree = $repo->childrenHierarchy( + null, /* starting from root nodes */ + false, /* true: load all children, false: only direct */ + $options +); + +``` + +### Generate your own node list + +``` php +getRepository('Entity\Category'); +$query = $entityManager + ->createQueryBuilder() + ->select('node') + ->from('Entity\Category', 'node') + ->orderBy('node.root, node.lft', 'ASC') + ->where('node.root = 1') + ->getQuery() +; +$options = array('decorate' => true); +$tree = $repo->buildTree($query->getArrayResult(), $options); +``` + +### Using routes in decorator, show only selected items, return unlimited levels items as 2 levels + +``` php +childrenHierarchy(null,false,array('decorate' => true, + 'rootOpen' => function($tree) { + if(count($tree) && ($tree[0]['lvl'] == 0)){ + return '
    '; + } + }, + 'rootClose' => function($child) { + if(count($child) && ($child[0]['lvl'] == 0)){ + return '
    '; + } + }, + 'childOpen' => '', + 'childClose' => '', + 'nodeDecorator' => function($node) use (&$controller) { + if($node['lvl'] == 1) { + return '

    '.$node['title'].'

    '; + }elseif($node["isVisibleOnHome"]) { + return '$node['id'])).'">'.$node['title'].' '; + } + } + )); +``` + + + +## Building trees from your entities + +You can use the `childrenHierarchy` method to build an array tree from your result set. +However, sometimes it is more convenient to work with the entities directly. The `TreeObjectHydrator` +lets you build a tree from your entities instead, without triggering any more queries. + +First, you have to register the hydrator in your Doctrine entity manager. + +```php +getConfiguration()->addCustomHydrationMode('tree', 'Gedmo\Tree\Hydrator\ORM\TreeObjectHydrator'); +``` + +The hydrator requires the `HINT_INCLUDE_META_COLUMNS` query hint. Without it the hydrator will not work! +Other than that, the usage is straight-forward. + +```php +getRepository('Entity\Category'); + +$tree = $repo->createQueryBuilder('node')->getQuery() + ->setHint(\Doctrine\ORM\Query::HINT_INCLUDE_META_COLUMNS, true) + ->getResult('tree'); +``` + +## Advanced examples: + +### Nesting Translatable and Sluggable extensions + +If you want to attach **TranslatableListener** and also add it to EventManager after +the **SluggableListener** and **TreeListener**. It is important because slug must be generated first +before the creation of it`s translation. + +``` php +addEventSubscriber($treeListener); +$sluggableListener = new \Gedmo\Sluggable\SluggableListener(); +$evm->addEventSubscriber($sluggableListener); +$translatableListener = new \Gedmo\Translatable\TranslatableListener(); +$translatableListener->setTranslatableLocale('en_us'); +$evm->addEventSubscriber($translatableListener); +// now this event manager should be passed to entity manager constructor +``` + +And the Entity should look like: + +``` php +id; + } + + public function getSlug() + { + return $this->slug; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function getRoot() + { + return $this->root; + } + + public function setParent(Category $parent) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } +} +``` + +Yaml mapped Category: **/mapping/yaml/Entity.Category.dcm.yml** + +``` +--- +Entity\Category: + type: entity + repositoryClass: Gedmo\Tree\Entity\Repository\NestedTreeRepository + table: categories + gedmo: + tree: + type: nested + id: + id: + type: integer + generator: + strategy: AUTO + fields: + title: + type: string + length: 64 + gedmo: + - translatable + - sluggable + lft: + type: integer + gedmo: + - treeLeft + rgt: + type: integer + gedmo: + - treeRight + lvl: + type: integer + gedmo: + - treeLevel + slug: + type: string + length: 128 + gedmo: + - translatable + - slug + manyToOne: + root: + targetEntity: Entity\Category + joinColumn: + name: tree_root + referencedColumnName: id + onDelete: CASCADE + gedmo: + - treeRoot + parent: + targetEntity: Entity\Category + inversedBy: children + joinColumn: + name: parent_id + referencedColumnName: id + onDelete: CASCADE + gedmo: + - treeParent + oneToMany: + children: + targetEntity: Entity\Category + mappedBy: parent +``` + +**Note:** If you use dql without object hydration, the nodes will not be +translated, because the postLoad event never will be triggered + +Now the generated treenode slug will be translated by Translatable behavior. + +It's as easy as that. Any suggestions on improvements are very welcome. + + + +## Materialized Path + +### Important notes before defining the schema + +- If you use MongoDB you should activate the locking mechanism provided to avoid inconsistencies in cases where concurrent +modifications on the tree could occur. Look at the MongoDB example of schema definition to see how it must be configured. +- If your **TreePathSource** field is of type "string", then the primary key will be concatenated in the form: "value-id". + This is to allow you to use non-unique values as the path source. For example, this could be very useful if you need to + use the date as the path source (maybe to create a tree of comments and order them by date). If you want to change this + default behaviour you can set the attribute "appendId" of **TreePath** to true or false. By default the path does not start + with the given separator but ends with it. You can customize this behaviour with "startsWithSeparator" and "endsWithSeparator". + `@Gedmo\TreePath(appendId=false, startsWithSeparator=true, endsWithSeparator=false)` +- **TreePath** field can only be of types: string, text +- **TreePathSource** field can only be of types: id, integer, smallint, bigint, string, int, float (I include here all the +variations of the field types, including the ORM and ODM for MongoDB ones). +- **TreeLockTime** must be of type "date" (used only in MongoDB for now). +- **TreePathHash** allows you to define a field that is automatically filled with the md5 hash of the path. This field could be necessary if you want to set a unique constraint on the database table. + +### ORM Entity example (Annotations) + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setParent(Category $parent = null) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + public function getLevel() + { + return $this->level; + } +} + +``` + +### MongoDB example (Annotations) + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setParent(Category $parent = null) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } + + public function getLevel() + { + return $this->level; + } + + public function getPath() + { + return $this->path; + } + + public function getLockTime() + { + return $this->lockTime; + } +} + +``` + +### MongoDB example (Yaml) +``` +YourNamespace\Document\Category: + type: mappedSuperclass + repositoryClass: Gedmo\Tree\Document\MongoDB\Repository\MaterializedPathRepository + collection: categories + gedmo: + tree: + type: materializedPath + activateLocking: true + fields: + id: + id: true + title: + type: string + gedmo: + - sluggable + slug: + type: string + gedmo: + 0: treePathSource + slug: + unique: false + fields: + - title + path: + type: string + gedmo: + treePath: + separator: '/' + appendId: false + startsWithSeparator: false # default + endsWithSeparator: true # default + level: + type: int + name: lvl + nullable: true + gedmo: + - treeLevel + lockTime: + type: date + gedmo: + - treeLockTime + hash: + type: string + gedmo: + - treePathHash + parent: + reference: true + type: one + inversedBy: children + targetDocument: YourNamespace\Document\Category + simple: true + gedmo: + - treeParent +``` + +### Path generation + +When an entity is inserted, a path is generated using the value of the field configured as the TreePathSource. +For example: + +``` php +$food = new Category(); +$food->setTitle('Food'); + +$em->persist($food); +$em->flush(); + +// This would print "Food-1" assuming the id is 1. +echo $food->getPath(); + +$fruits = new Category(); +$fruits->setTitle('Fruits'); +$fruits->setParent($food); + +$em->persist($fruits); +$em->flush(); + +// This would print "Food-1,Fruits-2" assuming that $food id is 1, +// $fruits id is 2 and separator = "," (the default value) +echo $fruits->getPath(); + +``` + +### Locking mechanism for MongoDB + +Why do we need a locking mechanism for MongoDB? Sadly, MongoDB lacks full transactional support, so if two or more +users try to modify the same tree concurrently, it could lead to an inconsistent tree. So we've implemented a simple +locking mechanism to avoid this type of problems. It works like this: As soon as a user tries to modify a node of a tree, +it first check if the root node is locked (or if the current lock has expired). + +If it is locked, then it throws an exception of type "Gedmo\Exception\TreeLockingException". If it's not locked, +it locks the tree and proceeds with the modification. After all the modifications are done, the lock is freed. + +If, for some reason, the lock couldn't get freed, there's a lock timeout configured with a default time of 3 seconds. +You can change this value using the **lockingTimeout** parameter under the Tree annotation (or equivalent in XML and YML). +You must pass a value in seconds to this parameter. + + + + +## Closure Table + +To be able to use this strategy, you'll need an additional entity which represents the closures. We already provide you an abstract +entity, so you only need to extend it. + +### Closure Entity + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setParent(Category $parent = null) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } + + public function addClosure(CategoryClosure $closure) + { + $this->closures[] = $closure; + } + + public function setLevel($level) + { + $this->level = $level; + } + + public function getLevel() + { + return $this->level; + } +} + +``` + +And that's it! + + + + +## Repository Methods (All strategies) + +There are repository methods that are available for you in all the strategies: + +* **getRootNodes** / **getRootNodesQuery** / **getRootNodesQueryBuilder**: Returns an array with the available root nodes. Arguments: + - *sortByField*: An optional field to order the root nodes. Defaults to "null". + - *direction*: In case the first argument is used, you can pass the direction here: "asc" or "desc". Defaults to "asc". +* **getChildren** / **getChildrenQuery** / **getChildrenQueryBuilder**: Returns an array of children nodes. Arguments: + - *node*: If you pass a node, the method will return its children. Defaults to "null" (this means it will return ALL nodes). + - *direct*: If you pass true as a value for this argument, you'll get only the direct children of the node + (or only the root nodes if you pass "null" to the "node" argument). + - *sortByField*: An optional field to sort the children. Defaults to "null". + - *direction*: If you use the "sortByField" argument, this allows you to set the direction: "asc" or "desc". Defaults to "asc". + - *includeNode*: Using "true", this argument allows you to include in the result the node you passed as the first argument. Defaults to "false". +* **childrenHierarchy**: This useful method allows you to build an array of nodes representing the hierarchy of a tree. Arguments: + - *node*: If you pass a node, the method will return its children. Defaults to "null" (this means it will return ALL nodes). + - *direct*: If you pass true as a value for this argument, you'll get only the direct children of the node + - *options*: An array of options that allows you to decorate the results with HTML. Available options: + * decorate: boolean (false) - retrieves tree as UL->LI tree + * nodeDecorator: Closure (null) - uses $node as argument and returns decorated item as string + * rootOpen: string || Closure ('\') - branch start, closure will be given $children as a parameter + * rootClose: string ('\') - branch close + * childStart: string || Closure ('\') - start of node, closure will be given $node as a parameter + * childClose: string ('\') - close of node + * childSort: array || keys allowed: field: field to sort on, dir: direction. 'asc' or 'desc' + - *includeNode*: Using "true", this argument allows you to include in the result the node you passed as the first argument. Defaults to "false". +* **setChildrenIndex** / **getChildrenIndex**: These methods allow you to change the default index used to hold the children when you use the **childrenHierarchy** method. Index defaults to "__children". + +This list is not complete yet. We're working on including more methods in the common API offered by repositories of all the strategies. +Soon we'll be adding more helpful methods here. diff --git a/vendor/gedmo/doctrine-extensions/doc/uploadable.md b/vendor/gedmo/doctrine-extensions/doc/uploadable.md new file mode 100644 index 0000000..9712bf2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/uploadable.md @@ -0,0 +1,467 @@ +# Uploadable behavior extension for Doctrine 2 + +**Uploadable** behavior provides the tools to manage the persistence of files with +Doctrine 2, including automatic handling of moving, renaming and removal of files and other features. + +Features: + +- Extension moves, removes and renames files according to configuration automatically +- Lots of options: Allow overwrite, append a number if file exists, filename generators, post-move callbacks, etc. +- It can be extended to work not only with uploaded files, but with files coming from any source (an URL, another + file in the same server, etc). +- Validation of size and mime type + +Content: + +- [Including](#including-extension) the extension +- Entity [example](#entity-mapping) +- [Yaml](#yaml-mapping) mapping example +- [Xml](#xml-mapping) mapping example +- Usage [examples](#usage) +- [Using](#additional-usages) the extension to handle not only uploaded files +- [Custom](#custom-mime-type-guessers) mime type guessers + + + +## Setup and autoloading + +Read the [documentation](http://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) +or check the [example code](http://github.com/l3pp4rd/DoctrineExtensions/tree/master/example) +on how to setup and use the extensions in most optimized way. + + + + +## Uploadable Entity example: + +### Uploadable annotations: +1. **@Gedmo\Mapping\Annotation\Uploadable** this class annotation tells if a class is Uploadable. Available configuration options: + * **allowOverwrite** - If this option is true, it will overwrite a file if it already exists. If you set "false", an + exception will be thrown. Default: false + * **appendNumber** - If this option is true and "allowOverwrite" is false, in the case that the file already exists, + it will append a number to the filename. Example: if you're uploading a file named "test.txt", if the file already + exists and this option is true, the extension will modify the name of the uploaded file to "test-1.txt", where "1" + could be any number. The extension will check if the file exists until it finds a filename with a number as its postfix that is not used. + If you use a filename generator and this option is true, it will append a number to the filename anyway if a file with + the same name already exists. + Default value: false + * **path** - This option expects a string containing the path where the files represented by this entity will be moved. + Default: "". Path can be set in other ways: From the listener or from a method. More details later. + * **pathMethod** - Similar to option "path", but this time it represents the name of a method on the entity that + will return the path to which the files represented by this entity will be moved. This is useful in several cases. + For example, you can set specific paths for specific entities, or you can get the path from other sources (like a + framework configuration) instead of hardcoding it in the entity. Default: "". As first argument this method takes + default path, so you can return path relative to default. + * **callback** - This option allows you to set a method name. If this option is set, the method will be called after + the file is moved. Default value: "". As first argument, this method can receive an array with information about the uploaded file, which + includes the following keys: + 1. **fileName**: The filename. + 2. **fileExtension**: The extension of the file (including the dot). Example: .jpg + 3. **fileWithoutExt**: The filename without the extension. + 4. **filePath**: The file path. Example: /my/path/filename.jpg + 5. **fileMimeType**: The mime-type of the file. Example: text/plain. + 6. **fileSize**: Size of the file in bytes. Example: 140000. + * **filenameGenerator**: This option allows you to set a filename generator for the file. There are two already included + by the extension: **SHA1**, which generates a sha1 filename for the file, and **ALPHANUMERIC**, which "normalizes" + the filename, leaving only alphanumeric characters in the filename, and replacing anything else with a "-". You can + even create your own FilenameGenerator class (implementing the Gedmo\Uploadable\FilenameGenerator\FilenameGeneratorInterface) and set this option with the + fully qualified class name. The other option available is "NONE" which, as you may guess, means no generation for the + filename will occur. Default: "NONE". + * **maxSize**: This option allows you to set a maximum size for the file in bytes. If file size exceeds the value + set in this configuration, an exception of type "UploadableMaxSizeException" will be thrown. By default, its value is set to 0, meaning + that no size validation will occur. + * **allowedTypes**: With this option you can set a comma-separated list of allowed mime types for the file. The extension + will use a simple mime type guesser to guess the file type, and then it will compare it to the list of allowed types. + If the mime type is not valid, then an exception of type "UploadableInvalidMimeTypeException" will be thrown. If you + set this option, you can't set the **disallowedTypes** option described next. By default, no validation of mime type + occurs. If you want to use a custom mime type guesser, see [this](#custom-mime-type-guessers). + * **disallowedTypes**: Similar to the option **allowedTypes**, but with this one you configure a "black list" of + mime types. If the mime type of the file is on this list, n exception of type "UploadableInvalidMimeTypeException" will be thrown. If you + set this option, you can't set the **allowedTypes** option described above. By default, no validation of mime type + occurs. If you want to use a custom mime type guesser, see [this](#custom-mime-type-guessers). +2. **@Gedmo\Mapping\Annotation\UploadableFilePath**: This annotation is used to set which field will receive the path + to the file. The field MUST be of type "string". Either this one or UploadableFileName annotation is REQUIRED to be set. +3. **@Gedmo\Mapping\Annotation\UploadableFileName**: This annotation is used to set which field will receive the name + of the file. The field MUST be of type "string". Either this one or UploadableFilePath annotation is REQUIRED to be set. +4. **@Gedmo\Mapping\Annotation\UploadableFileMimeType**: This is an optional annotation used to set which field will + receive the mime type of the file as its value. This field MUST be of type "string". +5. **@Gedmo\Mapping\Annotation\UploadableFileSize**: This is an optional annotation used to set which field will + receive the size in bytes of the file as its value. This field MUST be of type "decimal". + +### Notes about setting the path where the files will be moved: + +You have three choices to configure the path. You can set a default path on the listener, which will be used on every +entity which doesn't have a path or pathMethod defined: + +``` php +$listener->setDefaultPath('/my/path'); +``` + +You can use the Uploadable "path" option to set the path: + +``` php +/** + * @ORM\Entity + * @Gedmo\Uploadable(path="/my/path") + */ +class File +{ + //... +} +``` + +Or you can use the Uploadable "pathMethod" option to set the name of the method which will return the path: + +``` php +/** + * @ORM\Entity + * @Gedmo\Uploadable(pathMethod="getPath") + */ +class File +{ + public function getPath() + { + return '/my/path'; + } +} +``` + + +### Note regarding the Uploadable interface: + +The Uploadable interface is not necessary, except in cases there +you need to identify an entity as Uploadable. The metadata is loaded only once then +you need to identify an entity as Uploadable. The metadata is loaded only once then +cache is activated + +### Minimum configuration needed: + +``` php + + +## Yaml mapping example: + +Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** + +``` +--- +Entity\File: + type: entity + table: files + gedmo: + uploadable: + allowOverwrite: true + appendNumber: true + path: '/my/path' + pathMethod: getPath + callback: callbackMethod + filenameGenerator: SHA1 + id: + id: + type: integer + generator: + strategy: AUTO + fields: + path: + type: string + gedmo: + - uploadableFilePath + name: + type: string + gedmo: + - uploadableFileName + mimeType: + type: string + gedmo: + - uploadableFileMimeType + size: + type: decimal + gedmo: + - uploadableFileSize +``` + + + +## Xml mapping example + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + + + +## Usage: + +``` php +setDefaultPath('/my/app/web/upload'); + +if (isset($_FILES['images']) && is_array($_FILES['images'])) { + foreach ($_FILES['images'] as $fileInfo) { + $file = new File(); + + $listener->addEntityFileInfo($file, $fileInfo); + + // You can set the file info directly with a FileInfoInterface object, like this: + // + // $listener->addEntityFileInfo($file, new FileInfoArray($fileInfo)); + // + // Or create your own class which implements FileInfoInterface + // + // $listener->addEntityFileInfo($file, new MyOwnFileInfo($fileInfo)); + + + $em->persist($file); + } +} + +$em->flush(); +``` + +Easy like that, any suggestions on improvements are very welcome. + + + +### Using the extension to handle not only uploaded files + +Maybe you want to handle files obtained from an URL, or even files that are already located in the same server than your app. +This can be handled in a very simple way. First, you need to create a class that implements the FileInfoInterface +interface. As an example: + +``` php +use Gedmo\Uploadable\FileInfo\FileInfoInterface; + +class CustomFileInfo implements FileInfoInterface +{ + protected $path; + protected $size; + protected $type; + protected $filename; + protected $error = 0; + + public function __construct($path) + { + $this->path = $path; + + // Now, process the file and fill the rest of the properties. + } + + // This returns the actual path of the file + public function getTmpName() + { + return $path; + } + + // This returns the filename + public function getName() + { + return $this->name; + } + + // This returns the file size in bytes + public function getSize() + { + return $this->size; + } + + // This returns the mime type + public function getType() + { + return $this->type; + } + + public function getError() + { + // This should return 0, as it's only used to return the codes from PHP file upload errors. + return $this->error; + } + + // If this method returns true, it will produce that the extension uses "move_uploaded_file" function to move + // the file. If it returns false, the extension will use the "copy" function. + public function isUploadedFile() + { + return false; + } +} +``` + +Or you could simply extend the FileInfoArray class and do the following: + +``` php +use Gedmo\Uploadable\FileInfo\FileInfoArray; + +class CustomFileInfo extends FileInfoArray +{ + public function __construct($path) + { + // There's already a $fileInfo property, which needs to be an array with the + // following keys: tmp_name, name, size, type, error + $this->fileInfo = array( + 'tmp_name' => '', + 'name' => '', + 'size' => 0, + 'type' => '', + 'error' => 0 + ); + + // Now process the file at $path and fill the keys with the correct values. + // + // In this example we use a $path as the first argument, but it could be an URL + // to the file we need to obtain, etc. + } + + public function isUploadedFile() + { + // Remember to set this to false so we use "copy" instead of "move_uploaded_file" + + return false; + } +} +``` + +And that's it. Then, instead of getting the file info from the $_FILES array, you would do: + +``` php +// We set the default path in the listener again +$listener->setDefaultPath('/my/path'); + +$file = new File(); + +$listener->addEntityFileInfo($file, new CustomFileInfo('/path/to/file.txt')); + +$em->persist($file); +$em->flush(); +``` + + + +### Custom Mime type guessers + +If you want to use your own mime type guesser, you need to implement the interface "Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface", +which has only one method: "guess($filePath)". Then, you can set the mime type guesser used on the listener in the following +way: + +``` php +$listener->setMimeTypeGuesser(new MyCustomMimeTypeGuesser()); + +``` diff --git a/vendor/gedmo/doctrine-extensions/doc/zendframework2.md b/vendor/gedmo/doctrine-extensions/doc/zendframework2.md new file mode 100644 index 0000000..4eec20e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/doc/zendframework2.md @@ -0,0 +1,97 @@ +## Using Gedmo Doctrine Extensions in Zend Framework 2 + +Assuming you are familiar with [DoctrineModule](https://github.com/doctrine/DoctrineModule) (if not, you should definitely start there!), integrating Doctrine Extensions with Zend Framework 2 application is super-easy. + +### Composer + +Add DoctrineModule, DoctrineORMModule and DoctrineExtensions to composer.json file: + +```json +{ + "require": { + "php": ">=5.3.3", + "zendframework/zendframework": "2.1.*", + "doctrine/doctrine-module": "0.*", + "doctrine/doctrine-orm-module": "0.*", + "gedmo/doctrine-extensions": "2.3.*", + } +} +``` + +Then run `composer.phar update`. + +### Configuration + +Once libraries are installed, you can tell Doctrine which behaviors you want to use, by declaring appropriate subscribers in Event Manager settings. Together with [entity mapping options](https://github.com/doctrine/DoctrineORMModule#entities-settings), your module configuration file should look like following: + +```php +return array( + 'doctrine' => array( + 'eventmanager' => array( + 'orm_default' => array( + 'subscribers' => array( + + // pick any listeners you need + 'Gedmo\Tree\TreeListener', + 'Gedmo\Timestampable\TimestampableListener', + 'Gedmo\Sluggable\SluggableListener', + 'Gedmo\Loggable\LoggableListener', + 'Gedmo\Sortable\SortableListener' + ), + ), + ), + 'driver' => array( + 'my_driver' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array(__DIR__ . '/../src/MyModule/Entity') + ), + 'orm_default' => array( + 'drivers' => array( + 'MyModule\Entity' => 'my_driver' + ), + ), + ), + ), +); +``` + +That's it! From now on you can use Gedmo annotations, just as it is described in [documentation](https://github.com/mtymek/DoctrineExtensions/blob/master/doc/annotations.md). + +#### Note: You may need to provide additional settings for some of the available listeners. + +For instance, `Translatable` requires additional metadata driver in order to manage translation tables: + +```php +return array( + 'doctrine' => array( + 'eventmanager' => array( + 'orm_default' => array( + 'subscribers' => array( + 'Gedmo\Translatable\TranslatableListener', + ), + ), + ), + 'driver' => array( + 'my_driver' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array(__DIR__ . '/../src/MyModule/Entity') + ), + 'translatable_metadata_driver' => array( + 'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver', + 'cache' => 'array', + 'paths' => array( + 'vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity', + ), + ), + 'orm_default' => array( + 'drivers' => array( + 'MyModule\Entity' => 'my_driver', + 'Gedmo\Translatable\Entity' => 'translatable_metadata_driver', + ), + ), + ), + ), +); +``` diff --git a/vendor/gedmo/doctrine-extensions/example/app/Entity/Category.php b/vendor/gedmo/doctrine-extensions/example/app/Entity/Category.php new file mode 100644 index 0000000..c8e2df1 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/example/app/Entity/Category.php @@ -0,0 +1,219 @@ +children = new ArrayCollection(); + $this->translations = new ArrayCollection(); + } + + public function getTranslations() + { + return $this->translations; + } + + public function addTranslation(CategoryTranslation $t) + { + if (!$this->translations->contains($t)) { + $this->translations[] = $t; + $t->setObject($this); + } + } + + public function getSlug() + { + return $this->slug; + } + + public function getId() + { + return $this->id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + + public function setDescription($description) + { + $this->description = $description; + } + + public function getDescription() + { + return $this->description; + } + + public function setParent($parent) + { + $this->parent = $parent; + } + + public function getParent() + { + return $this->parent; + } + + public function getRoot() + { + return $this->root; + } + + public function getLevel() + { + return $this->level; + } + + public function getChildren() + { + return $this->children; + } + + public function getLeft() + { + return $this->lft; + } + + public function getRight() + { + return $this->rgt; + } + + public function getCreated() + { + return $this->created; + } + + public function getUpdated() + { + return $this->updated; + } + + public function getCreatedBy() + { + return $this->createdBy; + } + + public function getUpdatedBy() + { + return $this->updatedBy; + } + + public function __toString() + { + return $this->getTitle(); + } +} diff --git a/vendor/gedmo/doctrine-extensions/example/app/Entity/CategoryTranslation.php b/vendor/gedmo/doctrine-extensions/example/app/Entity/CategoryTranslation.php new file mode 100644 index 0000000..27182e2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/example/app/Entity/CategoryTranslation.php @@ -0,0 +1,37 @@ +setLocale($locale); + $this->setField($field); + $this->setContent($value); + } + + /** + * @ORM\ManyToOne(targetEntity="Category", inversedBy="translations") + * @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE") + */ + protected $object; +} diff --git a/vendor/gedmo/doctrine-extensions/example/app/Entity/Repository/CategoryRepository.php b/vendor/gedmo/doctrine-extensions/example/app/Entity/Repository/CategoryRepository.php new file mode 100644 index 0000000..4a143c8 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/example/app/Entity/Repository/CategoryRepository.php @@ -0,0 +1,9 @@ +run(); + diff --git a/vendor/gedmo/doctrine-extensions/example/bin/console.php b/vendor/gedmo/doctrine-extensions/example/bin/console.php new file mode 100644 index 0000000..9f9ca17 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/example/bin/console.php @@ -0,0 +1,38 @@ +setCatchExceptions(true); +// commands +$cli->addCommands(array( + // DBAL Commands + new Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + + // ORM Commands + new Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(), + new Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(), + new Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(), + new Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(), + new Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(), + new Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(), + new Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(), + new Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(), + new Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(), + new Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(), + new Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(), + new Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(), + new Doctrine\ORM\Tools\Console\Command\RunDqlCommand(), + new Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(), +)); +// helpers +$helpers = array( + 'db' => new Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()), + 'em' => new Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em), +); +foreach ($helpers as $name => $helper) { + $cli->getHelperSet()->set($helper, $name); +} + +return $cli; diff --git a/vendor/gedmo/doctrine-extensions/example/em.php b/vendor/gedmo/doctrine-extensions/example/em.php new file mode 100644 index 0000000..dd8831f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/example/em.php @@ -0,0 +1,122 @@ + '127.0.0.1', + 'port' => 3306, + 'user' => 'root', + 'password' => null, + 'dbname' => 'test', + 'driver' => 'pdo_mysql', +); +if (!file_exists(__DIR__.'/../vendor/autoload.php')) { + die('cannot find vendors, read README.md how to use composer'); +} +// First of all autoloading of vendors +$loader = require __DIR__.'/../vendor/autoload.php'; + +// gedmo extensions +$loader->add('Gedmo', __DIR__.'/../lib'); + +// autoloader for Entity namespace +$loader->add('Entity', __DIR__.'/app'); + +// ensure standard doctrine annotations are registered +Doctrine\Common\Annotations\AnnotationRegistry::registerFile( + __DIR__.'/../vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php' +); + +// Second configure ORM +// globally used cache driver, in production use APC or memcached +$cache = new Doctrine\Common\Cache\ArrayCache(); +// standard annotation reader +$annotationReader = new Doctrine\Common\Annotations\AnnotationReader(); +$cachedAnnotationReader = new Doctrine\Common\Annotations\CachedReader( + $annotationReader, // use reader + $cache // and a cache driver +); +// create a driver chain for metadata reading +$driverChain = new Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain(); +// load superclass metadata mapping only, into driver chain +// also registers Gedmo annotations.NOTE: you can personalize it +Gedmo\DoctrineExtensions::registerAbstractMappingIntoDriverChainORM( + $driverChain, // our metadata driver chain, to hook into + $cachedAnnotationReader // our cached annotation reader +); + +// now we want to register our application entities, +// for that we need another metadata driver used for Entity namespace +$annotationDriver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver( + $cachedAnnotationReader, // our cached annotation reader + array(__DIR__.'/app/Entity') // paths to look in +); +// NOTE: driver for application Entity can be different, Yaml, Xml or whatever +// register annotation driver for our application Entity fully qualified namespace +$driverChain->addDriver($annotationDriver, 'Entity'); + +// general ORM configuration +$config = new Doctrine\ORM\Configuration(); +$config->setProxyDir(sys_get_temp_dir()); +$config->setProxyNamespace('Proxy'); +$config->setAutoGenerateProxyClasses(false); // this can be based on production config. +// register metadata driver +$config->setMetadataDriverImpl($driverChain); +// use our allready initialized cache driver +$config->setMetadataCacheImpl($cache); +$config->setQueryCacheImpl($cache); + +// Third, create event manager and hook prefered extension listeners +$evm = new Doctrine\Common\EventManager(); +// gedmo extension listeners + +// sluggable +$sluggableListener = new Gedmo\Sluggable\SluggableListener(); +// you should set the used annotation reader to listener, to avoid creating new one for mapping drivers +$sluggableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($sluggableListener); + +// tree +$treeListener = new Gedmo\Tree\TreeListener(); +$treeListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($treeListener); + +// loggable, not used in example +//$loggableListener = new Gedmo\Loggable\LoggableListener; +//$loggableListener->setAnnotationReader($cachedAnnotationReader); +//$loggableListener->setUsername('admin'); +//$evm->addEventSubscriber($loggableListener); + +// timestampable +$timestampableListener = new Gedmo\Timestampable\TimestampableListener(); +$timestampableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($timestampableListener); + +// blameable + +$blameableListener = new \Gedmo\Blameable\BlameableListener(); +$blameableListener->setAnnotationReader($cachedAnnotationReader); +$blameableListener->setUserValue('MyUsername'); // determine from your environment +$evm->addEventSubscriber($blameableListener); + +// translatable +$translatableListener = new Gedmo\Translatable\TranslatableListener(); +// current translation locale should be set from session or hook later into the listener +// most important, before entity manager is flushed +$translatableListener->setTranslatableLocale('en'); +$translatableListener->setDefaultLocale('en'); +$translatableListener->setAnnotationReader($cachedAnnotationReader); +$evm->addEventSubscriber($translatableListener); + +// sortable, not used in example +//$sortableListener = new Gedmo\Sortable\SortableListener; +//$sortableListener->setAnnotationReader($cachedAnnotationReader); +//$evm->addEventSubscriber($sortableListener); + +// mysql set names UTF-8 if required +$evm->addEventSubscriber(new Doctrine\DBAL\Event\Listeners\MysqlSessionInit()); +// Finally, create entity manager +return Doctrine\ORM\EntityManager::create($connection, $config, $evm); diff --git a/vendor/gedmo/doctrine-extensions/example/run.php b/vendor/gedmo/doctrine-extensions/example/run.php new file mode 100644 index 0000000..90046ea --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/example/run.php @@ -0,0 +1,79 @@ +getRepository('Entity\Category'); +$food = $repository->findOneByTitle('Food'); +if (!$food) { + // lets create some categories + $food = new Entity\Category(); + $food->setTitle('Food'); + $food->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Maistas')); + + $fruits = new Entity\Category(); + $fruits->setParent($food); + $fruits->setTitle('Fruits'); + $fruits->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Vaisiai')); + + $apple = new Entity\Category(); + $apple->setParent($fruits); + $apple->setTitle('Apple'); + $apple->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Obuolys')); + + $milk = new Entity\Category(); + $milk->setParent($food); + $milk->setTitle('Milk'); + $milk->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Pienas')); + + $em->persist($food); + $em->persist($milk); + $em->persist($fruits); + $em->persist($apple); + $em->flush(); +} + +// create query to fetch tree nodes +$query = $em + ->createQueryBuilder() + ->select('node') + ->from('Entity\Category', 'node') + ->orderBy('node.root, node.lft', 'ASC') + ->getQuery() +; +// set hint to translate nodes +$query->setHint( + Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, + 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' +); +$treeDecorationOptions = array( + 'decorate' => true, + 'rootOpen' => '', + 'rootClose' => '', + 'childOpen' => '', + 'childClose' => '', + 'nodeDecorator' => function ($node) { + return str_repeat('-', $node['level']).$node['title'].PHP_EOL; + }, +); +// build tree in english +echo $repository->buildTree($query->getArrayResult(), $treeDecorationOptions).PHP_EOL.PHP_EOL; +// change locale +$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'lt'); +// build tree in lithuanian +echo $repository->buildTree($query->getArrayResult(), $treeDecorationOptions).PHP_EOL.PHP_EOL; + +$ms = round(microtime(true) - $executionStart, 4) * 1000; +$mem = round((memory_get_usage(true) - $memoryStart) / 1000000, 2); +echo "Execution took: {$ms} ms, memory consumed: {$mem} Mb"; diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/AbstractTrackingListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/AbstractTrackingListener.php new file mode 100644 index 0000000..f6b80be --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/AbstractTrackingListener.php @@ -0,0 +1,227 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class AbstractTrackingListener extends MappedEventSubscriber +{ + /** + * Specifies the list of events to listen + * + * @return array + */ + public function getSubscribedEvents() + { + return array( + 'prePersist', + 'onFlush', + 'loadClassMetadata', + ); + } + + /** + * Maps additional metadata for the Entity + * + * @param EventArgs $eventArgs + * + * @return void + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * Looks for Timestampable objects being updated + * to update modification date + * + * @param EventArgs $args + * + * @return void + */ + public function onFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + // check all scheduled updates + $all = array_merge($ea->getScheduledObjectInsertions($uow), $ea->getScheduledObjectUpdates($uow)); + foreach ($all as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if (!$config = $this->getConfiguration($om, $meta->name)) { + continue; + } + $changeSet = $ea->getObjectChangeSet($uow, $object); + $needChanges = false; + + if ($uow->isScheduledForInsert($object) && isset($config['create'])) { + foreach ($config['create'] as $field) { + // Field can not exist in change set, when persisting embedded document without parent for example + $new = array_key_exists($field, $changeSet) ? $changeSet[$field][1] : false; + if ($new === null) { // let manual values + $needChanges = true; + $this->updateField($object, $ea, $meta, $field); + } + } + } + + if (isset($config['update'])) { + foreach ($config['update'] as $field) { + $isInsertAndNull = $uow->isScheduledForInsert($object) + && array_key_exists($field, $changeSet) + && $changeSet[$field][1] === null; + if (!isset($changeSet[$field]) || $isInsertAndNull) { // let manual values + $needChanges = true; + $this->updateField($object, $ea, $meta, $field); + } + } + } + + if (!$uow->isScheduledForInsert($object) && isset($config['change'])) { + foreach ($config['change'] as $options) { + if (isset($changeSet[$options['field']])) { + continue; // value was set manually + } + + if (!is_array($options['trackedField'])) { + $singleField = true; + $trackedFields = array($options['trackedField']); + } else { + $singleField = false; + $trackedFields = $options['trackedField']; + } + + foreach ($trackedFields as $trackedField) { + $trackedChild = null; + $tracked = null; + $parts = explode('.', $trackedField); + if (isset($parts[1])) { + $tracked = $parts[0]; + $trackedChild = $parts[1]; + } + + if (!isset($tracked) || array_key_exists($trackedField, $changeSet)) { + $tracked = $trackedField; + $trackedChild = null; + } + + if (isset($changeSet[$tracked])) { + $changes = $changeSet[$tracked]; + if (isset($trackedChild)) { + $changingObject = $changes[1]; + if (!is_object($changingObject)) { + throw new UnexpectedValueException( + "Field - [{$tracked}] is expected to be object in class - {$meta->name}" + ); + } + $objectMeta = $om->getClassMetadata(get_class($changingObject)); + $om->initializeObject($changingObject); + $value = $objectMeta->getReflectionProperty($trackedChild)->getValue($changingObject); + } else { + $value = $changes[1]; + } + + if (($singleField && in_array($value, (array) $options['value'])) || $options['value'] === null) { + $needChanges = true; + $this->updateField($object, $ea, $meta, $options['field']); + } + } + } + } + } + + if ($needChanges) { + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + } + } + } + + /** + * Checks for persisted Timestampable objects + * to update creation and modification dates + * + * @param EventArgs $args + * + * @return void + */ + public function prePersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + if ($config = $this->getConfiguration($om, $meta->getName())) { + if (isset($config['update'])) { + foreach ($config['update'] as $field) { + if ($meta->getReflectionProperty($field)->getValue($object) === null) { // let manual values + $this->updateField($object, $ea, $meta, $field); + } + } + } + if (isset($config['create'])) { + foreach ($config['create'] as $field) { + if ($meta->getReflectionProperty($field)->getValue($object) === null) { // let manual values + $this->updateField($object, $ea, $meta, $field); + } + } + } + } + } + + /** + * Get value for update field + * + * @param ClassMetadata $meta + * @param string $field + * @param AdapterInterface $eventAdapter + */ + abstract protected function getFieldValue($meta, $field, $eventAdapter); + + /** + * Updates a field + * + * @param object $object + * @param AdapterInterface $eventAdapter + * @param ClassMetadata $meta + * @param string $field + */ + protected function updateField($object, $eventAdapter, $meta, $field) + { + /** @var \Doctrine\Orm\Mapping\ClassMetadata|\Doctrine\ODM\MongoDB\Mapping\ClassMetadata $meta */ + $property = $meta->getReflectionProperty($field); + $oldValue = $property->getValue($object); + $newValue = $this->getFieldValue($meta, $field, $eventAdapter); + + // if field value is reference, persist object + if ($meta->hasAssociation($field) && is_object($newValue) && !$eventAdapter->getObjectManager()->contains($newValue)) { + $uow = $eventAdapter->getObjectManager()->getUnitOfWork(); + + // Check to persist only when the entity isn't already managed, persists always for MongoDB + if(!($uow instanceof UnitOfWork) || $uow->getEntityState($newValue) !== UnitOfWork::STATE_MANAGED) { + $eventAdapter->getObjectManager()->persist($newValue); + } + } + + $property->setValue($object, $newValue); + + if ($object instanceof NotifyPropertyChanged) { + $uow = $eventAdapter->getObjectManager()->getUnitOfWork(); + $uow->propertyChanged($object, $field, $oldValue, $newValue); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Blameable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Blameable.php new file mode 100644 index 0000000..251b028 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Blameable.php @@ -0,0 +1,50 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Blameable +{ + // blameable expects annotations on properties + + /** + * @gedmo:Blameable(on="create") + * fields which should be updated on insert only + */ + + /** + * @gedmo:Blameable(on="update") + * fields which should be updated on update and insert + */ + + /** + * @gedmo:Blameable(on="change", field="field", value="value") + * fields which should be updated on changed "property" + * value and become equal to given "value" + */ + + /** + * @gedmo:Blameable(on="change", field="field") + * fields which should be updated on changed "property" + */ + + /** + * @gedmo:Blameable(on="change", fields={"field1", "field2"}) + * fields which should be updated if at least one of the given fields changed + */ + + /** + * example + * + * @gedmo:Blameable(on="create") + * @Column(type="string") + * $created + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php new file mode 100644 index 0000000..6cdd09e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php @@ -0,0 +1,72 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class BlameableListener extends AbstractTrackingListener +{ + protected $user; + + /** + * Get the user value to set on a blameable field + * + * @param object $meta + * @param string $field + * + * @return mixed + */ + public function getFieldValue($meta, $field, $eventAdapter) + { + if ($meta->hasAssociation($field)) { + if (null !== $this->user && ! is_object($this->user)) { + throw new InvalidArgumentException("Blame is reference, user must be an object"); + } + + return $this->user; + } + + // ok so its not an association, then it is a string + if (is_object($this->user)) { + if (method_exists($this->user, 'getUsername')) { + return (string) $this->user->getUsername(); + } + if (method_exists($this->user, '__toString')) { + return $this->user->__toString(); + } + throw new InvalidArgumentException("Field expects string, user must be a string, or object should have method getUsername or __toString"); + } + + return $this->user; + } + + /** + * Set a user value to return + * + * @param mixed $user + */ + public function setUserValue($user) + { + $this->user = $user; + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..03e246d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Annotation.php @@ -0,0 +1,86 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation field is blameable + */ + const BLAMEABLE = 'Gedmo\\Mapping\\Annotation\\Blameable'; + + /** + * List of types which are valid for blame + * + * @var array + */ + protected $validTypes = array( + 'one', + 'string', + 'int', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + if ($blameable = $this->reader->getPropertyAnnotation($property, self::BLAMEABLE)) { + $field = $property->getName(); + + if (!$meta->hasField($field) && !$meta->hasAssociation($field)) { + throw new InvalidMappingException("Unable to find blameable [{$field}] as mapped property in entity - {$meta->name}"); + } + if ($meta->hasField($field)) { + if ( !$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' or a one-to-many relation in class - {$meta->name}"); + } + } else { + // association + if (! $meta->isSingleValuedAssociation($field)) { + throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}"); + } + } + if (!in_array($blameable->on, array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + if ($blameable->on == 'change') { + if (!isset($blameable->field)) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + if (is_array($blameable->field) && isset($blameable->value)) { + throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $blameable->field, + 'value' => $blameable->value, + ); + } + // properties are unique and mapper checks that, no risk here + $config[$blameable->on][] = $field; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..091de54 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Xml.php @@ -0,0 +1,129 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * List of types which are valid for blame + * + * @var array + */ + private $validTypes = array( + 'one', + 'string', + 'int', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $mapping + */ + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping->field)) { + /** + * @var \SimpleXmlElement $fieldMapping + */ + foreach ($mapping->field as $fieldMapping) { + $fieldMappingDoctrine = $fieldMapping; + $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($fieldMapping->blameable)) { + /** + * @var \SimpleXmlElement $data + */ + $data = $fieldMapping->blameable; + + $field = $this->_getAttribute($fieldMappingDoctrine, 'name'); + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' or a reference in class - {$meta->name}"); + } + if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($this->_getAttribute($data, 'on') == 'change') { + if (!$this->_isAttributeSet($data, 'field')) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $this->_getAttribute($data, 'field'); + $valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$this->_getAttribute($data, 'on')][] = $field; + } + } + } + + if (isset($mapping->{'many-to-one'})) { + foreach ($mapping->{'many-to-one'} as $fieldMapping) { + $field = $this->_getAttribute($fieldMapping, 'field'); + $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($fieldMapping->blameable)) { + $data = $fieldMapping->blameable; + if (! $meta->isSingleValuedAssociation($field)) { + throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}"); + } + if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($this->_getAttribute($data, 'on') == 'change') { + if (!$this->_isAttributeSet($data, 'field')) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $this->_getAttribute($data, 'field'); + $valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$this->_getAttribute($data, 'on')][] = $field; + } + } + } + } + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..56fb9b5 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Yaml.php @@ -0,0 +1,129 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * List of types which are valid for blameable + * + * @var array + */ + private $validTypes = array( + 'one', + 'string', + 'int', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo']['blameable'])) { + $mappingProperty = $fieldMapping['gedmo']['blameable']; + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' or a reference in class - {$meta->name}"); + } + if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($mappingProperty['on'] == 'change') { + if (!isset($mappingProperty['field'])) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $mappingProperty['field']; + $valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$mappingProperty['on']][] = $field; + } + } + } + + if (isset($mapping['manyToOne'])) { + foreach ($mapping['manyToOne'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo']['blameable'])) { + $mappingProperty = $fieldMapping['gedmo']['blameable']; + if (! $meta->isSingleValuedAssociation($field)) { + throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}"); + } + if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($mappingProperty['on'] == 'change') { + if (!isset($mappingProperty['field'])) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $mappingProperty['field']; + $valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$mappingProperty['on']][] = $field; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + /** + * Checks if $field type is valid + * + * @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..298d2fa --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements BlameableAdapter +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..226dec7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements BlameableAdapter +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/BlameableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/BlameableAdapter.php new file mode 100644 index 0000000..5bed21d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/BlameableAdapter.php @@ -0,0 +1,16 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface BlameableAdapter extends AdapterInterface +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/Blameable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/Blameable.php new file mode 100644 index 0000000..88bfb91 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/Blameable.php @@ -0,0 +1,68 @@ += 5.4 + * + * @author David Buchmann + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait Blameable +{ + /** + * @var string + */ + private $createdBy; + + /** + * @var string + */ + private $updatedBy; + + /** + * Sets createdBy. + * + * @param string $createdBy + * @return $this + */ + public function setCreatedBy($createdBy) + { + $this->createdBy = $createdBy; + + return $this; + } + + /** + * Returns createdBy. + * + * @return string + */ + public function getCreatedBy() + { + return $this->createdBy; + } + + /** + * Sets updatedBy. + * + * @param string $updatedBy + * @return $this + */ + public function setUpdatedBy($updatedBy) + { + $this->updatedBy = $updatedBy; + + return $this; + } + + /** + * Returns updatedBy. + * + * @return string + */ + public function getUpdatedBy() + { + return $this->updatedBy; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableDocument.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableDocument.php new file mode 100644 index 0000000..e58d882 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableDocument.php @@ -0,0 +1,75 @@ += 5.4 + * + * @author David Buchmann + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait BlameableDocument +{ + /** + * @var string + * @Gedmo\Blameable(on="create") + * @ODM\Field(type="string") + */ + protected $createdBy; + + /** + * @var string + * @Gedmo\Blameable(on="update") + * @ODM\Field(type="string") + */ + protected $updatedBy; + + /** + * Sets createdBy. + * + * @param string $createdBy + * @return $this + */ + public function setCreatedBy($createdBy) + { + $this->createdBy = $createdBy; + + return $this; + } + + /** + * Returns createdBy. + * + * @return string + */ + public function getCreatedBy() + { + return $this->createdBy; + } + + /** + * Sets updatedBy. + * + * @param string $updatedBy + * @return $this + */ + public function setUpdatedBy($updatedBy) + { + $this->updatedBy = $updatedBy; + + return $this; + } + + /** + * Returns updatedBy. + * + * @return string + */ + public function getUpdatedBy() + { + return $this->updatedBy; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableEntity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableEntity.php new file mode 100644 index 0000000..837f8f9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableEntity.php @@ -0,0 +1,75 @@ += 5.4 + * + * @author David Buchmann + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait BlameableEntity +{ + /** + * @var string + * @Gedmo\Blameable(on="create") + * @ORM\Column(nullable=true) + */ + protected $createdBy; + + /** + * @var string + * @Gedmo\Blameable(on="update") + * @ORM\Column(nullable=true) + */ + protected $updatedBy; + + /** + * Sets createdBy. + * + * @param string $createdBy + * @return $this + */ + public function setCreatedBy($createdBy) + { + $this->createdBy = $createdBy; + + return $this; + } + + /** + * Returns createdBy. + * + * @return string + */ + public function getCreatedBy() + { + return $this->createdBy; + } + + /** + * Sets updatedBy. + * + * @param string $updatedBy + * @return $this + */ + public function setUpdatedBy($updatedBy) + { + $this->updatedBy = $updatedBy; + + return $this; + } + + /** + * Returns updatedBy. + * + * @return string + */ + public function getUpdatedBy() + { + return $this->updatedBy; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/DoctrineExtensions.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/DoctrineExtensions.php new file mode 100644 index 0000000..99b64c4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/DoctrineExtensions.php @@ -0,0 +1,117 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class DoctrineExtensions +{ + /** + * Current version of extensions + */ + const VERSION = 'v2.4.26'; + + /** + * Hooks all extensions metadata mapping drivers + * into given $driverChain of drivers for ORM + * + * @param MappingDriverChain $driverChain + * @param Reader|null $reader + */ + public static function registerMappingIntoDriverChainORM(MappingDriverChain $driverChain, Reader $reader = null) + { + self::registerAnnotations(); + if (!$reader) { + $reader = new CachedReader(new AnnotationReader(), new ArrayCache()); + } + $annotationDriver = new DriverORM\AnnotationDriver($reader, array( + __DIR__.'/Translatable/Entity', + __DIR__.'/Loggable/Entity', + __DIR__.'/Tree/Entity', + )); + $driverChain->addDriver($annotationDriver, 'Gedmo'); + } + + /** + * Hooks only superclass metadata mapping drivers + * into given $driverChain of drivers for ORM + * + * @param MappingDriverChain $driverChain + * @param Reader|null $reader + */ + public static function registerAbstractMappingIntoDriverChainORM(MappingDriverChain $driverChain, Reader $reader = null) + { + self::registerAnnotations(); + if (!$reader) { + $reader = new CachedReader(new AnnotationReader(), new ArrayCache()); + } + $annotationDriver = new DriverORM\AnnotationDriver($reader, array( + __DIR__.'/Translatable/Entity/MappedSuperclass', + __DIR__.'/Loggable/Entity/MappedSuperclass', + __DIR__.'/Tree/Entity/MappedSuperclass', + )); + $driverChain->addDriver($annotationDriver, 'Gedmo'); + } + + /** + * Hooks all extensions metadata mapping drivers + * into given $driverChain of drivers for ODM MongoDB + * + * @param MappingDriverChain $driverChain + * @param Reader|null $reader + */ + public static function registerMappingIntoDriverChainMongodbODM(MappingDriverChain $driverChain, Reader $reader = null) + { + self::registerAnnotations(); + if (!$reader) { + $reader = new CachedReader(new AnnotationReader(), new ArrayCache()); + } + $annotationDriver = new DriverMongodbODM\AnnotationDriver($reader, array( + __DIR__.'/Translatable/Document', + __DIR__.'/Loggable/Document', + )); + $driverChain->addDriver($annotationDriver, 'Gedmo'); + } + + /** + * Hooks only superclass metadata mapping drivers + * into given $driverChain of drivers for ODM MongoDB + * + * @param MappingDriverChain $driverChain + * @param Reader|null $reader + */ + public static function registerAbstractMappingIntoDriverChainMongodbODM(MappingDriverChain $driverChain, Reader $reader = null) + { + self::registerAnnotations(); + if (!$reader) { + $reader = new CachedReader(new AnnotationReader(), new ArrayCache()); + } + $annotationDriver = new DriverMongodbODM\AnnotationDriver($reader, array( + __DIR__.'/Translatable/Document/MappedSuperclass', + __DIR__.'/Loggable/Document/MappedSuperclass', + )); + $driverChain->addDriver($annotationDriver, 'Gedmo'); + } + + /** + * Includes all extension annotations once + */ + public static function registerAnnotations() + { + AnnotationRegistry::registerFile(__DIR__.'/Mapping/Annotation/All.php'); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception.php new file mode 100644 index 0000000..fcbe45b --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception.php @@ -0,0 +1,19 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Exception +{ + /** + * Following best practices for PHP5.3 package exceptions. + * All exceptions thrown in this package will have to implement this interface + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/BadMethodCallException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/BadMethodCallException.php new file mode 100644 index 0000000..55d6a2c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/BadMethodCallException.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class BadMethodCallException + extends \BadMethodCallException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/FeatureNotImplementedException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/FeatureNotImplementedException.php new file mode 100644 index 0000000..46f235e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/FeatureNotImplementedException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class FeatureNotImplementedException + extends \RuntimeException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidArgumentException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..622f1ab --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidArgumentException.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class InvalidArgumentException + extends \InvalidArgumentException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidMappingException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidMappingException.php new file mode 100644 index 0000000..e43fe4c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidMappingException.php @@ -0,0 +1,20 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class InvalidMappingException + extends InvalidArgumentException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/ReferenceIntegrityStrictException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/ReferenceIntegrityStrictException.php new file mode 100644 index 0000000..e436e3f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/ReferenceIntegrityStrictException.php @@ -0,0 +1,15 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ReferenceIntegrityStrictException extends RuntimeException +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/RuntimeException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/RuntimeException.php new file mode 100644 index 0000000..4f83b41 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/RuntimeException.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class RuntimeException + extends \RuntimeException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/TreeLockingException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/TreeLockingException.php new file mode 100644 index 0000000..53ffa20 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/TreeLockingException.php @@ -0,0 +1,16 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TreeLockingException extends RuntimeException +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnexpectedValueException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnexpectedValueException.php new file mode 100644 index 0000000..c1d9514 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnexpectedValueException.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UnexpectedValueException + extends \UnexpectedValueException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnsupportedObjectManagerException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnsupportedObjectManagerException.php new file mode 100644 index 0000000..8acf66c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnsupportedObjectManagerException.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UnsupportedObjectManagerException + extends InvalidArgumentException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCantWriteException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCantWriteException.php new file mode 100644 index 0000000..e74ad8d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCantWriteException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableCantWriteException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCouldntGuessMimeTypeException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCouldntGuessMimeTypeException.php new file mode 100644 index 0000000..7b25b69 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCouldntGuessMimeTypeException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableCouldntGuessMimeTypeException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableDirectoryNotFoundException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableDirectoryNotFoundException.php new file mode 100644 index 0000000..1d75c38 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableDirectoryNotFoundException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableDirectoryNotFoundException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableException.php new file mode 100644 index 0000000..2639f08 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableException + extends RuntimeException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableExtensionException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableExtensionException.php new file mode 100644 index 0000000..f53bd53 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableExtensionException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableExtensionException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php new file mode 100644 index 0000000..fdc80eb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableFileAlreadyExistsException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileNotReadableException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileNotReadableException.php new file mode 100644 index 0000000..f537e7c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileNotReadableException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableFileNotReadableException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFormSizeException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFormSizeException.php new file mode 100644 index 0000000..6b76153 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFormSizeException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableFormSizeException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableIniSizeException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableIniSizeException.php new file mode 100644 index 0000000..b56923e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableIniSizeException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableIniSizeException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidFileException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidFileException.php new file mode 100644 index 0000000..9dd222a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidFileException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableInvalidFileException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidMimeTypeException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidMimeTypeException.php new file mode 100644 index 0000000..13080ff --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidMimeTypeException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableInvalidMimeTypeException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidPathException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidPathException.php new file mode 100644 index 0000000..793213a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidPathException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableInvalidPathException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableMaxSizeException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableMaxSizeException.php new file mode 100644 index 0000000..5f0bd14 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableMaxSizeException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableMaxSizeException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoFileException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoFileException.php new file mode 100644 index 0000000..cb33c32 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoFileException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableNoFileException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoPathDefinedException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoPathDefinedException.php new file mode 100644 index 0000000..7d33965 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoPathDefinedException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableNoPathDefinedException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoTmpDirException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoTmpDirException.php new file mode 100644 index 0000000..3d02477 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoTmpDirException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableNoTmpDirException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadablePartialException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadablePartialException.php new file mode 100644 index 0000000..cc769b1 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadablePartialException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadablePartialException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableUploadException.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableUploadException.php new file mode 100644 index 0000000..32022ba --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableUploadException.php @@ -0,0 +1,18 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableUploadException + extends UploadableException + implements Exception +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceable.php new file mode 100644 index 0000000..a24b092 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceable.php @@ -0,0 +1,50 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface IpTraceable +{ + // ipTraceable expects annotations on properties + + /** + * @gedmo:IpTraceable(on="create") + * strings which should be updated on insert only + */ + + /** + * @gedmo:IpTraceable(on="update") + * strings which should be updated on update and insert + */ + + /** + * @gedmo:IpTraceable(on="change", field="field", value="value") + * strings which should be updated on changed "property" + * value and become equal to given "value" + */ + + /** + * @gedmo:IpTraceable(on="change", field="field") + * strings which should be updated on changed "property" + */ + + /** + * @gedmo:IpTraceable(on="change", fields={"field1", "field2"}) + * strings which should be updated if at least one of the given fields changed + */ + + /** + * example + * + * @gedmo:IpTraceable(on="create") + * @Column(type="string") + * $created + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceableListener.php new file mode 100644 index 0000000..b09cda5 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceableListener.php @@ -0,0 +1,59 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class IpTraceableListener extends AbstractTrackingListener +{ + /** + * @var string|null + */ + protected $ip; + + /** + * Get the ipValue value to set on a ip field + * + * @param object $meta + * @param string $field + * @param AdapterInterface $eventAdapter + * + * @return null|string + */ + public function getFieldValue($meta, $field, $eventAdapter) + { + return $this->ip; + } + + /** + * Set a ip value to return + * + * @param string $ip + * @throws InvalidArgumentException + */ + public function setIpValue($ip = null) + { + if (isset($ip) && filter_var($ip, FILTER_VALIDATE_IP) === false) { + throw new InvalidArgumentException("ip address is not valid $ip"); + } + + $this->ip = $ip; + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..206ced2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Annotation.php @@ -0,0 +1,77 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation field is ipTraceable + */ + const IP_TRACEABLE = 'Gedmo\\Mapping\\Annotation\\IpTraceable'; + + /** + * List of types which are valid for IP + * + * @var array + */ + protected $validTypes = array( + 'string', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + if ($ipTraceable = $this->reader->getPropertyAnnotation($property, self::IP_TRACEABLE)) { + $field = $property->getName(); + + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find ipTraceable [{$field}] as mapped property in entity - {$meta->name}"); + } + if ($meta->hasField($field) && !$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' - {$meta->name}"); + } + if (!in_array($ipTraceable->on, array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + if ($ipTraceable->on == 'change') { + if (!isset($ipTraceable->field)) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + if (is_array($ipTraceable->field) && isset($ipTraceable->value)) { + throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $ipTraceable->field, + 'value' => $ipTraceable->value, + ); + } + // properties are unique and mapper checks that, no risk here + $config[$ipTraceable->on][] = $field; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..2140a7e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Xml.php @@ -0,0 +1,133 @@ + + * @author Miha Vrhovnik + * @author Pierre-Charles Bertineau + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * List of types which are valid for IP + * + * @var array + */ + private $validTypes = array( + 'string', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $mapping + */ + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping->field)) { + /** + * @var \SimpleXmlElement $fieldMapping + */ + foreach ($mapping->field as $fieldMapping) { + $fieldMappingDoctrine = $fieldMapping; + $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($fieldMapping->{'ip-traceable'})) { + /** + * @var \SimpleXmlElement $data + */ + $data = $fieldMapping->{'ip-traceable'}; + + $field = $this->_getAttribute($fieldMappingDoctrine, 'name'); + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' in class - {$meta->name}"); + } + if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($this->_getAttribute($data, 'on') == 'change') { + if (!$this->_isAttributeSet($data, 'field')) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $this->_getAttribute($data, 'field'); + $valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$this->_getAttribute($data, 'on')][] = $field; + } + } + } + + if (isset($mapping->{'many-to-one'})) { + foreach ($mapping->{'many-to-one'} as $fieldMapping) { + $field = $this->_getAttribute($fieldMapping, 'field'); + $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($fieldMapping->{'ip-traceable'})) { + /** + * @var \SimpleXmlElement $data + */ + $data = $fieldMapping->{'ip-traceable'}; + + if (! $meta->isSingleValuedAssociation($field)) { + throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}"); + } + if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($this->_getAttribute($data, 'on') == 'change') { + if (!$this->_isAttributeSet($data, 'field')) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $this->_getAttribute($data, 'field'); + $valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$this->_getAttribute($data, 'on')][] = $field; + } + } + } + } + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..2f33c4a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Yaml.php @@ -0,0 +1,127 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * List of types which are valid for IP + * + * @var array + */ + private $validTypes = array( + 'string', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo']['ipTraceable'])) { + $mappingProperty = $fieldMapping['gedmo']['ipTraceable']; + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' in class - {$meta->name}"); + } + if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($mappingProperty['on'] == 'change') { + if (!isset($mappingProperty['field'])) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $mappingProperty['field']; + $valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$mappingProperty['on']][] = $field; + } + } + } + + if (isset($mapping['manyToOne'])) { + foreach ($mapping['manyToOne'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo']['ipTraceable'])) { + $mappingProperty = $fieldMapping['gedmo']['ipTraceable']; + if (! $meta->isSingleValuedAssociation($field)) { + throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}"); + } + if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($mappingProperty['on'] == 'change') { + if (!isset($mappingProperty['field'])) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $mappingProperty['field']; + $valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$mappingProperty['on']][] = $field; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..8202345 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements IpTraceableAdapter +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..784264a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements IpTraceableAdapter +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/IpTraceableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/IpTraceableAdapter.php new file mode 100644 index 0000000..9128a35 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/IpTraceableAdapter.php @@ -0,0 +1,16 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface IpTraceableAdapter extends AdapterInterface +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceable.php new file mode 100644 index 0000000..ecfd362 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceable.php @@ -0,0 +1,68 @@ += 5.4 + * + * @author Pierre-Charles Bertineau + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait IpTraceable +{ + /** + * @var string + */ + protected $createdFromIp; + + /** + * @var string + */ + protected $updatedFromIp; + + /** + * Sets createdFromIp. + * + * @param string $createdFromIp + * @return $this + */ + public function setCreatedFromIp($createdFromIp) + { + $this->createdFromIp = $createdFromIp; + + return $this; + } + + /** + * Returns createdFromIp. + * + * @return string + */ + public function getCreatedFromIp() + { + return $this->createdFromIp; + } + + /** + * Sets updatedFromIp. + * + * @param string $updatedFromIp + * @return $this + */ + public function setUpdatedFromIp($updatedFromIp) + { + $this->updatedFromIp = $updatedFromIp; + + return $this; + } + + /** + * Returns updatedFromIp. + * + * @return string + */ + public function getUpdatedFromIp() + { + return $this->updatedFromIp; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableDocument.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableDocument.php new file mode 100644 index 0000000..31e7691 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableDocument.php @@ -0,0 +1,75 @@ += 5.4 + * + * @author Pierre-Charles Bertineau + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait IpTraceableDocument +{ + /** + * @var string + * @Gedmo\IpTraceable(on="create") + * @ODM\Field(type="string") + */ + protected $createdFromIp; + + /** + * @var string + * @Gedmo\IpTraceable(on="update") + * @ODM\Field(type="string") + */ + protected $updatedFromIp; + + /** + * Sets createdFromIp. + * + * @param string $createdFromIp + * @return $this + */ + public function setCreatedFromIp($createdFromIp) + { + $this->createdFromIp = $createdFromIp; + + return $this; + } + + /** + * Returns createdFromIp. + * + * @return string + */ + public function getCreatedFromIp() + { + return $this->createdFromIp; + } + + /** + * Sets updatedFromIp. + * + * @param string $updatedFromIp + * @return $this + */ + public function setUpdatedFromIp($updatedFromIp) + { + $this->updatedFromIp = $updatedFromIp; + + return $this; + } + + /** + * Returns updatedFromIp. + * + * @return string + */ + public function getUpdatedFromIp() + { + return $this->updatedFromIp; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableEntity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableEntity.php new file mode 100644 index 0000000..e5f4c8d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableEntity.php @@ -0,0 +1,75 @@ += 5.4 + * + * @author Pierre-Charles Bertineau + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait IpTraceableEntity +{ + /** + * @var string + * @Gedmo\IpTraceable(on="create") + * @ORM\Column(length=45, nullable=true) + */ + protected $createdFromIp; + + /** + * @var string + * @Gedmo\IpTraceable(on="update") + * @ORM\Column(length=45, nullable=true) + */ + protected $updatedFromIp; + + /** + * Sets createdFromIp. + * + * @param string $createdFromIp + * @return $this + */ + public function setCreatedFromIp($createdFromIp) + { + $this->createdFromIp = $createdFromIp; + + return $this; + } + + /** + * Returns createdFromIp. + * + * @return string + */ + public function getCreatedFromIp() + { + return $this->createdFromIp; + } + + /** + * Sets updatedFromIp. + * + * @param string $updatedFromIp + * @return $this + */ + public function setUpdatedFromIp($updatedFromIp) + { + $this->updatedFromIp = $updatedFromIp; + + return $this; + } + + /** + * Returns updatedFromIp. + * + * @return string + */ + public function getUpdatedFromIp() + { + return $this->updatedFromIp; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/LogEntry.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/LogEntry.php new file mode 100644 index 0000000..0ffa297 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/LogEntry.php @@ -0,0 +1,25 @@ +id; + } + + /** + * Get action + * + * @return string + */ + public function getAction() + { + return $this->action; + } + + /** + * Set action + * + * @param string $action + */ + public function setAction($action) + { + $this->action = $action; + } + + /** + * Get object class + * + * @return string + */ + public function getObjectClass() + { + return $this->objectClass; + } + + /** + * Set object class + * + * @param string $objectClass + */ + public function setObjectClass($objectClass) + { + $this->objectClass = $objectClass; + } + + /** + * Get object id + * + * @return string + */ + public function getObjectId() + { + return $this->objectId; + } + + /** + * Set object id + * + * @param string $objectId + */ + public function setObjectId($objectId) + { + $this->objectId = $objectId; + } + + /** + * Get username + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Set username + * + * @param string $username + */ + public function setUsername($username) + { + $this->username = $username; + } + + /** + * Get loggedAt + * + * @return \DateTime + */ + public function getLoggedAt() + { + return $this->loggedAt; + } + + /** + * Set loggedAt to "now" + */ + public function setLoggedAt() + { + $this->loggedAt = new \DateTime(); + } + + /** + * Get data + * + * @return array or null + */ + public function getData() + { + return $this->data; + } + + /** + * Set data + * + * @param array $data + */ + public function setData($data) + { + $this->data = $data; + } + + /** + * Set current version + * + * @param integer $version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Get current version + * + * @return integer + */ + public function getVersion() + { + return $this->version; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php new file mode 100644 index 0000000..7b6139d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php @@ -0,0 +1,161 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class LogEntryRepository extends DocumentRepository +{ + /** + * Currently used loggable listener + * + * @var LoggableListener + */ + private $listener; + + /** + * Loads all log entries for the + * given $document + * + * @param object $document + * + * @return LogEntry[] + */ + public function getLogEntries($document) + { + $wrapped = new MongoDocumentWrapper($document, $this->dm); + $objectId = $wrapped->getIdentifier(); + + $qb = $this->createQueryBuilder(); + $qb->field('objectId')->equals($objectId); + $qb->field('objectClass')->equals($wrapped->getMetadata()->name); + $qb->sort('version', 'DESC'); + $q = $qb->getQuery(); + + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = $result->toArray(); + } + return $result; + } + + /** + * Reverts given $document to $revision by + * restoring all fields from that $revision. + * After this operation you will need to + * persist and flush the $document. + * + * @param object $document + * @param integer $version + * + * @throws \Gedmo\Exception\UnexpectedValueException + * + * @return void + */ + public function revert($document, $version = 1) + { + $wrapped = new MongoDocumentWrapper($document, $this->dm); + $objectMeta = $wrapped->getMetadata(); + $objectId = $wrapped->getIdentifier(); + + $qb = $this->createQueryBuilder(); + $qb->field('objectId')->equals($objectId); + $qb->field('objectClass')->equals($objectMeta->name); + $qb->field('version')->lte(intval($version)); + $qb->sort('version', 'ASC'); + $q = $qb->getQuery(); + + $logs = $q->execute(); + if ($logs instanceof Cursor) { + $logs = $logs->toArray(); + } + if ($logs) { + $data = array(); + while (($log = array_shift($logs))) { + $data = array_merge($data, $log->getData()); + } + $this->fillDocument($document, $data, $objectMeta); + } else { + throw new \Gedmo\Exception\UnexpectedValueException('Count not find any log entries under version: '.$version); + } + } + + /** + * Fills a documents versioned fields with data + * + * @param object $document + * @param array $data + */ + protected function fillDocument($document, array $data) + { + $wrapped = new MongoDocumentWrapper($document, $this->dm); + $objectMeta = $wrapped->getMetadata(); + $config = $this->getLoggableListener()->getConfiguration($this->dm, $objectMeta->name); + $fields = $config['versioned']; + foreach ($data as $field => $value) { + if (!in_array($field, $fields)) { + continue; + } + $mapping = $objectMeta->getFieldMapping($field); + // Fill the embedded document + if ($wrapped->isEmbeddedAssociation($field)) { + if (!empty($value)) { + $embeddedMetadata = $this->dm->getClassMetadata($mapping['targetDocument']); + $document = $embeddedMetadata->newInstance(); + $this->fillDocument($document, $value); + $value = $document; + } + } elseif ($objectMeta->isSingleValuedAssociation($field)) { + $value = $value ? $this->dm->getReference($mapping['targetDocument'], $value) : null; + } + $wrapped->setPropertyValue($field, $value); + unset($fields[$field]); + } + + /* + if (count($fields)) { + throw new \Gedmo\Exception\UnexpectedValueException('Cound not fully revert the document to version: '.$version); + } + */ + } + + /** + * Get the currently used LoggableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return LoggableListener + */ + private function getLoggableListener() + { + if (is_null($this->listener)) { + foreach ($this->dm->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof LoggableListener) { + $this->listener = $listener; + break; + } + } + if ($this->listener) { + break; + } + } + + if (is_null($this->listener)) { + throw new \Gedmo\Exception\RuntimeException('The loggable listener could not be found'); + } + } + return $this->listener; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/LogEntry.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/LogEntry.php new file mode 100644 index 0000000..846a6da --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/LogEntry.php @@ -0,0 +1,27 @@ +id; + } + + /** + * Get action + * + * @return string + */ + public function getAction() + { + return $this->action; + } + + /** + * Set action + * + * @param string $action + */ + public function setAction($action) + { + $this->action = $action; + } + + /** + * Get object class + * + * @return string + */ + public function getObjectClass() + { + return $this->objectClass; + } + + /** + * Set object class + * + * @param string $objectClass + */ + public function setObjectClass($objectClass) + { + $this->objectClass = $objectClass; + } + + /** + * Get object id + * + * @return string + */ + public function getObjectId() + { + return $this->objectId; + } + + /** + * Set object id + * + * @param string $objectId + */ + public function setObjectId($objectId) + { + $this->objectId = $objectId; + } + + /** + * Get username + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Set username + * + * @param string $username + */ + public function setUsername($username) + { + $this->username = $username; + } + + /** + * Get loggedAt + * + * @return \DateTime + */ + public function getLoggedAt() + { + return $this->loggedAt; + } + + /** + * Set loggedAt to "now" + */ + public function setLoggedAt() + { + $this->loggedAt = new \DateTime(); + } + + /** + * Get data + * + * @return array + */ + public function getData() + { + return $this->data; + } + + /** + * Set data + * + * @param array $data + */ + public function setData($data) + { + $this->data = $data; + } + + /** + * Set current version + * + * @param integer $version + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Get current version + * + * @return integer + */ + public function getVersion() + { + return $this->version; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php new file mode 100644 index 0000000..b576162 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php @@ -0,0 +1,164 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class LogEntryRepository extends EntityRepository +{ + /** + * Currently used loggable listener + * + * @var LoggableListener + */ + private $listener; + + /** + * Loads all log entries for the given entity + * + * @param object $entity + * + * @return LogEntry[] + */ + public function getLogEntries($entity) + { + $q = $this->getLogEntriesQuery($entity); + + return $q->getResult(); + } + + /** + * Get the query for loading of log entries + * + * @param object $entity + * + * @return Query + */ + public function getLogEntriesQuery($entity) + { + $wrapped = new EntityWrapper($entity, $this->_em); + $objectClass = $wrapped->getMetadata()->name; + $meta = $this->getClassMetadata(); + $dql = "SELECT log FROM {$meta->name} log"; + $dql .= " WHERE log.objectId = :objectId"; + $dql .= " AND log.objectClass = :objectClass"; + $dql .= " ORDER BY log.version DESC"; + + $objectId = (string) $wrapped->getIdentifier(); + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('objectId', 'objectClass')); + + return $q; + } + + /** + * Reverts given $entity to $revision by + * restoring all fields from that $revision. + * After this operation you will need to + * persist and flush the $entity. + * + * @param object $entity + * @param integer $version + * + * @throws \Gedmo\Exception\UnexpectedValueException + * + * @return void + */ + public function revert($entity, $version = 1) + { + $wrapped = new EntityWrapper($entity, $this->_em); + $objectMeta = $wrapped->getMetadata(); + $objectClass = $objectMeta->name; + $meta = $this->getClassMetadata(); + $dql = "SELECT log FROM {$meta->name} log"; + $dql .= " WHERE log.objectId = :objectId"; + $dql .= " AND log.objectClass = :objectClass"; + $dql .= " AND log.version <= :version"; + $dql .= " ORDER BY log.version ASC"; + + $objectId = $wrapped->getIdentifier(); + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('objectId', 'objectClass', 'version')); + $logs = $q->getResult(); + + if ($logs) { + $config = $this->getLoggableListener()->getConfiguration($this->_em, $objectMeta->name); + $fields = $config['versioned']; + $filled = false; + while (($log = array_pop($logs)) && !$filled) { + if ($data = $log->getData()) { + foreach ($data as $field => $value) { + if (in_array($field, $fields)) { + $this->mapValue($objectMeta, $field, $value); + $wrapped->setPropertyValue($field, $value); + unset($fields[array_search($field, $fields)]); + } + } + } + $filled = count($fields) === 0; + } + /*if (count($fields)) { + throw new \Gedmo\Exception\UnexpectedValueException('Could not fully revert the entity to version: '.$version); + }*/ + } else { + throw new \Gedmo\Exception\UnexpectedValueException('Could not find any log entries under version: '.$version); + } + } + + /** + * @param ClassMetadata $objectMeta + * @param string $field + * @param mixed $value + */ + protected function mapValue(ClassMetadata $objectMeta, $field, &$value) + { + if (!$objectMeta->isSingleValuedAssociation($field)) { + return; + } + + $mapping = $objectMeta->getAssociationMapping($field); + $value = $value ? $this->_em->getReference($mapping['targetEntity'], $value) : null; + } + + /** + * Get the currently used LoggableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return LoggableListener + */ + private function getLoggableListener() + { + if (is_null($this->listener)) { + foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof LoggableListener) { + $this->listener = $listener; + break; + } + } + if ($this->listener) { + break; + } + } + + if (is_null($this->listener)) { + throw new \Gedmo\Exception\RuntimeException('The loggable listener could not be found'); + } + } + + return $this->listener; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Loggable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Loggable.php new file mode 100644 index 0000000..9adb9a9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Loggable.php @@ -0,0 +1,28 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Loggable +{ + // this interface is not necessary to implement + + /** + * @gedmo:Loggable + * to mark the class as loggable use class annotation @gedmo:Loggable + * this object will contain now a history + * available options: + * logEntryClass="My\LogEntryObject" (optional) defaultly will use internal object class + * example: + * + * @gedmo:Loggable(logEntryClass="My\LogEntryObject") + * class MyEntity + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/LoggableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/LoggableListener.php new file mode 100644 index 0000000..7621850 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/LoggableListener.php @@ -0,0 +1,324 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class LoggableListener extends MappedEventSubscriber +{ + /** + * Create action + */ + const ACTION_CREATE = 'create'; + + /** + * Update action + */ + const ACTION_UPDATE = 'update'; + + /** + * Remove action + */ + const ACTION_REMOVE = 'remove'; + + /** + * Username for identification + * + * @var string + */ + protected $username; + + /** + * List of log entries which do not have the foreign + * key generated yet - MySQL case. These entries + * will be updated with new keys on postPersist event + * + * @var array + */ + protected $pendingLogEntryInserts = array(); + + /** + * For log of changed relations we use + * its identifiers to avoid storing serialized Proxies. + * These are pending relations in case it does not + * have an identifier yet + * + * @var array + */ + protected $pendingRelatedObjects = array(); + + /** + * Set username for identification + * + * @param mixed $username + * + * @throws \Gedmo\Exception\InvalidArgumentException Invalid username + */ + public function setUsername($username) + { + if (is_string($username)) { + $this->username = $username; + } elseif (is_object($username) && method_exists($username, 'getUsername')) { + $this->username = (string) $username->getUsername(); + } else { + throw new \Gedmo\Exception\InvalidArgumentException("Username must be a string, or object should have method: getUsername"); + } + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array( + 'onFlush', + 'loadClassMetadata', + 'postPersist', + ); + } + + /** + * Get the LogEntry class + * + * @param LoggableAdapter $ea + * @param string $class + * + * @return string + */ + protected function getLogEntryClass(LoggableAdapter $ea, $class) + { + return isset(self::$configurations[$this->name][$class]['logEntryClass']) ? + self::$configurations[$this->name][$class]['logEntryClass'] : + $ea->getDefaultLogEntryClass(); + } + + /** + * Maps additional metadata + * + * @param EventArgs $eventArgs + * + * @return void + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * Checks for inserted object to update its logEntry + * foreign key + * + * @param EventArgs $args + * + * @return void + */ + public function postPersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $object = $ea->getObject(); + $om = $ea->getObjectManager(); + $oid = spl_object_hash($object); + $uow = $om->getUnitOfWork(); + if ($this->pendingLogEntryInserts && array_key_exists($oid, $this->pendingLogEntryInserts)) { + $wrapped = AbstractWrapper::wrap($object, $om); + + $logEntry = $this->pendingLogEntryInserts[$oid]; + $logEntryMeta = $om->getClassMetadata(get_class($logEntry)); + + $id = $wrapped->getIdentifier(); + $logEntryMeta->getReflectionProperty('objectId')->setValue($logEntry, $id); + $uow->scheduleExtraUpdate($logEntry, array( + 'objectId' => array(null, $id), + )); + $ea->setOriginalObjectProperty($uow, spl_object_hash($logEntry), 'objectId', $id); + unset($this->pendingLogEntryInserts[$oid]); + } + if ($this->pendingRelatedObjects && array_key_exists($oid, $this->pendingRelatedObjects)) { + $wrapped = AbstractWrapper::wrap($object, $om); + $identifiers = $wrapped->getIdentifier(false); + foreach ($this->pendingRelatedObjects[$oid] as $props) { + $logEntry = $props['log']; + $logEntryMeta = $om->getClassMetadata(get_class($logEntry)); + $oldData = $data = $logEntry->getData(); + $data[$props['field']] = $identifiers; + + $logEntry->setData($data); + + $uow->scheduleExtraUpdate($logEntry, array( + 'data' => array($oldData, $data), + )); + $ea->setOriginalObjectProperty($uow, spl_object_hash($logEntry), 'data', $data); + } + unset($this->pendingRelatedObjects[$oid]); + } + } + + /** + * Handle any custom LogEntry functionality that needs to be performed + * before persisting it + * + * @param object $logEntry The LogEntry being persisted + * @param object $object The object being Logged + */ + protected function prePersistLogEntry($logEntry, $object) + { + + } + + /** + * Looks for loggable objects being inserted or updated + * for further processing + * + * @param EventArgs $eventArgs + * + * @return void + */ + public function onFlush(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + foreach ($ea->getScheduledObjectInsertions($uow) as $object) { + $this->createLogEntry(self::ACTION_CREATE, $object, $ea); + } + foreach ($ea->getScheduledObjectUpdates($uow) as $object) { + $this->createLogEntry(self::ACTION_UPDATE, $object, $ea); + } + foreach ($ea->getScheduledObjectDeletions($uow) as $object) { + $this->createLogEntry(self::ACTION_REMOVE, $object, $ea); + } + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } + + /** + * Returns an objects changeset data + * + * @param LoggableAdapter $ea + * @param object $object + * @param object $logEntry + * + * @return array + */ + protected function getObjectChangeSetData($ea, $object, $logEntry) + { + $om = $ea->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $om); + $meta = $wrapped->getMetadata(); + $config = $this->getConfiguration($om, $meta->name); + $uow = $om->getUnitOfWork(); + $newValues = array(); + + foreach ($ea->getObjectChangeSet($uow, $object) as $field => $changes) { + if (empty($config['versioned']) || !in_array($field, $config['versioned'])) { + continue; + } + $value = $changes[1]; + if ($meta->isSingleValuedAssociation($field) && $value) { + if ($wrapped->isEmbeddedAssociation($field)) { + $value = $this->getObjectChangeSetData($ea, $value, $logEntry); + } else { + $oid = spl_object_hash($value); + $wrappedAssoc = AbstractWrapper::wrap($value, $om); + $value = $wrappedAssoc->getIdentifier(false); + if (!is_array($value) && !$value) { + $this->pendingRelatedObjects[$oid][] = array( + 'log' => $logEntry, + 'field' => $field, + ); + } + } + } + $newValues[$field] = $value; + } + + return $newValues; + } + + /** + * Create a new Log instance + * + * @param string $action + * @param object $object + * @param LoggableAdapter $ea + * + * @return \Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry|null + */ + protected function createLogEntry($action, $object, LoggableAdapter $ea) + { + $om = $ea->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $om); + $meta = $wrapped->getMetadata(); + + // Filter embedded documents + if (isset($meta->isEmbeddedDocument) && $meta->isEmbeddedDocument) { + return; + } + + if ($config = $this->getConfiguration($om, $meta->name)) { + $logEntryClass = $this->getLogEntryClass($ea, $meta->name); + $logEntryMeta = $om->getClassMetadata($logEntryClass); + /** @var \Gedmo\Loggable\Entity\LogEntry $logEntry */ + $logEntry = $logEntryMeta->newInstance(); + + $logEntry->setAction($action); + $logEntry->setUsername($this->username); + $logEntry->setObjectClass($meta->name); + $logEntry->setLoggedAt(); + + // check for the availability of the primary key + $uow = $om->getUnitOfWork(); + if ($action === self::ACTION_CREATE && $ea->isPostInsertGenerator($meta)) { + $this->pendingLogEntryInserts[spl_object_hash($object)] = $logEntry; + } else { + $logEntry->setObjectId($wrapped->getIdentifier()); + } + $newValues = array(); + if ($action !== self::ACTION_REMOVE && isset($config['versioned'])) { + $newValues = $this->getObjectChangeSetData($ea, $object, $logEntry); + $logEntry->setData($newValues); + } + + if($action === self::ACTION_UPDATE && 0 === count($newValues)) { + return null; + } + + $version = 1; + if ($action !== self::ACTION_CREATE) { + $version = $ea->getNewVersion($logEntryMeta, $object); + if (empty($version)) { + // was versioned later + $version = 1; + } + } + $logEntry->setVersion($version); + + $this->prePersistLogEntry($logEntry, $object); + + $om->persist($logEntry); + $uow->computeChangeSet($logEntryMeta, $logEntry); + + return $logEntry; + } + + return null; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..d200eb6 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Annotation.php @@ -0,0 +1,140 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to define that this object is loggable + */ + const LOGGABLE = 'Gedmo\\Mapping\\Annotation\\Loggable'; + + /** + * Annotation to define that this property is versioned + */ + const VERSIONED = 'Gedmo\\Mapping\\Annotation\\Versioned'; + + /** + * {@inheritDoc} + */ + public function validateFullMetadata(ClassMetadata $meta, array $config) + { + if ($config && is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}"); + } + if (isset($config['versioned']) && !isset($config['loggable'])) { + throw new InvalidMappingException("Class must be annotated with Loggable annotation in order to track versioned fields in class - {$meta->name}"); + } + } + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // class annotations + if ($annot = $this->reader->getClassAnnotation($class, self::LOGGABLE)) { + $config['loggable'] = true; + if ($annot->logEntryClass) { + if (!$cl = $this->getRelatedClassName($meta, $annot->logEntryClass)) { + throw new InvalidMappingException("LogEntry class: {$annot->logEntryClass} does not exist."); + } + $config['logEntryClass'] = $cl; + } + } + + // property annotations + foreach ($class->getProperties() as $property) { + $field = $property->getName(); + if ($meta->isMappedSuperclass && !$property->isPrivate()) { + continue; + } + + // versioned property + if ($this->reader->getPropertyAnnotation($property, self::VERSIONED)) { + if (!$this->isMappingValid($meta, $field)) { + throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}"); + } + if (isset($meta->embeddedClasses[$field])) { + $this->inspectEmbeddedForVersioned($field, $config, $meta); + continue; + } + // fields cannot be overrided and throws mapping exception + if (!(isset($config['versioned']) && in_array($field, $config['versioned']))) { + $config['versioned'][] = $field; + } + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}"); + } + if ($this->isClassAnnotationInValid($meta, $config)) { + throw new InvalidMappingException("Class must be annotated with Loggable annotation in order to track versioned fields in class - {$meta->name}"); + } + } + } + + /** + * @param ClassMetadata $meta + * @param string $field + * + * @return bool + */ + protected function isMappingValid(ClassMetadata $meta, $field) + { + return $meta->isCollectionValuedAssociation($field) == false; + } + + /** + * @param ClassMetadata $meta + * @param array $config + * + * @return bool + */ + protected function isClassAnnotationInValid(ClassMetadata $meta, array &$config) + { + return isset($config['versioned']) && !isset($config['loggable']) && (!isset($meta->isEmbeddedClass) || !$meta->isEmbeddedClass); + } + + /** + * Searches properties of embedded object for versioned fields + * + * @param string $field + * @param array $config + * @param \Doctrine\ORM\Mapping\ClassMetadata $meta + */ + private function inspectEmbeddedForVersioned($field, array &$config, \Doctrine\ORM\Mapping\ClassMetadata $meta) + { + $сlass = new \ReflectionClass($meta->embeddedClasses[$field]['class']); + + // property annotations + foreach ($сlass->getProperties() as $property) { + // versioned property + if ($this->reader->getPropertyAnnotation($property, self::VERSIONED)) { + $embeddedField = $field . '.' . $property->getName(); + $config['versioned'][] = $embeddedField; + + if (isset($meta->embeddedClasses[$embeddedField])) { + $this->inspectEmbeddedForVersioned($embeddedField, $config, $meta); + } + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..9a3f3b2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Xml.php @@ -0,0 +1,104 @@ + + * @author Gediminas Morkevicius + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + $xmlDoctrine = $xml; + + $xml = $xml->children(self::GEDMO_NAMESPACE_URI); + + if ($xmlDoctrine->getName() == 'entity' || $xmlDoctrine->getName() == 'document' || $xmlDoctrine->getName() == 'mapped-superclass') { + if (isset($xml->loggable)) { + /** + * @var \SimpleXMLElement $data; + */ + $data = $xml->loggable; + $config['loggable'] = true; + if ($this->_isAttributeSet($data, 'log-entry-class')) { + $class = $this->_getAttribute($data, 'log-entry-class'); + if (!$cl = $this->getRelatedClassName($meta, $class)) { + throw new InvalidMappingException("LogEntry class: {$class} does not exist."); + } + $config['logEntryClass'] = $cl; + } + } + } + + if (isset($xmlDoctrine->field)) { + $this->inspectElementForVersioned($xmlDoctrine->field, $config, $meta); + } + if (isset($xmlDoctrine->{'many-to-one'})) { + $this->inspectElementForVersioned($xmlDoctrine->{'many-to-one'}, $config, $meta); + } + if (isset($xmlDoctrine->{'one-to-one'})) { + $this->inspectElementForVersioned($xmlDoctrine->{'one-to-one'}, $config, $meta); + } + if (isset($xmlDoctrine->{'reference-one'})) { + $this->inspectElementForVersioned($xmlDoctrine->{'reference-one'}, $config, $meta); + } + if (isset($xmlDoctrine->{'embedded'})) { + $this->inspectElementForVersioned($xmlDoctrine->{'embedded'}, $config, $meta); + } + + if (!$meta->isMappedSuperclass && $config) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}"); + } + if (isset($config['versioned']) && !isset($config['loggable'])) { + throw new InvalidMappingException("Class must be annotated with Loggable annotation in order to track versioned fields in class - {$meta->name}"); + } + } + } + + /** + * Searches mappings on element for versioned fields + * + * @param \SimpleXMLElement $element + * @param array $config + * @param object $meta + */ + private function inspectElementForVersioned(\SimpleXMLElement $element, array &$config, $meta) + { + foreach ($element as $mapping) { + $mappingDoctrine = $mapping; + /** + * @var \SimpleXmlElement $mapping + */ + $mapping = $mapping->children(self::GEDMO_NAMESPACE_URI); + + $isAssoc = $this->_isAttributeSet($mappingDoctrine, 'field'); + $field = $this->_getAttribute($mappingDoctrine, $isAssoc ? 'field' : 'name'); + + if (isset($mapping->versioned)) { + if ($isAssoc && !$meta->associationMappings[$field]['isOwningSide']) { + throw new InvalidMappingException("Cannot version [{$field}] as it is not the owning side in object - {$meta->name}"); + } + $config['versioned'][] = $field; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..967bb8a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Yaml.php @@ -0,0 +1,149 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['gedmo'])) { + $classMapping = $mapping['gedmo']; + if (isset($classMapping['loggable'])) { + $config['loggable'] = true; + if (isset ($classMapping['loggable']['logEntryClass'])) { + if (!$cl = $this->getRelatedClassName($meta, $classMapping['loggable']['logEntryClass'])) { + throw new InvalidMappingException("LogEntry class: {$classMapping['loggable']['logEntryClass']} does not exist."); + } + $config['logEntryClass'] = $cl; + } + } + } + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('versioned', $fieldMapping['gedmo'])) { + if ($meta->isCollectionValuedAssociation($field)) { + throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}"); + } + // fields cannot be overrided and throws mapping exception + $config['versioned'][] = $field; + } + } + } + } + + if (isset($mapping['attributeOverride'])) { + foreach ($mapping['attributeOverride'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('versioned', $fieldMapping['gedmo'])) { + if ($meta->isCollectionValuedAssociation($field)) { + throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}"); + } + // fields cannot be overrided and throws mapping exception + $config['versioned'][] = $field; + } + } + } + } + + if (isset($mapping['manyToOne'])) { + foreach ($mapping['manyToOne'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('versioned', $fieldMapping['gedmo'])) { + if ($meta->isCollectionValuedAssociation($field)) { + throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}"); + } + // fields cannot be overrided and throws mapping exception + $config['versioned'][] = $field; + } + } + } + } + + if (isset($mapping['oneToOne'])) { + foreach ($mapping['oneToOne'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('versioned', $fieldMapping['gedmo'])) { + if ($meta->isCollectionValuedAssociation($field)) { + throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}"); + } + // fields cannot be overrided and throws mapping exception + $config['versioned'][] = $field; + } + } + } + } + + if (isset($mapping['embedded'])) { + foreach ($mapping['embedded'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('versioned', $fieldMapping['gedmo'])) { + if ($meta->isCollectionValuedAssociation($field)) { + throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}"); + } + // fields cannot be overrided and throws mapping exception + $mapping = $this->_getMapping($fieldMapping['class']); + $this->inspectEmbeddedForVersioned($field, $mapping, $config); + } + } + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}"); + } + if (isset($config['versioned']) && !isset($config['loggable'])) { + throw new InvalidMappingException("Class must be annoted with Loggable annotation in order to track versioned fields in class - {$meta->name}"); + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + /** + * @param string $field + * @param array $mapping + * @param array $config + */ + private function inspectEmbeddedForVersioned($field, array $mapping, array &$config) + { + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $property => $fieldMapping) { + $config['versioned'][] = $field . '.' . $property; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..0aa5df0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,59 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements LoggableAdapter +{ + /** + * {@inheritDoc} + */ + public function getDefaultLogEntryClass() + { + return 'Gedmo\\Loggable\\Document\\LogEntry'; + } + + /** + * {@inheritDoc} + */ + public function isPostInsertGenerator($meta) + { + return false; + } + + /** + * {@inheritDoc} + */ + public function getNewVersion($meta, $object) + { + $dm = $this->getObjectManager(); + $objectMeta = $dm->getClassMetadata(get_class($object)); + $identifierField = $this->getSingleIdentifierFieldName($objectMeta); + $objectId = $objectMeta->getReflectionProperty($identifierField)->getValue($object); + + $qb = $dm->createQueryBuilder($meta->name); + $qb->select('version'); + $qb->field('objectId')->equals($objectId); + $qb->field('objectClass')->equals($objectMeta->name); + $qb->sort('version', 'DESC'); + $qb->limit(1); + $q = $qb->getQuery(); + $q->setHydrate(false); + + $result = $q->getSingleResult(); + if ($result) { + $result = $result['version'] + 1; + } + + return $result; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..7d768da --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,55 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements LoggableAdapter +{ + /** + * {@inheritDoc} + */ + public function getDefaultLogEntryClass() + { + return 'Gedmo\\Loggable\\Entity\\LogEntry'; + } + + /** + * {@inheritDoc} + */ + public function isPostInsertGenerator($meta) + { + return $meta->idGenerator->isPostInsertGenerator(); + } + + /** + * {@inheritDoc} + */ + public function getNewVersion($meta, $object) + { + $em = $this->getObjectManager(); + $objectMeta = $em->getClassMetadata(get_class($object)); + $identifierField = $this->getSingleIdentifierFieldName($objectMeta); + $objectId = (string) $objectMeta->getReflectionProperty($identifierField)->getValue($object); + + $dql = "SELECT MAX(log.version) FROM {$meta->name} log"; + $dql .= " WHERE log.objectId = :objectId"; + $dql .= " AND log.objectClass = :objectClass"; + + $q = $em->createQuery($dql); + $q->setParameters(array( + 'objectId' => $objectId, + 'objectClass' => $objectMeta->name, + )); + + return $q->getSingleScalarResult() + 1; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/LoggableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/LoggableAdapter.php new file mode 100644 index 0000000..ba5ce47 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/LoggableAdapter.php @@ -0,0 +1,39 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface LoggableAdapter extends AdapterInterface +{ + /** + * Get default LogEntry class used to store the logs + * + * @return string + */ + public function getDefaultLogEntryClass(); + + /** + * Checks whether an id should be generated post insert + * + * @return boolean + */ + public function isPostInsertGenerator($meta); + + /** + * Get new version number + * + * @param object $meta + * @param object $object + * + * @return integer + */ + public function getNewVersion($meta, $object); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/All.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/All.php new file mode 100644 index 0000000..1df469f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/All.php @@ -0,0 +1,15 @@ + +* @license MIT License (http://www.opensource.org/licenses/mit-license.php) +*/ +foreach (glob(__DIR__ . "/*.php") as $filename) { + if (basename($filename, '.php') === 'All') { + continue; + } + include_once $filename; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Blameable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Blameable.php new file mode 100644 index 0000000..2993785 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Blameable.php @@ -0,0 +1,24 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Blameable extends Annotation +{ + /** @var string */ + public $on = 'update'; + /** @var string|array */ + public $field; + /** @var mixed */ + public $value; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/IpTraceable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/IpTraceable.php new file mode 100644 index 0000000..7ee1892 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/IpTraceable.php @@ -0,0 +1,24 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class IpTraceable extends Annotation +{ + /** @var string */ + public $on = 'update'; + /** @var string|array */ + public $field; + /** @var mixed */ + public $value; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Language.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Language.php new file mode 100644 index 0000000..a026a98 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Language.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Language extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Locale.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Locale.php new file mode 100644 index 0000000..f00602f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Locale.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Locale extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Loggable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Loggable.php new file mode 100644 index 0000000..dbde423 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Loggable.php @@ -0,0 +1,20 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Loggable extends Annotation +{ + /** @var string */ + public $logEntryClass; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Reference.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Reference.php new file mode 100644 index 0000000..11dd2cb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Reference.php @@ -0,0 +1,22 @@ + ODM references extension + * to be user like "@ReferenceMany(type="entity", class="MyEntity", identifier="entity_id")" + * + * @author Bulat Shakirzyanov + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * @Annotation + */ +abstract class Reference extends Annotation +{ + public $type; + public $class; + public $identifier; + public $mappedBy; + public $inversedBy; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceIntegrity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceIntegrity.php new file mode 100644 index 0000000..254578d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceIntegrity.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ReferenceIntegrity extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceMany.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceMany.php new file mode 100644 index 0000000..3eaa17c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceMany.php @@ -0,0 +1,15 @@ + ODM references extension + * to be user like "@ReferenceMany(type="entity", class="MyEntity", identifier="entity_id")" + * + * @author Bulat Shakirzyanov + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * @Annotation + */ +class ReferenceMany extends Reference +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceManyEmbed.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceManyEmbed.php new file mode 100644 index 0000000..a432b12 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/ReferenceManyEmbed.php @@ -0,0 +1,10 @@ + ODM references extension + * to be user like "@ReferenceOne(type="entity", class="MyEntity", identifier="entity_id")" + * + * @author Bulat Shakirzyanov + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * @Annotation + */ +class ReferenceOne extends Reference +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Slug.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Slug.php new file mode 100644 index 0000000..f1d99d9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Slug.php @@ -0,0 +1,38 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Slug extends Annotation +{ + /** @var array @Required */ + public $fields = array(); + /** @var boolean */ + public $updatable = true; + /** @var string */ + public $style = 'default'; // or "camel" + /** @var boolean */ + public $unique = true; + /** @var string */ + public $unique_base = null; + /** @var string */ + public $separator = '-'; + /** @var string */ + public $prefix = ''; + /** @var string */ + public $suffix = ''; + /** @var array */ + public $handlers = array(); + /** @var string */ + public $dateFormat = 'Y-m-d-H:i'; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandler.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandler.php new file mode 100644 index 0000000..8ff2c07 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandler.php @@ -0,0 +1,31 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class SlugHandler extends Annotation +{ + public $class = ''; + public $options = array(); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandlerOption.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandlerOption.php new file mode 100644 index 0000000..5242ab2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SlugHandlerOption.php @@ -0,0 +1,31 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class SlugHandlerOption extends Annotation +{ + public $name; + public $value; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SoftDeleteable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SoftDeleteable.php new file mode 100644 index 0000000..d52f126 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SoftDeleteable.php @@ -0,0 +1,26 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * + * @Annotation + * @Target("CLASS") + */ +final class SoftDeleteable extends Annotation +{ + /** @var string */ + public $fieldName = 'deletedAt'; + + /** @var bool */ + public $timeAware = false; + + /** @var bool */ + public $hardDelete = true; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortableGroup.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortableGroup.php new file mode 100644 index 0000000..9a39811 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortableGroup.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * + * @Annotation + * @Target("PROPERTY") + */ +final class SortableGroup extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortablePosition.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortablePosition.php new file mode 100644 index 0000000..86fd52c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/SortablePosition.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * + * @Annotation + * @Target("PROPERTY") + */ +final class SortablePosition extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Timestampable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Timestampable.php new file mode 100644 index 0000000..91f72b9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Timestampable.php @@ -0,0 +1,24 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Timestampable extends Annotation +{ + /** @var string */ + public $on = 'update'; + /** @var string|array */ + public $field; + /** @var mixed */ + public $value; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Translatable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Translatable.php new file mode 100644 index 0000000..7cc2845 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Translatable.php @@ -0,0 +1,20 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Translatable extends Annotation +{ + /** @var boolean */ + public $fallback; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TranslationEntity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TranslationEntity.php new file mode 100644 index 0000000..e4887bf --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TranslationEntity.php @@ -0,0 +1,20 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TranslationEntity extends Annotation +{ + /** @var string @Required */ + public $class; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Tree.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Tree.php new file mode 100644 index 0000000..aed53c7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Tree.php @@ -0,0 +1,29 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Tree extends Annotation +{ + /** @var string */ + public $type = 'nested'; + + /** @var string */ + public $activateLocking = false; + + /** @var integer */ + public $lockingTimeout = 3; + + /** @var string $identifierMethod */ + public $identifierMethod; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeClosure.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeClosure.php new file mode 100644 index 0000000..7026d84 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeClosure.php @@ -0,0 +1,20 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeClosure extends Annotation +{ + /** @var string @Required */ + public $class; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLeft.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLeft.php new file mode 100644 index 0000000..a8e3115 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLeft.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeLeft extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLevel.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLevel.php new file mode 100644 index 0000000..11b291c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLevel.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeLevel extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLockTime.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLockTime.php new file mode 100644 index 0000000..40fd5a7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeLockTime.php @@ -0,0 +1,19 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeLockTime extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeParent.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeParent.php new file mode 100644 index 0000000..5fd8ceb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeParent.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeParent extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePath.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePath.php new file mode 100644 index 0000000..143bb9f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePath.php @@ -0,0 +1,27 @@ + + * @author Gediminas Morkevicius + * @author + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreePath extends Annotation +{ + public $separator = ','; + + public $appendId = null; + + public $startsWithSeparator = false; + + public $endsWithSeparator = true; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathHash.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathHash.php new file mode 100644 index 0000000..be7c061 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathHash.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreePathHash extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathSource.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathSource.php new file mode 100644 index 0000000..648358b --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreePathSource.php @@ -0,0 +1,19 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreePathSource extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRight.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRight.php new file mode 100644 index 0000000..f7831f6 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRight.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeRight extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRoot.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRoot.php new file mode 100644 index 0000000..ab79952 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/TreeRoot.php @@ -0,0 +1,20 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class TreeRoot extends Annotation +{ + /** @var string $identifierMethod */ + public $identifierMethod; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Uploadable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Uploadable.php new file mode 100644 index 0000000..107da1d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Uploadable.php @@ -0,0 +1,46 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Uploadable extends Annotation +{ + /** @var boolean */ + public $allowOverwrite = false; + + /** @var boolean */ + public $appendNumber = false; + + /** @var string */ + public $path = ''; + + /** @var string */ + public $pathMethod = ''; + + /** @var string */ + public $callback = ''; + + /** @var string */ + public $filenameGenerator = Validator::FILENAME_GENERATOR_NONE; + + /** @var double */ + public $maxSize = 0; + + /** @var array */ + public $allowedTypes = ''; + + /** @var array */ + public $disallowedTypes = ''; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileMimeType.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileMimeType.php new file mode 100644 index 0000000..714f5dd --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileMimeType.php @@ -0,0 +1,19 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class UploadableFileMimeType extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileName.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileName.php new file mode 100644 index 0000000..478d1bd --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileName.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class UploadableFileName extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFilePath.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFilePath.php new file mode 100644 index 0000000..8981bab --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFilePath.php @@ -0,0 +1,19 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class UploadableFilePath extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileSize.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileSize.php new file mode 100644 index 0000000..2644771 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/UploadableFileSize.php @@ -0,0 +1,19 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class UploadableFileSize extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Versioned.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Versioned.php new file mode 100644 index 0000000..d68bee8 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Annotation/Versioned.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class Versioned extends Annotation +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver.php new file mode 100644 index 0000000..67e4b9d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver.php @@ -0,0 +1,34 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Driver +{ + /** + * Read extended metadata configuration for + * a single mapped class + * + * @param object $meta + * @param array $config + * + * @return void + */ + public function readExtendedMetadata($meta, array &$config); + + /** + * Passes in the original driver + * + * @param object $driver + * + * @return void + */ + public function setOriginalDriver($driver); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AbstractAnnotationDriver.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AbstractAnnotationDriver.php new file mode 100644 index 0000000..9ce5399 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AbstractAnnotationDriver.php @@ -0,0 +1,111 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class AbstractAnnotationDriver implements AnnotationDriverInterface +{ + /** + * Annotation reader instance + * + * @var object + */ + protected $reader; + + /** + * Original driver if it is available + */ + protected $_originalDriver = null; + + /** + * List of types which are valid for extension + * + * @var array + */ + protected $validTypes = array(); + + /** + * {@inheritDoc} + */ + public function setAnnotationReader($reader) + { + $this->reader = $reader; + } + + /** + * Passes in the mapping read by original driver + * + * @param object $driver + */ + public function setOriginalDriver($driver) + { + $this->_originalDriver = $driver; + } + + /** + * @param object $meta + * + * @return \ReflectionClass + */ + public function getMetaReflectionClass($meta) + { + $class = $meta->getReflectionClass(); + if (!$class) { + // based on recent doctrine 2.3.0-DEV maybe will be fixed in some way + // this happens when running annotation driver in combination with + // static reflection services. This is not the nicest fix + $class = new \ReflectionClass($meta->name); + } + + return $class; + } + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } + + /** + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $meta + * @param array $config + */ + public function validateFullMetadata(ClassMetadata $meta, array $config) + { + } + + /** + * Try to find out related class name out of mapping + * + * @param ClassMetadata $metadata - the mapped class metadata + * @param $name - the related object class name + * @return string - related class name or empty string if does not exist + */ + protected function getRelatedClassName($metadata, $name) + { + if (class_exists($name) || interface_exists($name)) { + return $name; + } + $refl = $metadata->getReflectionClass(); + $ns = $refl->getNamespaceName(); + $className = $ns . '\\' . $name; + return class_exists($className) ? $className : ''; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AnnotationDriverInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AnnotationDriverInterface.php new file mode 100644 index 0000000..513f321 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/AnnotationDriverInterface.php @@ -0,0 +1,28 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface AnnotationDriverInterface extends Driver +{ + /** + * Set annotation reader class + * since older doctrine versions do not provide an interface + * it must provide these methods: + * getClassAnnotations([reflectionClass]) + * getClassAnnotation([reflectionClass], [name]) + * getPropertyAnnotations([reflectionProperty]) + * getPropertyAnnotation([reflectionProperty], [name]) + * + * @param object $reader - annotation reader class + */ + public function setAnnotationReader($reader); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Chain.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Chain.php new file mode 100644 index 0000000..24b5694 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Chain.php @@ -0,0 +1,103 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Chain implements Driver +{ + /** + * The default driver + * + * @var Driver|null + */ + private $defaultDriver; + + /** + * List of drivers nested + * @var Driver[] + */ + private $_drivers = array(); + + /** + * Add a nested driver. + * + * @param Driver $nestedDriver + * @param string $namespace + */ + public function addDriver(Driver $nestedDriver, $namespace) + { + $this->_drivers[$namespace] = $nestedDriver; + } + + /** + * Get the array of nested drivers. + * + * @return Driver[] $drivers + */ + public function getDrivers() + { + return $this->_drivers; + } + + /** + * Get the default driver. + * + * @return Driver|null + */ + public function getDefaultDriver() + { + return $this->defaultDriver; + } + + /** + * Set the default driver. + * + * @param Driver $driver + */ + public function setDefaultDriver(Driver $driver) + { + $this->defaultDriver = $driver; + } + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + foreach ($this->_drivers as $namespace => $driver) { + if (strpos($meta->name, $namespace) === 0) { + $driver->readExtendedMetadata($meta, $config); + + return; + } + } + + if (null !== $this->defaultDriver) { + $this->defaultDriver->readExtendedMetadata($meta, $config); + + return; + } + + // commenting it for customized mapping support, debugging of such cases might get harder + //throw new \Gedmo\Exception\UnexpectedValueException('Class ' . $meta->name . ' is not a valid entity or mapped super class.'); + } + + /** + * Passes in the mapping read by original driver + * + * @param $driver + * @return void + */ + public function setOriginalDriver($driver) + { + //not needed here + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/File.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/File.php new file mode 100644 index 0000000..0198569 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/File.php @@ -0,0 +1,132 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class File implements Driver +{ + /** + * @var FileLocator + */ + protected $locator; + + /** + * File extension, must be set in child class + * @var string + */ + protected $_extension; + + /** + * original driver if it is available + */ + protected $_originalDriver = null; + + public function setLocator(FileLocator $locator) + { + $this->locator = $locator; + } + + /** + * Set the paths for file lookup + * + * @param array $paths + * + * @return void + */ + public function setPaths($paths) + { + $this->_paths = (array) $paths; + } + + /** + * Set the file extension + * + * @param string $extension + * + * @return void + */ + public function setExtension($extension) + { + $this->_extension = $extension; + } + + /** + * Loads a mapping file with the given name and returns a map + * from class/entity names to their corresponding elements. + * + * @param string $file The mapping file to load. + * + * @return array + */ + abstract protected function _loadMappingFile($file); + + /** + * Tries to get a mapping for a given class + * + * @param string $className + * + * @return null|array|object + */ + protected function _getMapping($className) + { + //try loading mapping from original driver first + $mapping = null; + if (!is_null($this->_originalDriver)) { + if ($this->_originalDriver instanceof FileDriver || $this->_originalDriver instanceof AbstractFileDriver) { + $mapping = $this->_originalDriver->getElement($className); + } + } + + //if no mapping found try to load mapping file again + if (is_null($mapping)) { + $yaml = $this->_loadMappingFile($this->locator->findMappingFile($className)); + $mapping = $yaml[$className]; + } + + return $mapping; + } + + /** + * Passes in the mapping read by original driver + * + * @param object $driver + * + * @return void + */ + public function setOriginalDriver($driver) + { + $this->_originalDriver = $driver; + } + + /** + * Try to find out related class name out of mapping + * + * @param $metadata - the mapped class metadata + * @param $name - the related object class name + * @return string - related class name or empty string if does not exist + */ + protected function getRelatedClassName($metadata, $name) + { + if (class_exists($name) || interface_exists($name)) { + return $name; + } + $refl = $metadata->getReflectionClass(); + $ns = $refl->getNamespaceName(); + $className = $ns . '\\' . $name; + return class_exists($className) ? $className : ''; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Xml.php new file mode 100644 index 0000000..a7fdb94 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Driver/Xml.php @@ -0,0 +1,106 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class Xml extends File +{ + const GEDMO_NAMESPACE_URI = 'http://gediminasm.org/schemas/orm/doctrine-extensions-mapping'; + const DOCTRINE_NAMESPACE_URI = 'http://doctrine-project.org/schemas/orm/doctrine-mapping'; + + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.xml'; + + /** + * Get attribute value. + * As we are supporting namespaces the only way to get to the attributes under a node is to use attributes function on it + * + * @param SimpleXMLElement $node + * @param string $attributeName + * + * @return string + */ + protected function _getAttribute(SimpleXmlElement $node, $attributeName) + { + $attributes = $node->attributes(); + + return (string) $attributes[$attributeName]; + } + + /** + * Get boolean attribute value. + * As we are supporting namespaces the only way to get to the attributes under a node is to use attributes function on it + * + * @param SimpleXMLElement $node + * @param string $attributeName + * + * @return boolean + */ + protected function _getBooleanAttribute(SimpleXmlElement $node, $attributeName) + { + $rawValue = strtolower($this->_getAttribute($node, $attributeName)); + if ($rawValue === '1' || $rawValue === 'true') { + return true; + } + if ($rawValue === '0' || $rawValue === 'false') { + return false; + } + throw new InvalidMappingException(sprintf("Attribute %s must have a valid boolean value, '%s' found", $attributeName, $this->_getAttribute($node, $attributeName))); + } + + /** + * does attribute exist under a specific node + * As we are supporting namespaces the only way to get to the attributes under a node is to use attributes function on it + * + * @param SimpleXMLElement $node + * @param string $attributeName + * + * @return string + */ + protected function _isAttributeSet(SimpleXmlElement $node, $attributeName) + { + $attributes = $node->attributes(); + + return isset($attributes[$attributeName]); + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + $result = array(); + $xmlElement = simplexml_load_file($file); + $xmlElement = $xmlElement->children(self::DOCTRINE_NAMESPACE_URI); + + if (isset($xmlElement->entity)) { + foreach ($xmlElement->entity as $entityElement) { + $entityName = $this->_getAttribute($entityElement, 'name'); + $result[$entityName] = $entityElement; + } + } elseif (isset($xmlElement->{'mapped-superclass'})) { + foreach ($xmlElement->{'mapped-superclass'} as $mappedSuperClass) { + $className = $this->_getAttribute($mappedSuperClass, 'name'); + $result[$className] = $mappedSuperClass; + } + } + + return $result; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..1a55ddc --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,184 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ODM implements AdapterInterface +{ + /** + * @var \Doctrine\Common\EventArgs + */ + private $args; + + /** + * @var \Doctrine\ODM\MongoDB\DocumentManager + */ + private $dm; + + /** + * {@inheritdoc} + */ + public function setEventArgs(EventArgs $args) + { + $this->args = $args; + } + + /** + * {@inheritdoc} + */ + public function getDomainObjectName() + { + return 'Document'; + } + + /** + * {@inheritdoc} + */ + public function getManagerName() + { + return 'ODM'; + } + + /** + * {@inheritdoc} + */ + public function getRootObjectClass($meta) + { + return $meta->rootDocumentName; + } + + /** + * Set the document manager + * + * @param \Doctrine\ODM\MongoDB\DocumentManager $dm + */ + public function setDocumentManager(DocumentManager $dm) + { + $this->dm = $dm; + } + + /** + * {@inheritdoc} + */ + public function getObjectManager() + { + if (!is_null($this->dm)) { + return $this->dm; + } + + return $this->__call('getDocumentManager', array()); + } + + /** + * {@inheritdoc} + */ + public function getObjectState($uow, $object) + { + return $uow->getDocumentState($object); + } + + /** + * {@inheritdoc} + */ + public function __call($method, $args) + { + if (is_null($this->args)) { + throw new RuntimeException("Event args must be set before calling its methods"); + } + $method = str_replace('Object', $this->getDomainObjectName(), $method); + + return call_user_func_array(array($this->args, $method), $args); + } + + /** + * {@inheritdoc} + */ + public function getObjectChangeSet($uow, $object) + { + return $uow->getDocumentChangeSet($object); + } + + /** + * {@inheritdoc} + */ + public function getSingleIdentifierFieldName($meta) + { + return $meta->identifier; + } + + /** + * {@inheritdoc} + */ + public function recomputeSingleObjectChangeSet($uow, $meta, $object) + { + $uow->recomputeSingleDocumentChangeSet($meta, $object); + } + + /** + * {@inheritdoc} + */ + public function getScheduledObjectUpdates($uow) + { + $updates = $uow->getScheduledDocumentUpdates(); + $upserts = $uow->getScheduledDocumentUpserts(); + + return array_merge($updates, $upserts); + } + + /** + * {@inheritdoc} + */ + public function getScheduledObjectInsertions($uow) + { + return $uow->getScheduledDocumentInsertions(); + } + + /** + * {@inheritdoc} + */ + public function getScheduledObjectDeletions($uow) + { + return $uow->getScheduledDocumentDeletions(); + } + + /** + * {@inheritdoc} + */ + public function setOriginalObjectProperty($uow, $oid, $property, $value) + { + $uow->setOriginalDocumentProperty($oid, $property, $value); + } + + /** + * {@inheritdoc} + */ + public function clearObjectChangeSet($uow, $oid) + { + $uow->clearDocumentChangeSet($oid); + } + + /** + * Creates a ODM specific LifecycleEventArgs. + * + * @param object $document + * @param \Doctrine\ODM\MongoDB\DocumentManager $documentManager + * + * @return \Doctrine\ODM\MongoDB\Event\LifecycleEventArgs + */ + public function createLifecycleEventArgsInstance($document, $documentManager) + { + return new LifecycleEventArgs($document, $documentManager); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..9775420 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,181 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ORM implements AdapterInterface +{ + /** + * @var \Doctrine\Common\EventArgs + */ + private $args; + + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * {@inheritdoc} + */ + public function setEventArgs(EventArgs $args) + { + $this->args = $args; + } + + /** + * {@inheritdoc} + */ + public function getDomainObjectName() + { + return 'Entity'; + } + + /** + * {@inheritdoc} + */ + public function getManagerName() + { + return 'ORM'; + } + + /** + * {@inheritdoc} + */ + public function getRootObjectClass($meta) + { + return $meta->rootEntityName; + } + + /** + * {@inheritdoc} + */ + public function __call($method, $args) + { + if (is_null($this->args)) { + throw new RuntimeException("Event args must be set before calling its methods"); + } + $method = str_replace('Object', $this->getDomainObjectName(), $method); + + return call_user_func_array(array($this->args, $method), $args); + } + + /** + * Set the entity manager + * + * @param \Doctrine\ORM\EntityManagerInterface $em + */ + public function setEntityManager(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * {@inheritdoc} + */ + public function getObjectManager() + { + if (!is_null($this->em)) { + return $this->em; + } + + return $this->__call('getEntityManager', array()); + } + + /** + * {@inheritdoc} + */ + public function getObjectState($uow, $object) + { + return $uow->getEntityState($object); + } + + /** + * {@inheritdoc} + */ + public function getObjectChangeSet($uow, $object) + { + return $uow->getEntityChangeSet($object); + } + + /** + * {@inheritdoc} + */ + public function getSingleIdentifierFieldName($meta) + { + return $meta->getSingleIdentifierFieldName(); + } + + /** + * {@inheritdoc} + */ + public function recomputeSingleObjectChangeSet($uow, $meta, $object) + { + $uow->recomputeSingleEntityChangeSet($meta, $object); + } + + /** + * {@inheritdoc} + */ + public function getScheduledObjectUpdates($uow) + { + return $uow->getScheduledEntityUpdates(); + } + + /** + * {@inheritdoc} + */ + public function getScheduledObjectInsertions($uow) + { + return $uow->getScheduledEntityInsertions(); + } + + /** + * {@inheritdoc} + */ + public function getScheduledObjectDeletions($uow) + { + return $uow->getScheduledEntityDeletions(); + } + + /** + * {@inheritdoc} + */ + public function setOriginalObjectProperty($uow, $oid, $property, $value) + { + $uow->setOriginalEntityProperty($oid, $property, $value); + } + + /** + * {@inheritdoc} + */ + public function clearObjectChangeSet($uow, $oid) + { + $uow->clearEntityChangeSet($oid); + } + + /** + * Creates a ORM specific LifecycleEventArgs. + * + * @param object $document + * @param \Doctrine\ODM\MongoDB\DocumentManager $documentManager + * + * @return \Doctrine\ODM\MongoDB\Event\LifecycleEventArgs + */ + public function createLifecycleEventArgsInstance($document, $documentManager) + { + return new LifecycleEventArgs($document, $documentManager); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/AdapterInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/AdapterInterface.php new file mode 100644 index 0000000..4763db3 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/Event/AdapterInterface.php @@ -0,0 +1,152 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface AdapterInterface +{ + /** + * Set the eventargs + * + * @param \Doctrine\Common\EventArgs $args + */ + public function setEventArgs(EventArgs $args); + + /** + * Call specific method on event args + * + * @param string $method + * @param array $args + * + * @return mixed + */ + public function __call($method, $args); + + /** + * Get the name of domain object + * + * @return string + */ + public function getDomainObjectName(); + + /** + * Get the name of used manager for this + * event adapter + * + * @return string + */ + public function getManagerName(); + + /** + * Get the root object class, handles inheritance + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $meta + * + * @return string + */ + public function getRootObjectClass($meta); + + /** + * Get used object manager + * + * @return \Doctrine\Common\Persistence\ObjectManager + */ + public function getObjectManager(); + + /** + * Get object state + * + * @param UnitOfWork $uow + * @param object $object + * + * @return int The document state. + */ + public function getObjectState($uow, $object); + + /** + * Get the object changeset from a UnitOfWork + * + * @param UnitOfWork $uow + * @param object $object + * + * @return array + */ + public function getObjectChangeSet($uow, $object); + + /** + * Get the single identifier field name + * + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $meta + * + * @return string + */ + public function getSingleIdentifierFieldName($meta); + + /** + * Recompute the single object changeset from a UnitOfWork + * + * @param UnitOfWork $uow + * @param \Doctrine\Common\Persistence\Mapping\ClassMetadata $meta + * @param object $object + * + * @return void + */ + public function recomputeSingleObjectChangeSet($uow, $meta, $object); + + /** + * Get the scheduled object updates from a UnitOfWork + * + * @param UnitOfWork $uow + * + * @return array + */ + public function getScheduledObjectUpdates($uow); + + /** + * Get the scheduled object insertions from a UnitOfWork + * + * @param UnitOfWork $uow + * + * @return array + */ + public function getScheduledObjectInsertions($uow); + + /** + * Get the scheduled object deletions from a UnitOfWork + * + * @param UnitOfWork $uow + * + * @return array + */ + public function getScheduledObjectDeletions($uow); + + /** + * Sets a property value of the original data array of an object + * + * @param UnitOfWork $uow + * @param string $oid + * @param string $property + * @param mixed $value + * + * @return void + */ + public function setOriginalObjectProperty($uow, $oid, $property, $value); + + /** + * Clears the property changeset of the object with the given OID. + * + * @param UnitOfWork $uow + * @param string $oid The object's OID. + */ + public function clearObjectChangeSet($uow, $oid); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/ExtensionMetadataFactory.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/ExtensionMetadataFactory.php new file mode 100644 index 0000000..1784998 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/ExtensionMetadataFactory.php @@ -0,0 +1,180 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ExtensionMetadataFactory +{ + /** + * Extension driver + * @var \Gedmo\Mapping\Driver + */ + protected $driver; + + /** + * Object manager, entity or document + * @var object + */ + protected $objectManager; + + /** + * Extension namespace + * + * @var string + */ + protected $extensionNamespace; + + /** + * Custom annotation reader + * + * @var object + */ + protected $annotationReader; + + /** + * Initializes extension driver + * + * @param ObjectManager $objectManager + * @param string $extensionNamespace + * @param object $annotationReader + */ + public function __construct(ObjectManager $objectManager, $extensionNamespace, $annotationReader) + { + $this->objectManager = $objectManager; + $this->annotationReader = $annotationReader; + $this->extensionNamespace = $extensionNamespace; + $omDriver = $objectManager->getConfiguration()->getMetadataDriverImpl(); + $this->driver = $this->getDriver($omDriver); + } + + /** + * Reads extension metadata + * + * @param object $meta + * @return array - the metatada configuration + */ + public function getExtensionMetadata($meta) + { + if ($meta->isMappedSuperclass) { + return; // ignore mappedSuperclasses for now + } + $config = array(); + $cmf = $this->objectManager->getMetadataFactory(); + $useObjectName = $meta->name; + // collect metadata from inherited classes + if (null !== $meta->reflClass) { + foreach (array_reverse(class_parents($meta->name)) as $parentClass) { + // read only inherited mapped classes + if ($cmf->hasMetadataFor($parentClass)) { + $class = $this->objectManager->getClassMetadata($parentClass); + $this->driver->readExtendedMetadata($class, $config); + $isBaseInheritanceLevel = !$class->isInheritanceTypeNone() + && !$class->parentClasses + && $config + ; + if ($isBaseInheritanceLevel) { + $useObjectName = $class->name; + } + } + } + $this->driver->readExtendedMetadata($meta, $config); + } + if ($config) { + $config['useObjectClass'] = $useObjectName; + } + + // cache the metadata (even if it's empty) + // caching empty metadata will prevent re-parsing non-existent annotations + $cacheId = self::getCacheId($meta->name, $this->extensionNamespace); + if ($cacheDriver = $cmf->getCacheDriver()) { + $cacheDriver->save($cacheId, $config, null); + } + + return $config; + } + + /** + * Get the cache id + * + * @param string $className + * @param string $extensionNamespace + * @return string + */ + public static function getCacheId($className, $extensionNamespace) + { + return $className.'\\$'.strtoupper(str_replace('\\', '_', $extensionNamespace)).'_CLASSMETADATA'; + } + + /** + * Get the extended driver instance which will + * read the metadata required by extension + * + * @param object $omDriver + * @throws \Gedmo\Exception\RuntimeException if driver was not found in extension + * @return \Gedmo\Mapping\Driver + */ + protected function getDriver($omDriver) + { + $driver = null; + $className = get_class($omDriver); + $driverName = substr($className, strrpos($className, '\\') + 1); + if ($omDriver instanceof MappingDriverChain || $driverName == 'DriverChain') { + $driver = new Driver\Chain(); + foreach ($omDriver->getDrivers() as $namespace => $nestedOmDriver) { + $driver->addDriver($this->getDriver($nestedOmDriver), $namespace); + } + if (version_compare(CommonLibVer::VERSION, '2.3.0', '>=') && $omDriver->getDefaultDriver() !== null) { + $driver->setDefaultDriver($this->getDriver($omDriver->getDefaultDriver())); + } + } else { + $driverName = substr($driverName, 0, strpos($driverName, 'Driver')); + $isSimplified = false; + if (substr($driverName, 0, 10) === 'Simplified') { + // support for simplified file drivers + $driverName = substr($driverName, 10); + $isSimplified = true; + } + // create driver instance + $driverClassName = $this->extensionNamespace.'\Mapping\Driver\\'.$driverName; + if (!class_exists($driverClassName)) { + $driverClassName = $this->extensionNamespace.'\Mapping\Driver\Annotation'; + if (!class_exists($driverClassName)) { + throw new \Gedmo\Exception\RuntimeException("Failed to fallback to annotation driver: ({$driverClassName}), extension driver was not found."); + } + } + $driver = new $driverClassName(); + $driver->setOriginalDriver($omDriver); + if ($driver instanceof FileDriver) { + /** @var $driver FileDriver */ + if ($omDriver instanceof MappingDriver) { + $driver->setLocator($omDriver->getLocator()); + // BC for Doctrine 2.2 + } elseif ($isSimplified) { + $driver->setLocator(new SymfonyFileLocator($omDriver->getNamespacePrefixes(), $omDriver->getFileExtension())); + } else { + $driver->setLocator(new DefaultFileLocator($omDriver->getPaths(), $omDriver->getFileExtension())); + } + } + if ($driver instanceof AnnotationDriverInterface) { + $driver->setAnnotationReader($this->annotationReader); + } + } + + return $driver; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/MappedEventSubscriber.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/MappedEventSubscriber.php new file mode 100644 index 0000000..4d833da --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Mapping/MappedEventSubscriber.php @@ -0,0 +1,283 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class MappedEventSubscriber implements EventSubscriber +{ + /** + * Static List of cached object configurations + * leaving it static for reasons to look into + * other listener configuration + * + * @var array + */ + protected static $configurations = array(); + + /** + * Listener name, etc: sluggable + * + * @var string + */ + protected $name; + + /** + * ExtensionMetadataFactory used to read the extension + * metadata through the extension drivers + * + * @var ExtensionMetadataFactory + */ + private $extensionMetadataFactory = array(); + + /** + * List of event adapters used for this listener + * + * @var array + */ + private $adapters = array(); + + /** + * Custom annotation reader + * + * @var object + */ + private $annotationReader; + + /** + * @var \Doctrine\Common\Annotations\AnnotationReader + */ + private static $defaultAnnotationReader; + + /** + * Constructor + */ + public function __construct() + { + $parts = explode('\\', $this->getNamespace()); + $this->name = end($parts); + } + + /** + * Get an event adapter to handle event specific + * methods + * + * @param EventArgs $args + * + * @throws \Gedmo\Exception\InvalidArgumentException - if event is not recognized + * + * @return \Gedmo\Mapping\Event\AdapterInterface + */ + protected function getEventAdapter(EventArgs $args) + { + $class = get_class($args); + if (preg_match('@Doctrine\\\([^\\\]+)@', $class, $m) && in_array($m[1], array('ODM', 'ORM'))) { + if (!isset($this->adapters[$m[1]])) { + $adapterClass = $this->getNamespace().'\\Mapping\\Event\\Adapter\\'.$m[1]; + if (!class_exists($adapterClass)) { + $adapterClass = 'Gedmo\\Mapping\\Event\\Adapter\\'.$m[1]; + } + $this->adapters[$m[1]] = new $adapterClass(); + } + $this->adapters[$m[1]]->setEventArgs($args); + + return $this->adapters[$m[1]]; + } else { + throw new \Gedmo\Exception\InvalidArgumentException('Event mapper does not support event arg class: '.$class); + } + } + + /** + * Get the configuration for specific object class + * if cache driver is present it scans it also + * + * @param ObjectManager $objectManager + * @param string $class + * + * @return array + */ + public function getConfiguration(ObjectManager $objectManager, $class) + { + $config = array(); + if (isset(self::$configurations[$this->name][$class])) { + $config = self::$configurations[$this->name][$class]; + } else { + $factory = $objectManager->getMetadataFactory(); + $cacheDriver = $factory->getCacheDriver(); + if ($cacheDriver) { + $cacheId = ExtensionMetadataFactory::getCacheId($class, $this->getNamespace()); + if (($cached = $cacheDriver->fetch($cacheId)) !== false) { + self::$configurations[$this->name][$class] = $cached; + $config = $cached; + } else { + // re-generate metadata on cache miss + $this->loadMetadataForObjectClass($objectManager, $factory->getMetadataFor($class)); + if (isset(self::$configurations[$this->name][$class])) { + $config = self::$configurations[$this->name][$class]; + } + } + + $objectClass = isset($config['useObjectClass']) ? $config['useObjectClass'] : $class; + if ($objectClass !== $class) { + $this->getConfiguration($objectManager, $objectClass); + } + } + } + + return $config; + } + + /** + * Get extended metadata mapping reader + * + * @param ObjectManager $objectManager + * + * @return ExtensionMetadataFactory + */ + public function getExtensionMetadataFactory(ObjectManager $objectManager) + { + $oid = spl_object_hash($objectManager); + if (!isset($this->extensionMetadataFactory[$oid])) { + if (is_null($this->annotationReader)) { + // create default annotation reader for extensions + $this->annotationReader = $this->getDefaultAnnotationReader(); + } + $this->extensionMetadataFactory[$oid] = new ExtensionMetadataFactory( + $objectManager, + $this->getNamespace(), + $this->annotationReader + ); + } + + return $this->extensionMetadataFactory[$oid]; + } + + /** + * Set annotation reader class + * since older doctrine versions do not provide an interface + * it must provide these methods: + * getClassAnnotations([reflectionClass]) + * getClassAnnotation([reflectionClass], [name]) + * getPropertyAnnotations([reflectionProperty]) + * getPropertyAnnotation([reflectionProperty], [name]) + * + * @param Reader $reader - annotation reader class + */ + public function setAnnotationReader($reader) + { + $this->annotationReader = $reader; + } + + /** + * Scans the objects for extended annotations + * event subscribers must subscribe to loadClassMetadata event + * + * @param ObjectManager $objectManager + * @param object $metadata + * @return void + */ + public function loadMetadataForObjectClass(ObjectManager $objectManager, $metadata) + { + $factory = $this->getExtensionMetadataFactory($objectManager); + try { + $config = $factory->getExtensionMetadata($metadata); + } catch (\ReflectionException $e) { + // entity\document generator is running + $config = false; // will not store a cached version, to remap later + } + if ($config) { + self::$configurations[$this->name][$metadata->name] = $config; + } + } + + /** + * Get the namespace of extension event subscriber. + * used for cache id of extensions also to know where + * to find Mapping drivers and event adapters + * + * @return string + */ + abstract protected function getNamespace(); + + /** + * Create default annotation reader for extensions + * + * @return \Doctrine\Common\Annotations\AnnotationReader + */ + private function getDefaultAnnotationReader() + { + if (null === self::$defaultAnnotationReader) { + if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) { + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace( + 'Gedmo\\Mapping\\Annotation', + __DIR__.'/../../' + ); + $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache()); + } elseif (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0RC4-DEV', '>=')) { + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + \Doctrine\Common\Annotations\AnnotationRegistry::registerAutoloadNamespace( + 'Gedmo\\Mapping\\Annotation', + __DIR__.'/../../' + ); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new ArrayCache()); + } elseif (version_compare(\Doctrine\Common\Version::VERSION, '2.1.0-BETA3-DEV', '>=')) { + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + $reader->setIgnoreNotImportedAnnotations(true); + $reader->setAnnotationNamespaceAlias('Gedmo\\Mapping\\Annotation\\', 'gedmo'); + $reader->setEnableParsePhpImports(false); + $reader->setAutoloadAnnotations(true); + $reader = new \Doctrine\Common\Annotations\CachedReader( + new \Doctrine\Common\Annotations\IndexedReader($reader), new ArrayCache() + ); + } else { + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + $reader->setAutoloadAnnotations(true); + $reader->setAnnotationNamespaceAlias('Gedmo\\Mapping\\Annotation\\', 'gedmo'); + $reader->setDefaultAnnotationNamespace('Doctrine\ORM\Mapping\\'); + } + self::$defaultAnnotationReader = $reader; + } + + return self::$defaultAnnotationReader; + } + + /** + * Sets the value for a mapped field + * + * @param AdapterInterface $adapter + * @param object $object + * @param string $field + * @param mixed $oldValue + * @param mixed $newValue + */ + protected function setFieldValue(AdapterInterface $adapter, $object, $field, $oldValue, $newValue) + { + $manager = $adapter->getObjectManager(); + $meta = $manager->getClassMetadata(get_class($object)); + $uow = $manager->getUnitOfWork(); + + $meta->getReflectionProperty($field)->setValue($object, $newValue); + $uow->propertyChanged($object, $field, $oldValue, $newValue); + $adapter->recomputeSingleObjectChangeSet($uow, $meta, $object); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..72730bd --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Annotation.php @@ -0,0 +1,77 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to identify the fields which manages the reference integrity + */ + const REFERENCE_INTEGRITY = 'Gedmo\\Mapping\\Annotation\\ReferenceIntegrity'; + + /** + * ReferenceIntegrityAction extension annotation + */ + const ACTION = 'Gedmo\\Mapping\\Annotation\\ReferenceIntegrityAction'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $validator = new Validator(); + $reflClass = $this->getMetaReflectionClass($meta); + + foreach ($reflClass->getProperties() as $reflProperty) { + if ($referenceIntegrity = $this->reader->getPropertyAnnotation($reflProperty, self::REFERENCE_INTEGRITY)) { + $property = $reflProperty->getName(); + if (!$meta->hasField($property)) { + throw new InvalidMappingException( + sprintf( + "Unable to find reference integrity [%s] as mapped property in entity - %s", + $property, + $meta->name + ) + ); + } + + $fieldMapping = $meta->getFieldMapping($property); + if (!isset($fieldMapping['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "'mappedBy' should be set on '%s' in '%s'", + $property, + $meta->name + ) + ); + } + + if (!in_array($referenceIntegrity->value, $validator->getIntegrityActions())) { + throw new InvalidMappingException( + sprintf( + "Field - [%s] does not have a valid integrity option, [%s] in class - %s", + $property, + implode($validator->getIntegrityActions(), ', '), + $meta->name + ) + ); + } + + $config['referenceIntegrity'][$property] = $referenceIntegrity->value; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..dd156e2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Driver/Yaml.php @@ -0,0 +1,83 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + $validator = new Validator(); + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $property => $fieldMapping) { + if (isset($fieldMapping['gedmo']['referenceIntegrity'])) { + if (!$meta->hasField($property)) { + throw new InvalidMappingException( + sprintf( + "Unable to find reference integrity [%s] as mapped property in entity - %s", + $property, + $meta->name + ) + ); + } + + if (empty($mapping['fields'][$property]['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "'mappedBy' should be set on '%s' in '%s'", + $property, + $meta->name + ) + ); + } + + if (!in_array($fieldMapping['gedmo']['referenceIntegrity'], $validator->getIntegrityActions())) { + throw new InvalidMappingException( + sprintf( + "Field - [%s] does not have a valid integrity option, [%s] in class - %s", + $property, + implode($validator->getIntegrityActions(), ', '), + $meta->name + ) + ); + } + + $config['referenceIntegrity'][$property][$mapping['fields'][$property]['mappedBy']] = + $fieldMapping['gedmo']['referenceIntegrity']; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Validator.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Validator.php new file mode 100644 index 0000000..d1f0d51 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/Mapping/Validator.php @@ -0,0 +1,38 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class Validator +{ + const NULLIFY = 'nullify'; + const PULL = 'pull'; + const RESTRICT = 'restrict'; + + /** + * List of actions which are valid as integrity check + * + * @var array + */ + private $integrityActions = array( + self::NULLIFY, + self::PULL, + self::RESTRICT, + ); + + /** + * Returns a list of available integrity actions + * + * @return array + */ + public function getIntegrityActions() + { + return $this->integrityActions; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrity.php new file mode 100644 index 0000000..2a74f84 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrity.php @@ -0,0 +1,47 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface ReferenceIntegrity +{ + /** + * ReferenceIntegrity expects certain settings to be required + * in combination with an association + */ + + /** + * example + * @ODM\ReferenceOne(targetDocument="Article", nullable="true", mappedBy="type") + * @Gedmo\ReferenceIntegrity("nullify") + * @var Article + */ + + /** + * example + * @ODM\ReferenceOne(targetDocument="Article", nullable="true", mappedBy="type") + * @Gedmo\ReferenceIntegrity("restrict") + * @var Article + */ + + /** + * example + * @ODM\ReferenceMany(targetDocument="Article", nullable="true", mappedBy="type") + * @Gedmo\ReferenceIntegrity("nullify") + * @var Doctrine\Common\Collections\ArrayCollection + */ + + /** + * example + * @ODM\ReferenceMany(targetDocument="Article", nullable="true", mappedBy="type") + * @Gedmo\ReferenceIntegrity("restrict") + * @var Doctrine\Common\Collections\ArrayCollection + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrityListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrityListener.php new file mode 100644 index 0000000..760a16d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/ReferenceIntegrity/ReferenceIntegrityListener.php @@ -0,0 +1,181 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ReferenceIntegrityListener extends MappedEventSubscriber +{ + /** + * {@inheritDoc} + */ + public function getSubscribedEvents() + { + return array( + 'loadClassMetadata', + 'preRemove', + ); + } + + /** + * Maps additional metadata for the Document + * + * @param EventArgs $eventArgs + * @return void + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * Looks for referenced objects being removed + * to nullify the relation or throw an exception + * + * @param EventArgs $args + * @return void + */ + public function preRemove(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $class = get_class($object); + $meta = $om->getClassMetadata($class); + + if ($config = $this->getConfiguration($om, $class)) { + foreach ($config['referenceIntegrity'] as $property => $action) { + $reflProp = $meta->getReflectionProperty($property); + $refDoc = $reflProp->getValue($object); + $fieldMapping = $meta->getFieldMapping($property); + + switch ($action) { + case Validator::NULLIFY: + if (!isset($fieldMapping['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "Reference '%s' on '%s' should have 'mappedBy' option defined", + $property, + $meta->name + ) + ); + } + + $subMeta = $om->getClassMetadata($fieldMapping['targetDocument']); + + if (!$subMeta->hasField($fieldMapping['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "Unable to find reference integrity [%s] as mapped property in entity - %s", + $fieldMapping['mappedBy'], + $fieldMapping['targetDocument'] + ) + ); + } + + $refReflProp = $subMeta->getReflectionProperty($fieldMapping['mappedBy']); + + if ($meta->isCollectionValuedReference($property)) { + foreach ($refDoc as $refObj) { + $refReflProp->setValue($refObj, null); + $om->persist($refObj); + } + } else { + $refReflProp->setValue($refDoc, null); + $om->persist($refDoc); + } + + break; + case Validator::PULL: + if (!isset($fieldMapping['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "Reference '%s' on '%s' should have 'mappedBy' option defined", + $property, + $meta->name + ) + ); + } + + $subMeta = $om->getClassMetadata($fieldMapping['targetDocument']); + + if (!$subMeta->hasField($fieldMapping['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "Unable to find reference integrity [%s] as mapped property in entity - %s", + $fieldMapping['mappedBy'], + $fieldMapping['targetDocument'] + ) + ); + } + + if (!$subMeta->isCollectionValuedReference($fieldMapping['mappedBy'])) { + throw new InvalidMappingException( + sprintf( + "Reference integrity [%s] mapped property in entity - %s should be a Reference Many", + $fieldMapping['mappedBy'], + $fieldMapping['targetDocument'] + ) + ); + } + + $refReflProp = $subMeta->getReflectionProperty($fieldMapping['mappedBy']); + + if ($meta->isCollectionValuedReference($property)) { + foreach ($refDoc as $refObj) { + $collection = $refReflProp->getValue($refObj); + $collection->removeElement($object); + $refReflProp->setValue($refObj, $collection); + $om->persist($refObj); + } + } else if (is_object($refDoc)) { + $collection = $refReflProp->getValue($refDoc); + $collection->removeElement($object); + $refReflProp->setValue($refDoc, $collection); + $om->persist($refDoc); + } + + break; + case Validator::RESTRICT: + if ($meta->isCollectionValuedReference($property) && $refDoc->count() > 0) { + throw new ReferenceIntegrityStrictException( + sprintf( + "The reference integrity for the '%s' collection is restricted", + $fieldMapping['targetDocument'] + ) + ); + } + if ($meta->isSingleValuedReference($property) && !is_null($refDoc)) { + throw new ReferenceIntegrityStrictException( + sprintf( + "The reference integrity for the '%s' document is restricted", + $fieldMapping['targetDocument'] + ) + ); + } + + break; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/LazyCollection.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/LazyCollection.php new file mode 100644 index 0000000..f28bf47 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/LazyCollection.php @@ -0,0 +1,241 @@ + + * @author Bulat Shakirzyanov + * @author Jonathan H. Wage + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class LazyCollection implements Collection +{ + private $results; + private $callback; + + public function __construct($callback) + { + $this->callback = $callback; + } + + public function add($element) + { + $this->initialize(); + + return $this->results->add($element); + } + + public function clear() + { + $this->initialize(); + + return $this->results->clear(); + } + + public function contains($element) + { + $this->initialize(); + + return $this->results->contains($element); + } + + public function containsKey($key) + { + $this->initialize(); + + return $this->results->containsKey($key); + } + + public function current() + { + $this->initialize(); + + return $this->results->current(); + } + + public function exists(\Closure $p) + { + $this->initialize(); + + return $this->results->exists($p); + } + + public function filter(\Closure $p) + { + $this->initialize(); + + return $this->results->filter($p); + } + + public function first() + { + $this->initialize(); + + return $this->results->first(); + } + + public function forAll(\Closure $p) + { + $this->initialize(); + + return $this->results->forAll($p); + } + + public function get($key) + { + $this->initialize(); + + return $this->results->get($key); + } + + public function getKeys() + { + $this->initialize(); + + return $this->results->getKeys(); + } + + public function getValues() + { + $this->initialize(); + + return $this->results->getValues(); + } + + public function indexOf($element) + { + $this->initialize(); + + return $this->results->indexOf($element); + } + + public function isEmpty() + { + $this->initialize(); + + return $this->results->isEmpty(); + } + + public function key() + { + $this->initialize(); + + return $this->results->key(); + } + + public function last() + { + $this->initialize(); + + return $this->results->last(); + } + + public function map(\Closure $func) + { + $this->initialize(); + + return $this->results->map($func); + } + + public function next() + { + $this->initialize(); + + return $this->results->next(); + } + + public function partition(\Closure $p) + { + $this->initialize(); + + return $this->results->partition($p); + } + + public function remove($key) + { + $this->initialize(); + + return $this->results->remove($key); + } + + public function removeElement($element) + { + $this->initialize(); + + return $this->results->removeElement($element); + } + + public function set($key, $value) + { + $this->initialize(); + + return $this->results->set($key, $value); + } + + public function slice($offset, $length = null) + { + $this->initialize(); + + return $this->results->slice($offset, $length); + } + + public function toArray() + { + $this->initialize(); + + return $this->results->toArray(); + } + + public function offsetExists($offset) + { + $this->initialize(); + + return $this->results->offsetExists($offset); + } + + public function offsetGet($offset) + { + $this->initialize(); + + return $this->results->offsetGet($offset); + } + + public function offsetSet($offset, $value) + { + $this->initialize(); + + return $this->results->offsetSet($offset, $value); + } + + public function offsetUnset($offset) + { + $this->initialize(); + + return $this->results->offsetUnset($offset); + } + + public function getIterator() + { + $this->initialize(); + + return $this->results->getIterator(); + } + + public function count() + { + $this->initialize(); + + return $this->results->count(); + } + + private function initialize() + { + if (null === $this->results) { + $this->results = call_user_func($this->callback); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..2b1f278 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Annotation.php @@ -0,0 +1,99 @@ + + * @author Bulat Shakirzyanov + * @author Jonathan H. Wage + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation implements AnnotationDriverInterface +{ + /** + * Annotation to mark field as reference to one + */ + const REFERENCE_ONE = 'Gedmo\\Mapping\\Annotation\\ReferenceOne'; + + /** + * Annotation to mark field as reference to many + */ + const REFERENCE_MANY = 'Gedmo\\Mapping\\Annotation\\ReferenceMany'; + + /** + * Annotation to mark field as reference to many + */ + const REFERENCE_MANY_EMBED = 'Gedmo\\Mapping\\Annotation\\ReferenceManyEmbed'; + + private $annotations = array( + 'referenceOne' => self::REFERENCE_ONE, + 'referenceMany' => self::REFERENCE_MANY, + 'referenceManyEmbed' => self::REFERENCE_MANY_EMBED, + ); + + /** + * Annotation reader instance + * + * @var object + */ + private $reader; + + /** + * original driver if it is available + */ + protected $_originalDriver = null; + + /** + * {@inheritDoc} + */ + public function setAnnotationReader($reader) + { + $this->reader = $reader; + } + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $meta->getReflectionClass(); + foreach ($this->annotations as $key => $annotation) { + $config[$key] = array(); + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + + if ($reference = $this->reader->getPropertyAnnotation($property, $annotation)) { + $config[$key][$property->getName()] = array( + 'field' => $property->getName(), + 'type' => $reference->type, + 'class' => $reference->class, + 'identifier' => $reference->identifier, + 'mappedBy' => $reference->mappedBy, + 'inversedBy' => $reference->inversedBy, + ); + } + } + } + } + + /** + * Passes in the mapping read by original driver + * + * @param $driver + * @return void + */ + public function setOriginalDriver($driver) + { + $this->_originalDriver = $driver; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Xml.php new file mode 100644 index 0000000..35b30ee --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Xml.php @@ -0,0 +1,109 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * @var array + */ + private $validTypes = array( + 'document', + 'entity' + ); + + /** + * @var array + */ + private $validReferences = array( + 'referenceOne', + 'referenceMany', + 'referenceManyEmbed' + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + $xmlDoctrine = $xml; + + $xml = $xml->children(self::GEDMO_NAMESPACE_URI); + + if ($xmlDoctrine->getName() === 'entity' || $xmlDoctrine->getName() === 'document' || $xmlDoctrine->getName() === 'mapped-superclass') { + if (isset($xml->reference)) { + /** + * @var \SimpleXMLElement $element + */ + foreach ($xml->reference as $element) { + if (!$this->_isAttributeSet($element, 'type')) { + throw new InvalidMappingException("Reference type (document or entity) is not set in class - {$meta->name}"); + } + + $type = $this->_getAttribute($element, 'type'); + if (!in_array($type, $this->validTypes)) { + throw new InvalidMappingException( + $type . + ' is not a valid reference type, valid types are: ' . + implode(', ', $this->validTypes) + ); + } + + $reference = $this->_getAttribute($element, 'reference'); + if (!in_array($reference, $this->validReferences)) { + throw new InvalidMappingException( + $reference . + ' is not a valid reference, valid references are: ' . + implode(', ', $this->validReferences) + ); + } + + if (!$this->_isAttributeSet($element, 'field')) { + throw new InvalidMappingException("Reference field is not set in class - {$meta->name}"); + } + $field = $this->_getAttribute($element, 'field'); + + if (!$this->_isAttributeSet($element, 'class')) { + throw new InvalidMappingException("Reference field is not set in class - {$meta->name}"); + } + $class = $this->_getAttribute($element, 'class'); + + if (!$this->_isAttributeSet($element, 'identifier')) { + throw new InvalidMappingException("Reference identifier is not set in class - {$meta->name}"); + } + $identifier = $this->_getAttribute($element, 'identifier'); + + $config[$reference][$field] = array( + 'field' => $field, + 'type' => $type, + 'class' => $class, + 'identifier' => $identifier + ); + + if (!$this->_isAttributeSet($element, 'mappedBy')) { + $config[$reference][$field]['mappedBy'] = $this->_getAttribute($element, 'mappedBy'); + } + if (!$this->_isAttributeSet($element, 'inversedBy')) { + $config[$reference][$field]['inversedBy'] = $this->_getAttribute($element, 'inversedBy'); + } + } + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..37bccd8 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Driver/Yaml.php @@ -0,0 +1,77 @@ + + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + private $validReferences = array( + 'referenceOne' => array(), + 'referenceMany' => array(), + 'referenceManyEmbed' => array(), + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['gedmo']) && isset($mapping['gedmo']['reference'])) { + + foreach ($mapping['gedmo']['reference'] as $field => $fieldMapping) { + $reference = $fieldMapping['reference']; + + if (!in_array($reference, array_keys($this->validReferences))) { + throw new InvalidMappingException( + $reference . + ' is not a valid reference, valid references are: ' . + implode(', ', array_keys($this->validReferences)) + ); + } + + $config[$reference][$field] = array( + 'field' => $field, + 'type' => $fieldMapping['type'], + 'class' => $fieldMapping['class'], + ); + + if (array_key_exists('mappedBy', $fieldMapping)) { + $config[$reference][$field]['mappedBy'] = $fieldMapping['mappedBy']; + + } + + if (array_key_exists('identifier', $fieldMapping)) { + $config[$reference][$field]['identifier'] = $fieldMapping['identifier']; + + } + + if (array_key_exists('inversedBy', $fieldMapping)) { + $config[$reference][$field]['inversedBy'] = $fieldMapping['inversedBy']; + } + } + } + $config = array_merge($this->validReferences, $config); + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse($file); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..8f301ed --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,94 @@ + + * @author Bulat Shakirzyanov + * @author Jonathan H. Wage + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements ReferencesAdapter +{ + /** + * @inheritDoc + */ + public function getIdentifier($om, $object, $single = true) + { + if ($om instanceof DocumentManager) { + return $this->extractIdentifier($om, $object, $single); + } + + if ($om instanceof EntityManagerInterface) { + if ($object instanceof ORMProxy) { + $id = $om->getUnitOfWork()->getEntityIdentifier($object); + } else { + $meta = $om->getClassMetadata(get_class($object)); + $id = array(); + foreach ($meta->identifier as $name) { + $id[$name] = $meta->getReflectionProperty($name)->getValue($object); + // return null if one of identifiers is missing + if (!$id[$name]) { + return null; + } + } + } + + if ($single) { + $id = current($id); + } + + return $id; + } + } + + /** + * @inheritDoc + */ + public function getSingleReference($om, $class, $identifier) + { + $this->throwIfNotEntityManager($om); + $meta = $om->getClassMetadata($class); + + if (!$meta->isInheritanceTypeNone()) { + return $om->find($class, $identifier); + } + + return $om->getReference($class, $identifier); + } + + /** + * @inheritDoc + */ + public function extractIdentifier($om, $object, $single = true) + { + $meta = $om->getClassMetadata(get_class($object)); + if ($object instanceof MongoDBProxy) { + $id = $om->getUnitOfWork()->getDocumentIdentifier($object); + } else { + $id = $meta->getReflectionProperty($meta->identifier)->getValue($object); + } + + if ($single || !$id) { + return $id; + } else { + return array($meta->identifier => $id); + } + } + + /** + * Override so we don't get an exception. We want to allow this. + */ + private function throwIfNotEntityManager(EntityManagerInterface $em) + { + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..b3fe825 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,119 @@ + + * @author Bulat Shakirzyanov + * @author Jonathan H. Wage + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements ReferencesAdapter +{ + /** + * @inheritDoc + */ + public function getIdentifier($om, $object, $single = true) + { + if ($om instanceof EntityManagerInterface) { + return $this->extractIdentifier($om, $object, $single); + } + + if ($om instanceof MongoDocumentManager) { + $meta = $om->getClassMetadata(get_class($object)); + if ($object instanceof MongoDBProxy) { + $id = $om->getUnitOfWork()->getDocumentIdentifier($object); + } else { + $id = $meta->getReflectionProperty($meta->identifier)->getValue($object); + } + + if ($single || !$id) { + return $id; + } + + return array($meta->identifier => $id); + } + + if ($om instanceof PhpcrDocumentManager) { + $meta = $om->getClassMetadata(get_class($object)); + $id = $meta->getReflectionProperty($meta->identifier)->getValue($object); + + if ($single || !$id) { + return $id; + } + + return array($meta->identifier => $id); + } + } + + /** + * @inheritDoc + */ + public function getSingleReference($om, $class, $identifier) + { + $this->throwIfNotDocumentManager($om); + $meta = $om->getClassMetadata($class); + + if ($om instanceof MongoDocumentManager) { + if (!$meta->isInheritanceTypeNone()) { + return $om->find($class, $identifier); + } + } + + return $om->getReference($class, $identifier); + } + + /** + * @inheritDoc + */ + public function extractIdentifier($om, $object, $single = true) + { + if ($object instanceof ORMProxy) { + $id = $om->getUnitOfWork()->getEntityIdentifier($object); + } else { + $meta = $om->getClassMetadata(get_class($object)); + $id = array(); + foreach ($meta->identifier as $name) { + $id[$name] = $meta->getReflectionProperty($name)->getValue($object); + // return null if one of identifiers is missing + if (!$id[$name]) { + return null; + } + } + } + + if ($single) { + $id = current($id); + } + + return $id; + } + + /** + * Override so we don't get an exception. We want to allow this. + */ + private function throwIfNotDocumentManager($dm) + { + if (!($dm instanceof MongoDocumentManager) && !($dm instanceof PhpcrDocumentManager)) { + throw new InvalidArgumentException( + sprintf( + 'Expected a %s or %s instance but got "%s"', + 'Doctrine\ODM\MongoDB\DocumentManager', + 'Doctrine\ODM\PHPCR\DocumentManager', + is_object($dm) ? get_class($dm) : gettype($dm) + ) + ); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/ReferencesAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/ReferencesAdapter.php new file mode 100644 index 0000000..6ec827d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/Mapping/Event/ReferencesAdapter.php @@ -0,0 +1,48 @@ + + * @author Bulat Shakirzyanov + * @author Jonathan H. Wage + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface ReferencesAdapter extends AdapterInterface +{ + /** + * Gets the identifier of the given object using the passed ObjectManager. + * + * @param ObjectManager $om + * @param object $object + * @param bool $single + * + * @return array|string|int $id - array or single identifier + */ + public function getIdentifier($om, $object, $single = true); + + /** + * Gets a single reference for the given ObjectManager, class and identifier. + * + * @param ObjectManager $om + * @param string $class + * @param array|string|int $identifier + **/ + public function getSingleReference($om, $class, $identifier); + + /** + * Extracts identifiers from object or proxy. + * + * @param ObjectManager $om + * @param object $object + * @param bool $single + * + * @return array|string|int - array or single identifier + */ + public function extractIdentifier($om, $object, $single = true); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/ReferencesListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/ReferencesListener.php new file mode 100644 index 0000000..b6eca05 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/References/ReferencesListener.php @@ -0,0 +1,209 @@ + + * @author Bulat Shakirzyanov + * @author Jonathan H. Wage + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ReferencesListener extends MappedEventSubscriber +{ + private $managers; + + public function __construct(array $managers = array()) + { + $this->managers = $managers; + } + + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass( + $ea->getObjectManager(), $eventArgs->getClassMetadata() + ); + } + + public function postLoad(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + + if (isset($config['referenceOne'])) { + foreach ($config['referenceOne'] as $mapping) { + $property = $meta->reflClass->getProperty($mapping['field']); + $property->setAccessible(true); + if (isset($mapping['identifier'])) { + $referencedObjectId = $meta->getFieldValue($object, $mapping['identifier']); + if (null !== $referencedObjectId) { + $property->setValue( + $object, + $ea->getSingleReference( + $this->getManager($mapping['type']), + $mapping['class'], + $referencedObjectId + ) + ); + } + } + } + } + + if (isset($config['referenceMany'])) { + foreach ($config['referenceMany'] as $mapping) { + $property = $meta->reflClass->getProperty($mapping['field']); + $property->setAccessible(true); + if (isset($mapping['mappedBy'])) { + $id = $ea->extractIdentifier($om, $object); + $manager = $this->getManager($mapping['type']); + $class = $mapping['class']; + $refMeta = $manager->getClassMetadata($class); + $refConfig = $this->getConfiguration($manager, $refMeta->name); + if (isset($refConfig['referenceOne'][$mapping['mappedBy']])) { + $refMapping = $refConfig['referenceOne'][$mapping['mappedBy']]; + $identifier = $refMapping['identifier']; + $property->setValue( + $object, + new LazyCollection( + function () use ($id, &$manager, $class, $identifier) { + $results = $manager + ->getRepository($class) + ->findBy(array( + $identifier => $id, + )); + + return new ArrayCollection((is_array($results) ? $results : $results->toArray())); + } + ) + ); + } + } + } + } + + $this->updateManyEmbedReferences($eventArgs); + } + + public function prePersist(EventArgs $eventArgs) + { + $this->updateReferences($eventArgs); + } + + public function preUpdate(EventArgs $eventArgs) + { + $this->updateReferences($eventArgs); + } + + public function getSubscribedEvents() + { + return array( + 'postLoad', + 'loadClassMetadata', + 'prePersist', + 'preUpdate', + ); + } + + public function registerManager($type, $manager) + { + $this->managers[$type] = $manager; + } + + /** + * @param string $type + * + * @return ObjectManager + */ + public function getManager($type) + { + return $this->managers[$type]; + } + + protected function getNamespace() + { + return __NAMESPACE__; + } + + private function updateReferences(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + + if (isset($config['referenceOne'])) { + foreach ($config['referenceOne'] as $mapping) { + if (isset($mapping['identifier'])) { + $property = $meta->reflClass->getProperty($mapping['field']); + $property->setAccessible(true); + $referencedObject = $property->getValue($object); + + if (is_object($referencedObject)) { + $manager = $this->getManager($mapping['type']); + $identifier = $ea->getIdentifier($manager, $referencedObject); + + $meta->setFieldValue( + $object, + $mapping['identifier'], + $identifier + ); + } + } + } + } + + $this->updateManyEmbedReferences($eventArgs); + } + + public function updateManyEmbedReferences(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + + if (isset($config['referenceManyEmbed'])) { + foreach ($config['referenceManyEmbed'] as $mapping) { + $property = $meta->reflClass->getProperty($mapping['field']); + $property->setAccessible(true); + + $id = $ea->extractIdentifier($om, $object); + $manager = $this->getManager('document'); + + $class = $mapping['class']; + $refMeta = $manager->getClassMetadata($class); + // Trigger the loading of the configuration to validate the mapping + $this->getConfiguration($manager, $refMeta->name); + + $identifier = $mapping['identifier']; + $property->setValue( + $object, + new LazyCollection( + function () use ($id, &$manager, $class, $identifier) { + $results = $manager + ->getRepository($class) + ->findBy(array( + $identifier => $id, + )); + + return new ArrayCollection((is_array($results) ? $results : $results->toArray())); + } + ) + ); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/InversedRelativeSlugHandler.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/InversedRelativeSlugHandler.php new file mode 100644 index 0000000..e32ffe9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/InversedRelativeSlugHandler.php @@ -0,0 +1,132 @@ + +* @license MIT License (http://www.opensource.org/licenses/mit-license.php) +*/ +class InversedRelativeSlugHandler implements SlugHandlerInterface +{ + /** + * @var ObjectManager + */ + protected $om; + + /** + * @var SluggableListener + */ + protected $sluggable; + + /** + * $options = array( + * 'relationClass' => 'objectclass', + * 'inverseSlugField' => 'slug', + * 'mappedBy' => 'relationField' + * ) + * {@inheritDoc} + */ + public function __construct(SluggableListener $sluggable) + { + $this->sluggable = $sluggable; + } + + /** + * {@inheritDoc} + */ + public function onChangeDecision(SluggableAdapter $ea, array &$config, $object, &$slug, &$needToChangeSlug) + { + } + + /** + * {@inheritDoc} + */ + public function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$slug) + { + } + + /** + * {@inheritDoc} + */ + public static function validate(array $options, ClassMetadata $meta) + { + if (!isset($options['relationClass']) || !strlen($options['relationClass'])) { + throw new InvalidMappingException("'relationClass' option must be specified for object slug mapping - {$meta->name}"); + } + if (!isset($options['mappedBy']) || !strlen($options['mappedBy'])) { + throw new InvalidMappingException("'mappedBy' option must be specified for object slug mapping - {$meta->name}"); + } + if (!isset($options['inverseSlugField']) || !strlen($options['inverseSlugField'])) { + throw new InvalidMappingException("'inverseSlugField' option must be specified for object slug mapping - {$meta->name}"); + } + } + + /** + * {@inheritDoc} + */ + public function onSlugCompletion(SluggableAdapter $ea, array &$config, $object, &$slug) + { + $this->om = $ea->getObjectManager(); + $isInsert = $this->om->getUnitOfWork()->isScheduledForInsert($object); + if (!$isInsert) { + $options = $config['handlers'][get_called_class()]; + $wrapped = AbstractWrapper::wrap($object, $this->om); + $oldSlug = $wrapped->getPropertyValue($config['slug']); + $mappedByConfig = $this->sluggable->getConfiguration( + $this->om, + $options['relationClass'] + ); + if ($mappedByConfig) { + $meta = $this->om->getClassMetadata($options['relationClass']); + if (!$meta->isSingleValuedAssociation($options['mappedBy'])) { + throw new InvalidMappingException("Unable to find ".$wrapped->getMetadata()->name." relation - [{$options['mappedBy']}] in class - {$meta->name}"); + } + if (!isset($mappedByConfig['slugs'][$options['inverseSlugField']])) { + throw new InvalidMappingException("Unable to find slug field - [{$options['inverseSlugField']}] in class - {$meta->name}"); + } + $mappedByConfig['slug'] = $mappedByConfig['slugs'][$options['inverseSlugField']]['slug']; + $mappedByConfig['mappedBy'] = $options['mappedBy']; + $ea->replaceInverseRelative($object, $mappedByConfig, $slug, $oldSlug); + $uow = $this->om->getUnitOfWork(); + // update in memory objects + foreach ($uow->getIdentityMap() as $className => $objects) { + // for inheritance mapped classes, only root is always in the identity map + if ($className !== $mappedByConfig['useObjectClass']) { + continue; + } + foreach ($objects as $object) { + if (property_exists($object, '__isInitialized__') && !$object->__isInitialized__) { + continue; + } + $oid = spl_object_hash($object); + $objectSlug = $meta->getReflectionProperty($mappedByConfig['slug'])->getValue($object); + if (preg_match("@^{$oldSlug}@smi", $objectSlug)) { + $objectSlug = str_replace($oldSlug, $slug, $objectSlug); + $meta->getReflectionProperty($mappedByConfig['slug'])->setValue($object, $objectSlug); + $ea->setOriginalObjectProperty($uow, $oid, $mappedByConfig['slug'], $objectSlug); + } + } + } + } + } + } + + /** + * {@inheritDoc} + */ + public function handlesUrlization() + { + return false; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/RelativeSlugHandler.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/RelativeSlugHandler.php new file mode 100644 index 0000000..8077855 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/RelativeSlugHandler.php @@ -0,0 +1,151 @@ + +* @license MIT License (http://www.opensource.org/licenses/mit-license.php) +*/ +class RelativeSlugHandler implements SlugHandlerInterface +{ + const SEPARATOR = '/'; + + /** + * @var ObjectManager + */ + protected $om; + + /** + * @var SluggableListener + */ + protected $sluggable; + + /** + * Used options + * + * @var array + */ + private $usedOptions; + + /** + * Callable of original transliterator + * which is used by sluggable + * + * @var callable + */ + private $originalTransliterator; + + /** + * $options = array( + * 'separator' => '/', + * 'relationField' => 'something', + * 'relationSlugField' => 'slug' + * ) + * {@inheritDoc} + */ + public function __construct(SluggableListener $sluggable) + { + $this->sluggable = $sluggable; + } + + /** + * {@inheritDoc} + */ + public function onChangeDecision(SluggableAdapter $ea, array &$config, $object, &$slug, &$needToChangeSlug) + { + $this->om = $ea->getObjectManager(); + $isInsert = $this->om->getUnitOfWork()->isScheduledForInsert($object); + $this->usedOptions = $config['handlers'][get_called_class()]; + if (!isset($this->usedOptions['separator'])) { + $this->usedOptions['separator'] = self::SEPARATOR; + } + if (!$isInsert && !$needToChangeSlug) { + $changeSet = $ea->getObjectChangeSet($this->om->getUnitOfWork(), $object); + if (isset($changeSet[$this->usedOptions['relationField']])) { + $needToChangeSlug = true; + } + } + } + + /** + * {@inheritDoc} + */ + public function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$slug) + { + $this->originalTransliterator = $this->sluggable->getTransliterator(); + $this->sluggable->setTransliterator(array($this, 'transliterate')); + } + + /** + * {@inheritDoc} + */ + public static function validate(array $options, ClassMetadata $meta) + { + if (!$meta->isSingleValuedAssociation($options['relationField'])) { + throw new InvalidMappingException("Unable to find slug relation through field - [{$options['relationField']}] in class - {$meta->name}"); + } + } + + /** + * {@inheritDoc} + */ + public function onSlugCompletion(SluggableAdapter $ea, array &$config, $object, &$slug) + { + } + + /** + * Transliterates the slug and prefixes the slug + * by relative one + * + * @param string $text + * @param string $separator + * @param object $object + * + * @return string + */ + public function transliterate($text, $separator, $object) + { + $result = call_user_func_array( + $this->originalTransliterator, + array($text, $separator, $object) + ); + $wrapped = AbstractWrapper::wrap($object, $this->om); + $relation = $wrapped->getPropertyValue($this->usedOptions['relationField']); + if ($relation) { + $wrappedRelation = AbstractWrapper::wrap($relation, $this->om); + $slug = $wrappedRelation->getPropertyValue($this->usedOptions['relationSlugField']); + + if (isset($this->usedOptions['urilize']) && $this->usedOptions['urilize']) { + $slug = call_user_func_array( + $this->originalTransliterator, + array($slug, $separator, $object) + ); + } + + $result = $slug.$this->usedOptions['separator'].$result; + } + $this->sluggable->setTransliterator($this->originalTransliterator); + + return $result; + } + + /** + * {@inheritDoc} + */ + public function handlesUrlization() + { + return true; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerInterface.php new file mode 100644 index 0000000..8b90c86 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerInterface.php @@ -0,0 +1,78 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface SlugHandlerInterface +{ + /** + * Construct the slug handler + * + * @param SluggableListener $sluggable + */ + public function __construct(SluggableListener $sluggable); + + /** + * Callback on slug handlers before the decision + * is made whether or not the slug needs to be + * recalculated + * + * @param SluggableAdapter $ea + * @param array $config + * @param object $object + * @param string $slug + * @param boolean $needToChangeSlug + * + * @return void + */ + public function onChangeDecision(SluggableAdapter $ea, array &$config, $object, &$slug, &$needToChangeSlug); + + /** + * Callback on slug handlers right after the slug is built + * + * @param SluggableAdapter $ea + * @param array $config + * @param object $object + * @param string $slug + * + * @return void + */ + public function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$slug); + + /** + * Callback for slug handlers on slug completion + * + * @param SluggableAdapter $ea + * @param array $config + * @param object $object + * @param string $slug + * + * @return void + */ + public function onSlugCompletion(SluggableAdapter $ea, array &$config, $object, &$slug); + + /** + * @return boolean whether or not this handler has already urlized the slug + */ + public function handlesUrlization(); + + /** + * Validate handler options + * + * @param array $options + * @param ClassMetadata $meta + */ + public static function validate(array $options, ClassMetadata $meta); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerWithUniqueCallbackInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerWithUniqueCallbackInterface.php new file mode 100644 index 0000000..11a9ff1 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/SlugHandlerWithUniqueCallbackInterface.php @@ -0,0 +1,28 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface SlugHandlerWithUniqueCallbackInterface extends SlugHandlerInterface +{ + /** + * Callback for slug handlers before it is made unique + * + * @param SluggableAdapter $ea + * @param array $config + * @param object $object + * @param string $slug + * + * @return void + */ + public function beforeMakingUnique(SluggableAdapter $ea, array &$config, $object, &$slug); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/TreeSlugHandler.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/TreeSlugHandler.php new file mode 100644 index 0000000..ed62eeb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Handler/TreeSlugHandler.php @@ -0,0 +1,201 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TreeSlugHandler implements SlugHandlerWithUniqueCallbackInterface +{ + const SEPARATOR = '/'; + + /** + * @var ObjectManager + */ + protected $om; + + /** + * @var SluggableListener + */ + protected $sluggable; + + /** + * @var string + */ + private $prefix; + + /** + * @var string + */ + private $suffix; + + /** + * True if node is being inserted + * + * @var boolean + */ + private $isInsert = false; + + /** + * Transliterated parent slug + * + * @var string + */ + private $parentSlug; + + /** + * Used path separator + * + * @var string + */ + private $usedPathSeparator; + + /** + * {@inheritDoc} + */ + public function __construct(SluggableListener $sluggable) + { + $this->sluggable = $sluggable; + } + + /** + * {@inheritDoc} + */ + public function onChangeDecision(SluggableAdapter $ea, array &$config, $object, &$slug, &$needToChangeSlug) + { + $this->om = $ea->getObjectManager(); + $this->isInsert = $this->om->getUnitOfWork()->isScheduledForInsert($object); + $options = $config['handlers'][get_called_class()]; + + $this->usedPathSeparator = isset($options['separator']) ? $options['separator'] : self::SEPARATOR; + $this->prefix = isset($options['prefix']) ? $options['prefix'] : ''; + $this->suffix = isset($options['suffix']) ? $options['suffix'] : ''; + + if (!$this->isInsert && !$needToChangeSlug) { + $changeSet = $ea->getObjectChangeSet($this->om->getUnitOfWork(), $object); + if (isset($changeSet[$options['parentRelationField']])) { + $needToChangeSlug = true; + } + } + } + + /** + * {@inheritDoc} + */ + public function postSlugBuild(SluggableAdapter $ea, array &$config, $object, &$slug) + { + $options = $config['handlers'][get_called_class()]; + $this->parentSlug = ''; + + $wrapped = AbstractWrapper::wrap($object, $this->om); + if ($parent = $wrapped->getPropertyValue($options['parentRelationField'])) { + $parent = AbstractWrapper::wrap($parent, $this->om); + $this->parentSlug = $parent->getPropertyValue($config['slug']); + + // if needed, remove suffix from parentSlug, so we can use it to prepend it to our slug + if (isset($options['suffix'])) { + $suffix = $options['suffix']; + + if (substr($this->parentSlug, -strlen($suffix)) === $suffix) { //endsWith + $this->parentSlug = substr_replace($this->parentSlug, '', -1 * strlen($suffix)); + } + } + } + } + + /** + * {@inheritDoc} + */ + public static function validate(array $options, ClassMetadata $meta) + { + if (!$meta->isSingleValuedAssociation($options['parentRelationField'])) { + throw new InvalidMappingException("Unable to find tree parent slug relation through field - [{$options['parentRelationField']}] in class - {$meta->name}"); + } + } + + /** + * {@inheritDoc} + */ + public function beforeMakingUnique(SluggableAdapter $ea, array &$config, $object, &$slug) + { + $slug = $this->transliterate($slug, $config['separator'], $object); + } + + /** + * {@inheritDoc} + */ + public function onSlugCompletion(SluggableAdapter $ea, array &$config, $object, &$slug) + { + if (!$this->isInsert) { + $wrapped = AbstractWrapper::wrap($object, $this->om); + $meta = $wrapped->getMetadata(); + $target = $wrapped->getPropertyValue($config['slug']); + $config['pathSeparator'] = $this->usedPathSeparator; + $ea->replaceRelative($object, $config, $target.$config['pathSeparator'], $slug); + $uow = $this->om->getUnitOfWork(); + // update in memory objects + foreach ($uow->getIdentityMap() as $className => $objects) { + // for inheritance mapped classes, only root is always in the identity map + if ($className !== $wrapped->getRootObjectName()) { + continue; + } + foreach ($objects as $object) { + if (property_exists($object, '__isInitialized__') && !$object->__isInitialized__) { + continue; + } + $oid = spl_object_hash($object); + $objectSlug = $meta->getReflectionProperty($config['slug'])->getValue($object); + if (preg_match("@^{$target}{$config['pathSeparator']}@smi", $objectSlug)) { + $objectSlug = str_replace($target, $slug, $objectSlug); + $meta->getReflectionProperty($config['slug'])->setValue($object, $objectSlug); + $ea->setOriginalObjectProperty($uow, $oid, $config['slug'], $objectSlug); + } + } + } + } + } + + /** + * Transliterates the slug and prefixes the slug + * by collection of parent slugs + * + * @param string $text + * @param string $separator + * @param object $object + * + * @return string + */ + public function transliterate($text, $separator, $object) + { + $slug = $text . $this->suffix; + + if (strlen($this->parentSlug)) { + $slug = $this->parentSlug.$this->usedPathSeparator.$slug; + } else { + // if no parentSlug, apply our prefix + $slug = $this->prefix.$slug; + } + + return $slug; + } + + /** + * {@inheritDoc} + */ + public function handlesUrlization() + { + return false; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..38288cb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Annotation.php @@ -0,0 +1,173 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to identify field as one which holds the slug + * together with slug options + */ + const SLUG = 'Gedmo\\Mapping\\Annotation\\Slug'; + + /** + * SlugHandler extension annotation + */ + const HANDLER = 'Gedmo\\Mapping\\Annotation\\SlugHandler'; + + /** + * SlugHandler option annotation + */ + const HANDLER_OPTION = 'Gedmo\\Mapping\\Annotation\\SlugHandlerOption'; + + /** + * List of types which are valid for slug and sluggable fields + * + * @var array + */ + protected $validTypes = array( + 'string', + 'text', + 'integer', + 'int', + 'datetime', + 'citext', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + $config = $this->retrieveSlug($meta, $config, $property, ''); + } + + // Embedded entity + if (property_exists($meta, 'embeddedClasses') && $meta->embeddedClasses) { + foreach ($meta->embeddedClasses as $propertyName => $embeddedClassInfo) { + $embeddedClass = new \ReflectionClass($embeddedClassInfo['class']); + foreach ($embeddedClass->getProperties() as $embeddedProperty) { + $config = $this->retrieveSlug($meta, $config, $embeddedProperty, $propertyName); + } + } + } + + return $config; + } + + /** + * @param $meta + * @param array $config + * @param $property + * @param $fieldNamePrefix + * @return array + */ + private function retrieveSlug($meta, array &$config, $property, $fieldNamePrefix) + { + $fieldName = $fieldNamePrefix ? ($fieldNamePrefix . '.' . $property->getName()) : $property->getName(); +// slug property + if ($slug = $this->reader->getPropertyAnnotation($property, self::SLUG)) { + if (!$meta->hasField($fieldName)) { + throw new InvalidMappingException("Unable to find slug [{$fieldName}] as mapped property in entity - {$meta->name}"); + } + if (!$this->isValidField($meta, $fieldName)) { + throw new InvalidMappingException("Cannot use field - [{$fieldName}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}"); + } + // process slug handlers + $handlers = array(); + if (is_array($slug->handlers) && $slug->handlers) { + foreach ($slug->handlers as $handler) { + if (!$handler instanceof SlugHandler) { + throw new InvalidMappingException("SlugHandler: {$handler} should be instance of SlugHandler annotation in entity - {$meta->name}"); + } + if (!strlen($handler->class)) { + throw new InvalidMappingException("SlugHandler class: {$handler->class} should be a valid class name in entity - {$meta->name}"); + } + $class = $handler->class; + $handlers[$class] = array(); + foreach ((array)$handler->options as $option) { + if (!$option instanceof SlugHandlerOption) { + throw new InvalidMappingException("SlugHandlerOption: {$option} should be instance of SlugHandlerOption annotation in entity - {$meta->name}"); + } + if (!strlen($option->name)) { + throw new InvalidMappingException("SlugHandlerOption name: {$option->name} should be valid name in entity - {$meta->name}"); + } + $handlers[$class][$option->name] = $option->value; + } + $class::validate($handlers[$class], $meta); + } + } + // process slug fields + if (empty($slug->fields) || !is_array($slug->fields)) { + throw new InvalidMappingException("Slug must contain at least one field for slug generation in class - {$meta->name}"); + } + foreach ($slug->fields as $slugField) { + $slugFieldWithPrefix = $fieldNamePrefix ? ($fieldNamePrefix . '.' . $slugField) : $slugField; + if (!$meta->hasField($slugFieldWithPrefix)) { + throw new InvalidMappingException("Unable to find slug [{$slugFieldWithPrefix}] as mapped property in entity - {$meta->name}"); + } + if (!$this->isValidField($meta, $slugFieldWithPrefix)) { + throw new InvalidMappingException("Cannot use field - [{$slugFieldWithPrefix}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}"); + } + } + if (!is_bool($slug->updatable)) { + throw new InvalidMappingException("Slug annotation [updatable], type is not valid and must be 'boolean' in class - {$meta->name}"); + } + if (!is_bool($slug->unique)) { + throw new InvalidMappingException("Slug annotation [unique], type is not valid and must be 'boolean' in class - {$meta->name}"); + } + if (!empty($meta->identifier) && $meta->isIdentifier($fieldName) && !(bool)$slug->unique) { + throw new InvalidMappingException("Identifier field - [{$fieldName}] slug must be unique in order to maintain primary key in class - {$meta->name}"); + } + if ($slug->unique === false && $slug->unique_base) { + throw new InvalidMappingException("Slug annotation [unique_base] can not be set if unique is unset or 'false'"); + } + if ($slug->unique_base && !$meta->hasField($slug->unique_base) && !$meta->hasAssociation($slug->unique_base)) { + throw new InvalidMappingException("Unable to find [{$slug->unique_base}] as mapped property in entity - {$meta->name}"); + } + $sluggableFields = array(); + foreach ($slug->fields as $field) { + $sluggableFields[] = $fieldNamePrefix ? ($fieldNamePrefix . '.' . $field) : $field; + } + + // set all options + $config['slugs'][$fieldName] = array( + 'fields' => $sluggableFields, + 'slug' => $fieldName, + 'style' => $slug->style, + 'dateFormat' => $slug->dateFormat, + 'updatable' => $slug->updatable, + 'unique' => $slug->unique, + 'unique_base' => $slug->unique_base, + 'separator' => $slug->separator, + 'prefix' => $slug->prefix, + 'suffix' => $slug->suffix, + 'handlers' => $handlers, + ); + } + return $config; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..855257f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Xml.php @@ -0,0 +1,147 @@ + + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * List of types which are valid for slug and sluggable fields + * + * @var array + */ + private $validTypes = array( + 'string', + 'text', + 'integer', + 'int', + 'datetime', + 'citext', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + + if (isset($xml->field)) { + foreach ($xml->field as $mapping) { + $field = $this->_getAttribute($mapping, 'name'); + $this->buildFieldConfiguration($meta, $field, $mapping, $config); + } + } + + if (isset($xml->{'attribute-overrides'})) { + foreach ($xml->{'attribute-overrides'}->{'attribute-override'} as $mapping) { + $field = $this->_getAttribute($mapping, 'name'); + $this->buildFieldConfiguration($meta, $field, $mapping->field, $config); + } + } + } + + private function buildFieldConfiguration($meta, $field, \SimpleXMLElement $mapping, array &$config) + { + /** + * @var \SimpleXmlElement $mapping + */ + $mapping = $mapping->children(self::GEDMO_NAMESPACE_URI); + + if (isset($mapping->slug)) { + /** + * @var \SimpleXmlElement $slug + */ + $slug = $mapping->slug; + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Cannot use field - [{$field}] for slug storage, type is not valid and must be 'string' in class - {$meta->name}"); + } + $fields = array_map('trim', explode(',', (string) $this->_getAttribute($slug, 'fields'))); + foreach ($fields as $slugField) { + if (!$meta->hasField($slugField)) { + throw new InvalidMappingException("Unable to find slug [{$slugField}] as mapped property in entity - {$meta->name}"); + } + if (!$this->isValidField($meta, $slugField)) { + throw new InvalidMappingException("Cannot use field - [{$slugField}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}"); + } + } + + $handlers = array(); + if (isset($slug->handler)) { + foreach ($slug->handler as $handler) { + $class = (string) $this->_getAttribute($handler, 'class'); + $handlers[$class] = array(); + foreach ($handler->{'handler-option'} as $option) { + $handlers[$class][(string) $this->_getAttribute($option, 'name')] + = (string) $this->_getAttribute($option, 'value') + ; + } + $class::validate($handlers[$class], $meta); + } + } + + // set all options + $config['slugs'][$field] = array( + 'fields' => $fields, + 'slug' => $field, + 'style' => $this->_isAttributeSet($slug, 'style') ? + $this->_getAttribute($slug, 'style') : 'default', + 'updatable' => $this->_isAttributeSet($slug, 'updatable') ? + $this->_getBooleanAttribute($slug, 'updatable') : true, + 'dateFormat' => $this->_isAttributeSet($slug, 'dateFormat') ? + $this->_getAttribute($slug, 'dateFormat') : 'Y-m-d-H:i', + 'unique' => $this->_isAttributeSet($slug, 'unique') ? + $this->_getBooleanAttribute($slug, 'unique') : true, + 'unique_base' => $this->_isAttributeSet($slug, 'unique-base') ? + $this->_getAttribute($slug, 'unique-base') : null, + 'separator' => $this->_isAttributeSet($slug, 'separator') ? + $this->_getAttribute($slug, 'separator') : '-', + 'prefix' => $this->_isAttributeSet($slug, 'prefix') ? + $this->_getAttribute($slug, 'prefix') : '', + 'suffix' => $this->_isAttributeSet($slug, 'suffix') ? + $this->_getAttribute($slug, 'suffix') : '', + 'handlers' => $handlers, + ); + if (!$meta->isMappedSuperclass && $meta->isIdentifier($field) && !$config['slugs'][$field]['unique']) { + throw new InvalidMappingException("Identifier field - [{$field}] slug must be unique in order to maintain primary key in class - {$meta->name}"); + } + $ubase = $config['slugs'][$field]['unique_base']; + if ($config['slugs'][$field]['unique'] === false && $ubase) { + throw new InvalidMappingException("Slug annotation [unique_base] can not be set if unique is unset or 'false'"); + } + if ($ubase && !$meta->hasField($ubase) && !$meta->hasAssociation($ubase)) { + throw new InvalidMappingException("Unable to find [{$ubase}] as mapped property in entity - {$meta->name}"); + } + } + } + + /** + * Checks if $field type is valid as Sluggable field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..b530bf7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Driver/Yaml.php @@ -0,0 +1,155 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * List of types which are valid for slug and sluggable fields + * + * @var array + */ + private $validTypes = array( + 'string', + 'text', + 'integer', + 'int', + 'datetime', + 'citext', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + $this->buildFieldConfiguration($field, $fieldMapping, $meta, $config); + } + } + + if (isset($mapping['attributeOverride'])) { + foreach ($mapping['attributeOverride'] as $field => $overrideMapping) { + $this->buildFieldConfiguration($field, $overrideMapping, $meta, $config); + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + /** + * Checks if $field type is valid as Sluggable field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } + + private function buildFieldConfiguration($field, array $fieldMapping, $meta, array &$config) + { + if (isset($fieldMapping['gedmo'])) { + if (isset($fieldMapping['gedmo']['slug'])) { + $slug = $fieldMapping['gedmo']['slug']; + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Cannot use field - [{$field}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}"); + } + // process slug handlers + $handlers = array(); + if (isset($slug['handlers'])) { + foreach ($slug['handlers'] as $handlerClass => $options) { + if (!strlen($handlerClass)) { + throw new InvalidMappingException("SlugHandler class: {$handlerClass} should be a valid class name in entity - {$meta->name}"); + } + $handlers[$handlerClass] = $options; + $handlerClass::validate($handlers[$handlerClass], $meta); + } + } + // process slug fields + if (empty($slug['fields']) || !is_array($slug['fields'])) { + throw new InvalidMappingException("Slug must contain at least one field for slug generation in class - {$meta->name}"); + } + foreach ($slug['fields'] as $slugField) { + if (!$meta->hasField($slugField)) { + throw new InvalidMappingException("Unable to find slug [{$slugField}] as mapped property in entity - {$meta->name}"); + } + if (!$this->isValidField($meta, $slugField)) { + throw new InvalidMappingException("Cannot use field - [{$slugField}] for slug storage, type is not valid and must be 'string' or 'text' in class - {$meta->name}"); + } + } + + $config['slugs'][$field]['fields'] = $slug['fields']; + $config['slugs'][$field]['handlers'] = $handlers; + $config['slugs'][$field]['slug'] = $field; + $config['slugs'][$field]['style'] = isset($slug['style']) ? + (string) $slug['style'] : 'default'; + + $config['slugs'][$field]['dateFormat'] = isset($slug['dateFormat']) ? + (string) $slug['dateFormat'] : 'Y-m-d-H:i'; + + $config['slugs'][$field]['updatable'] = isset($slug['updatable']) ? + (bool) $slug['updatable'] : true; + + $config['slugs'][$field]['unique'] = isset($slug['unique']) ? + (bool) $slug['unique'] : true; + + $config['slugs'][$field]['unique_base'] = isset($slug['unique_base']) ? + $slug['unique_base'] : null; + + $config['slugs'][$field]['separator'] = isset($slug['separator']) ? + (string) $slug['separator'] : '-'; + + $config['slugs'][$field]['prefix'] = isset($slug['prefix']) ? + (string) $slug['prefix'] : ''; + + $config['slugs'][$field]['suffix'] = isset($slug['suffix']) ? + (string) $slug['suffix'] : ''; + + if (!$meta->isMappedSuperclass && $meta->isIdentifier($field) && !$config['slugs'][$field]['unique']) { + throw new InvalidMappingException("Identifier field - [{$field}] slug must be unique in order to maintain primary key in class - {$meta->name}"); + } + $ubase = $config['slugs'][$field]['unique_base']; + if ($config['slugs'][$field]['unique'] === false && $ubase) { + throw new InvalidMappingException("Slug annotation [unique_base] can not be set if unique is unset or 'false'"); + } + if ($ubase && !$meta->hasField($ubase) && !$meta->hasAssociation($ubase)) { + throw new InvalidMappingException("Unable to find [{$ubase}] as mapped property in entity - {$meta->name}"); + } + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..f4b12c5 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,123 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements SluggableAdapter +{ + /** + * {@inheritDoc} + */ + public function getSimilarSlugs($object, $meta, array $config, $slug) + { + $dm = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $dm); + $qb = $dm->createQueryBuilder($config['useObjectClass']); + if (($identifier = $wrapped->getIdentifier()) && !$meta->isIdentifier($config['slug'])) { + $qb->field($meta->identifier)->notEqual($identifier); + } + $qb->field($config['slug'])->equals(new \MongoRegex('/^'.preg_quote($slug, '/').'/')); + + // use the unique_base to restrict the uniqueness check + if ($config['unique'] && isset($config['unique_base'])) { + if (is_object($ubase = $wrapped->getPropertyValue($config['unique_base']))) { + $qb->field($config['unique_base'].'.$id')->equals(new \MongoId($ubase->getId())); + } elseif ($ubase) { + $qb->where('/^'.preg_quote($ubase, '/').'/.test(this.'.$config['unique_base'].')'); + } else { + $qb->field($config['unique_base'])->equals(null); + } + } + + $q = $qb->getQuery(); + $q->setHydrate(false); + + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = $result->toArray(); + } + + return $result; + } + + /** + * This query can cause some data integrity failures since it does not + * execute automatically + * + * {@inheritDoc} + */ + public function replaceRelative($object, array $config, $target, $replacement) + { + $dm = $this->getObjectManager(); + $meta = $dm->getClassMetadata($config['useObjectClass']); + + $q = $dm + ->createQueryBuilder($config['useObjectClass']) + ->where("function() { + return this.{$config['slug']}.indexOf('{$target}') === 0; + }") + ->getQuery() + ; + $q->setHydrate(false); + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = $result->toArray(); + foreach ($result as $targetObject) { + $slug = preg_replace("@^{$target}@smi", $replacement.$config['pathSeparator'], $targetObject[$config['slug']]); + $dm + ->createQueryBuilder() + ->update($config['useObjectClass']) + ->field($config['slug'])->set($slug) + ->field($meta->identifier)->equals($targetObject['_id']) + ->getQuery() + ->execute() + ; + } + } + } + + /** + * This query can cause some data integrity failures since it does not + * execute atomically + * + * {@inheritDoc} + */ + public function replaceInverseRelative($object, array $config, $target, $replacement) + { + $dm = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $dm); + $meta = $dm->getClassMetadata($config['useObjectClass']); + $q = $dm + ->createQueryBuilder($config['useObjectClass']) + ->field($config['mappedBy'].'.'.$meta->identifier)->equals($wrapped->getIdentifier()) + ->getQuery() + ; + $q->setHydrate(false); + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = $result->toArray(); + foreach ($result as $targetObject) { + $slug = preg_replace("@^{$replacement}@smi", $target, $targetObject[$config['slug']]); + $dm + ->createQueryBuilder() + ->update($config['useObjectClass']) + ->field($config['slug'])->set($slug) + ->field($meta->identifier)->equals($targetObject['_id']) + ->getQuery() + ->execute() + ; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..88cbadf --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,117 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ORM extends BaseAdapterORM implements SluggableAdapter +{ + /** + * {@inheritDoc} + */ + public function getSimilarSlugs($object, $meta, array $config, $slug) + { + $em = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $em); + $qb = $em->createQueryBuilder(); + $qb->select('rec.'.$config['slug']) + ->from($config['useObjectClass'], 'rec') + ->where($qb->expr()->like( + 'rec.'.$config['slug'], + ':slug') + ) + ; + $qb->setParameter('slug',$slug.'%'); + + // use the unique_base to restrict the uniqueness check + if ($config['unique'] && isset($config['unique_base'])) { + $ubase = $wrapped->getPropertyValue($config['unique_base']); + if (array_key_exists($config['unique_base'], $wrapped->getMetadata()->getAssociationMappings())) { + $mapping = $wrapped->getMetadata()->getAssociationMapping($config['unique_base']); + } else { + $mapping = false; + } + if (($ubase || $ubase === 0) && !$mapping) { + $qb->andWhere('rec.'.$config['unique_base'].' = :unique_base'); + $qb->setParameter(':unique_base', $ubase); + } elseif ($ubase && $mapping && in_array($mapping['type'], array(ClassMetadataInfo::ONE_TO_ONE, ClassMetadataInfo::MANY_TO_ONE))) { + $mappedAlias = 'mapped_'.$config['unique_base']; + $wrappedUbase = AbstractWrapper::wrap($ubase, $em); + $qb->innerJoin('rec.'.$config['unique_base'], $mappedAlias); + foreach (array_keys($mapping['targetToSourceKeyColumns']) as $i => $mappedKey) { + $mappedProp = $wrappedUbase->getMetadata()->fieldNames[$mappedKey]; + $qb->andWhere($qb->expr()->eq($mappedAlias.'.'.$mappedProp, ':assoc'.$i)); + $qb->setParameter(':assoc'.$i, $wrappedUbase->getPropertyValue($mappedProp)); + } + } else { + $qb->andWhere($qb->expr()->isNull('rec.'.$config['unique_base'])); + } + } + + // include identifiers + foreach ((array) $wrapped->getIdentifier(false) as $id => $value) { + if (!$meta->isIdentifier($config['slug'])) { + $namedId = str_replace('.', '_', $id); + $qb->andWhere($qb->expr()->neq('rec.'.$id, ':'.$namedId)); + $qb->setParameter($namedId, $value, $meta->getTypeOfField($namedId)); + } + } + $q = $qb->getQuery(); + $q->setHydrationMode(Query::HYDRATE_ARRAY); + + return $q->execute(); + } + + /** + * {@inheritDoc} + */ + public function replaceRelative($object, array $config, $target, $replacement) + { + $em = $this->getObjectManager(); + $qb = $em->createQueryBuilder(); + $qb->update($config['useObjectClass'], 'rec') + ->set('rec.'.$config['slug'], $qb->expr()->concat( + $qb->expr()->literal($replacement), + $qb->expr()->substring('rec.'.$config['slug'], mb_strlen($target)) + )) + ->where($qb->expr()->like( + 'rec.'.$config['slug'], + $qb->expr()->literal($target.'%')) + ) + ; + // update in memory + $q = $qb->getQuery(); + + return $q->execute(); + } + + /** + * {@inheritDoc} + */ + public function replaceInverseRelative($object, array $config, $target, $replacement) + { + $em = $this->getObjectManager(); + $qb = $em->createQueryBuilder(); + $qb->update($config['useObjectClass'], 'rec') + ->set('rec.'.$config['slug'], $qb->expr()->concat( + $qb->expr()->literal($target), + $qb->expr()->substring('rec.'.$config['slug'], mb_strlen($replacement)+1) + )) + ->where($qb->expr()->like('rec.'.$config['slug'], $qb->expr()->literal($replacement . '%'))) + ; + $q = $qb->getQuery(); + + return $q->execute(); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/SluggableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/SluggableAdapter.php new file mode 100644 index 0000000..4882b80 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Mapping/Event/SluggableAdapter.php @@ -0,0 +1,54 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface SluggableAdapter extends AdapterInterface +{ + /** + * Loads the similar slugs + * + * @param object $object + * @param object $meta + * @param array $config + * @param string $slug + * + * @return array + */ + public function getSimilarSlugs($object, $meta, array $config, $slug); + + /** + * Replace part of slug to all objects + * matching $target pattern + * + * @param object $object + * @param array $config + * @param string $target + * @param string $replacement + * + * @return integer + */ + public function replaceRelative($object, array $config, $target, $replacement); + + /** + * Replace part of slug to all objects + * matching $target pattern and having $object + * related + * + * @param object $object + * @param array $config + * @param string $target + * @param string $replacement + * + * @return integer + */ + public function replaceInverseRelative($object, array $config, $target, $replacement); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Sluggable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Sluggable.php new file mode 100644 index 0000000..9adcbdc --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Sluggable.php @@ -0,0 +1,41 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Sluggable +{ + // use now annotations instead of predefined methods, this interface is not necessary + + /** + * @gedmo:Sluggable + * to mark the field as sluggable use property annotation @gedmo:Sluggable + * this field value will be included in built slug + */ + + /** + * @gedmo:Slug - to mark property which will hold slug use annotation @gedmo:Slug + * available options: + * updatable (optional, default=true) - true to update the slug on sluggable field changes, false - otherwise + * unique (optional, default=true) - true if slug should be unique and if identical it will be prefixed, false - otherwise + * unique_base (optional, default="") - used in conjunction with unique. The name of the entity property that should be used as a key when doing a uniqueness check + * separator (optional, default="-") - separator which will separate words in slug + * prefix (optional, default="") - prefix which will be added to the generated slug + * suffix (optional, default="") - suffix which will be added to the generated slug + * style (optional, default="default") - "default" all letters will be lowercase, "camel" - first word letter will be uppercase + * dateFormat (optional, default="default") - "default" all letters will be lowercase, "camel" - first word letter will be uppercase + * + * example: + * + * @gedmo:Slug(style="camel", separator="_", prefix="", suffix="", updatable=false, unique=false) + * @Column(type="string", length=64) + * $property + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/SluggableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/SluggableListener.php new file mode 100644 index 0000000..c331f65 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/SluggableListener.php @@ -0,0 +1,569 @@ + + * @author Klein Florian + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class SluggableListener extends MappedEventSubscriber +{ + /** + * The power exponent to jump + * the slug unique number by tens. + * + * @var integer + */ + private $exponent = 0; + + /** + * Transliteration callback for slugs + * + * @var callable + */ + private $transliterator = array('Gedmo\Sluggable\Util\Urlizer', 'transliterate'); + + /** + * Urlize callback for slugs + * + * @var callable + */ + private $urlizer = array('Gedmo\Sluggable\Util\Urlizer', 'urlize'); + + /** + * List of inserted slugs for each object class. + * This is needed in case there are identical slug + * composition in number of persisted objects + * during the same flush + * + * @var array + */ + private $persisted = array(); + + /** + * List of initialized slug handlers + * + * @var array + */ + private $handlers = array(); + + /** + * List of filters which are manipulated when slugs are generated + * + * @var array + */ + private $managedFilters = array(); + + /** + * Specifies the list of events to listen + * + * @return array + */ + public function getSubscribedEvents() + { + return array( + 'onFlush', + 'loadClassMetadata', + 'prePersist', + ); + } + + /** + * Set the transliteration callable method + * to transliterate slugs + * + * @param callable $callable + * + * @throws \Gedmo\Exception\InvalidArgumentException + * + * @return void + */ + public function setTransliterator($callable) + { + if (!is_callable($callable)) { + throw new \Gedmo\Exception\InvalidArgumentException('Invalid transliterator callable parameter given'); + } + $this->transliterator = $callable; + } + + /** + * Set the urlization callable method + * to urlize slugs + * + * @param callable $callable + */ + public function setUrlizer($callable) + { + if (!is_callable($callable)) { + throw new \Gedmo\Exception\InvalidArgumentException('Invalid urlizer callable parameter given'); + } + $this->urlizer = $callable; + } + + /** + * Get currently used transliterator callable + * + * @return callable + */ + public function getTransliterator() + { + return $this->transliterator; + } + + /** + * Get currently used urlizer callable + * + * @return callable + */ + public function getUrlizer() + { + return $this->urlizer; + } + + /** + * Enables or disables the given filter when slugs are generated + * + * @param string $name + * @param bool $disable True by default + */ + public function addManagedFilter($name, $disable = true) + { + $this->managedFilters[$name] = array('disabled' => $disable); + } + + /** + * Removes a filter from the managed set + * + * @param string $name + */ + public function removeManagedFilter($name) + { + unset($this->managedFilters[$name]); + } + + /** + * Mapps additional metadata + * + * @param EventArgs $eventArgs + * + * @return void + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * Allows identifier fields to be slugged as usual + * + * @param EventArgs $args + * + * @return void + */ + public function prePersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($config = $this->getConfiguration($om, $meta->name)) { + foreach ($config['slugs'] as $slugField => $options) { + if ($meta->isIdentifier($slugField)) { + $meta->getReflectionProperty($slugField)->setValue($object, '__id__'); + } + } + } + } + + /** + * Generate slug on objects being updated during flush + * if they require changing + * + * @param EventArgs $args + * + * @return void + */ + public function onFlush(EventArgs $args) + { + $this->persisted = array(); + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + $this->manageFiltersBeforeGeneration($om); + + // process all objects being inserted, using scheduled insertions instead + // of prePersist in case if record will be changed before flushing this will + // ensure correct result. No additional overhead is encountered + foreach ($ea->getScheduledObjectInsertions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($this->getConfiguration($om, $meta->name)) { + // generate first to exclude this object from similar persisted slugs result + $this->generateSlug($ea, $object); + $this->persisted[$ea->getRootObjectClass($meta)][] = $object; + } + } + // we use onFlush and not preUpdate event to let other + // event listeners be nested together + foreach ($ea->getScheduledObjectUpdates($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($this->getConfiguration($om, $meta->name) && !$uow->isScheduledForInsert($object)) { + $this->generateSlug($ea, $object); + $this->persisted[$ea->getRootObjectClass($meta)][] = $object; + } + } + + $this->manageFiltersAfterGeneration($om); + + AbstractWrapper::clear(); + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } + + /** + * Get the slug handler instance by $class name + * + * @param string $class + * + * @return \Gedmo\Sluggable\Handler\SlugHandlerInterface + */ + private function getHandler($class) + { + if (!isset($this->handlers[$class])) { + $this->handlers[$class] = new $class($this); + } + + return $this->handlers[$class]; + } + + /** + * Creates the slug for object being flushed + * + * @param SluggableAdapter $ea + * @param object $object + * + * @return void + */ + private function generateSlug(SluggableAdapter $ea, $object) + { + $om = $ea->getObjectManager(); + $meta = $om->getClassMetadata(get_class($object)); + $uow = $om->getUnitOfWork(); + $changeSet = $ea->getObjectChangeSet($uow, $object); + $isInsert = $uow->isScheduledForInsert($object); + $config = $this->getConfiguration($om, $meta->name); + + foreach ($config['slugs'] as $slugField => $options) { + $hasHandlers = count($options['handlers']); + $options['useObjectClass'] = $config['useObjectClass']; + // collect the slug from fields + $slug = $meta->getReflectionProperty($slugField)->getValue($object); + + // if slug should not be updated, skip it + if (!$options['updatable'] && !$isInsert && (!isset($changeSet[$slugField]) || $slug === '__id__')) { + continue; + } + // must fetch the old slug from changeset, since $object holds the new version + $oldSlug = isset($changeSet[$slugField]) ? $changeSet[$slugField][0] : $slug; + $needToChangeSlug = false; + + // if slug is null, regenerate it, or needs an update + if (null === $slug || $slug === '__id__' || !isset($changeSet[$slugField])) { + $slug = ''; + + foreach ($options['fields'] as $sluggableField) { + if (isset($changeSet[$sluggableField]) || isset($changeSet[$slugField])) { + $needToChangeSlug = true; + } + $value = $meta->getReflectionProperty($sluggableField)->getValue($object); + // Remove `$value instanceof \DateTime` check when PHP version is bumped to >=5.5 + $slug .= ($value instanceof \DateTime || $value instanceof \DateTimeInterface) ? $value->format($options['dateFormat']) : $value; + $slug .= ' '; + } + // trim generated slug as it will have unnecessary trailing space + $slug = trim($slug); + } else { + // slug was set manually + $needToChangeSlug = true; + } + // notify slug handlers --> onChangeDecision + if ($hasHandlers) { + foreach ($options['handlers'] as $class => $handlerOptions) { + $this->getHandler($class)->onChangeDecision($ea, $options, $object, $slug, $needToChangeSlug); + } + } + // if slug is changed, do further processing + if ($needToChangeSlug) { + $mapping = $meta->getFieldMapping($slugField); + // notify slug handlers --> postSlugBuild + $urlized = false; + + if ($hasHandlers) { + foreach ($options['handlers'] as $class => $handlerOptions) { + $this->getHandler($class)->postSlugBuild($ea, $options, $object, $slug); + if ($this->getHandler($class)->handlesUrlization()) { + $urlized = true; + } + } + } + + // build the slug + // Step 1: transliteration, changing 北京 to 'Bei Jing' + $slug = call_user_func_array( + $this->transliterator, + array($slug, $options['separator'], $object) + ); + + // Step 2: urlization (replace spaces by '-' etc...) + if (!$urlized) { + $slug = call_user_func_array( + $this->urlizer, + array($slug, $options['separator'], $object) + ); + } + + // add suffix/prefix + $slug = $options['prefix'].$slug.$options['suffix']; + + // Step 3: stylize the slug + switch ($options['style']) { + case 'camel': + $quotedSeparator = preg_quote($options['separator']); + $slug = preg_replace_callback('/^[a-z]|'.$quotedSeparator.'[a-z]/smi', function ($m) { + return strtoupper($m[0]); + }, $slug); + break; + + case 'lower': + if (function_exists('mb_strtolower')) { + $slug = mb_strtolower($slug); + } else { + $slug = strtolower($slug); + } + break; + + case 'upper': + if (function_exists('mb_strtoupper')) { + $slug = mb_strtoupper($slug); + } else { + $slug = strtoupper($slug); + } + break; + + default: + // leave it as is + break; + } + + // cut slug if exceeded in length + if (isset($mapping['length']) && strlen($slug) > $mapping['length']) { + $slug = substr($slug, 0, $mapping['length']); + } + + if (isset($mapping['nullable']) && $mapping['nullable'] && strlen($slug) === 0) { + $slug = null; + } + + // notify slug handlers --> beforeMakingUnique + if ($hasHandlers) { + foreach ($options['handlers'] as $class => $handlerOptions) { + $handler = $this->getHandler($class); + if ($handler instanceof SlugHandlerWithUniqueCallbackInterface) { + $handler->beforeMakingUnique($ea, $options, $object, $slug); + } + } + } + + // make unique slug if requested + if ($options['unique'] && null !== $slug) { + $this->exponent = 0; + $slug = $this->makeUniqueSlug($ea, $object, $slug, false, $options); + } + + // notify slug handlers --> onSlugCompletion + if ($hasHandlers) { + foreach ($options['handlers'] as $class => $handlerOptions) { + $this->getHandler($class)->onSlugCompletion($ea, $options, $object, $slug); + } + } + + // set the final slug + $meta->getReflectionProperty($slugField)->setValue($object, $slug); + // recompute changeset + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + // overwrite changeset (to set old value) + $uow->propertyChanged($object, $slugField, $oldSlug, $slug); + + } + } + } + + /** + * Generates the unique slug + * + * @param SluggableAdapter $ea + * @param object $object + * @param string $preferredSlug + * @param boolean $recursing + * @param array $config[$slugField] + * + * @return string - unique slug + */ + private function makeUniqueSlug(SluggableAdapter $ea, $object, $preferredSlug, $recursing = false, $config = array()) + { + $om = $ea->getObjectManager(); + $meta = $om->getClassMetadata(get_class($object)); + $similarPersisted = array(); + // extract unique base + $base = false; + + if ($config['unique'] && isset($config['unique_base'])) { + $base = $meta->getReflectionProperty($config['unique_base'])->getValue($object); + } + + // collect similar persisted slugs during this flush + if (isset($this->persisted[$class = $ea->getRootObjectClass($meta)])) { + foreach ($this->persisted[$class] as $obj) { + if ($base !== false && $meta->getReflectionProperty($config['unique_base'])->getValue($obj) !== $base) { + continue; // if unique_base field is not the same, do not take slug as similar + } + $slug = $meta->getReflectionProperty($config['slug'])->getValue($obj); + $quotedPreferredSlug = preg_quote($preferredSlug); + if (preg_match("@^{$quotedPreferredSlug}.*@smi", $slug)) { + $similarPersisted[] = array($config['slug'] => $slug); + } + } + } + + // load similar slugs + $result = array_merge((array) $ea->getSimilarSlugs($object, $meta, $config, $preferredSlug), $similarPersisted); + // leave only right slugs + + if (!$recursing) { + // filter similar slugs + $quotedSeparator = preg_quote($config['separator']); + $quotedPreferredSlug = preg_quote($preferredSlug); + foreach ($result as $key => $similar) { + if (!preg_match("@{$quotedPreferredSlug}($|{$quotedSeparator}[\d]+$)@smi", $similar[$config['slug']])) { + unset($result[$key]); + } + } + } + + if ($result) { + $generatedSlug = $preferredSlug; + $sameSlugs = array(); + + foreach ((array) $result as $list) { + $sameSlugs[] = $list[$config['slug']]; + } + + $i = pow(10, $this->exponent); + if ($recursing || in_array($generatedSlug, $sameSlugs)) { + do { + $generatedSlug = $preferredSlug.$config['separator'].$i++; + } while (in_array($generatedSlug, $sameSlugs)); + } + + $mapping = $meta->getFieldMapping($config['slug']); + if (isset($mapping['length']) && strlen($generatedSlug) > $mapping['length']) { + $generatedSlug = substr( + $generatedSlug, + 0, + $mapping['length'] - (strlen($i) + strlen($config['separator'])) + ); + $this->exponent = strlen($i) - 1; + if (substr($generatedSlug,-strlen($config['separator'])) == $config['separator']) { + $generatedSlug = substr($generatedSlug,0,strlen($generatedSlug) - strlen($config['separator'])); + } + $generatedSlug = $this->makeUniqueSlug($ea, $object, $generatedSlug, true, $config); + } + $preferredSlug = $generatedSlug; + } + + return $preferredSlug; + } + + /** + * @param \Doctrine\Common\Persistence\ObjectManager $om + */ + private function manageFiltersBeforeGeneration(ObjectManager $om) + { + $collection = $this->getFilterCollectionFromObjectManager($om); + + $enabledFilters = array_keys($collection->getEnabledFilters()); + + // set each managed filter to desired status + foreach ($this->managedFilters as $name => &$config) { + $enabled = in_array($name, $enabledFilters); + $config['previouslyEnabled'] = $enabled; + + if ($config['disabled']) { + if ($enabled) { + $collection->disable($name); + } + } else { + $collection->enable($name); + } + } + } + + /** + * @param \Doctrine\Common\Persistence\ObjectManager $om + */ + private function manageFiltersAfterGeneration(ObjectManager $om) + { + $collection = $this->getFilterCollectionFromObjectManager($om); + + // Restore managed filters to their original status + foreach ($this->managedFilters as $name => &$config) { + if ($config['previouslyEnabled'] === true) { + $collection->enable($name); + } + + unset($config['previouslyEnabled']); + } + } + + /** + * Retrieves a FilterCollection instance from the given ObjectManager. + * + * @param \Doctrine\Common\Persistence\ObjectManager $om + * + * @throws \Gedmo\Exception\InvalidArgumentException + * + * @return mixed + */ + private function getFilterCollectionFromObjectManager(ObjectManager $om) + { + if (is_callable(array($om, 'getFilters'))) { + return $om->getFilters(); + } elseif (is_callable(array($om, 'getFilterCollection'))) { + return $om->getFilterCollection(); + } + + throw new \Gedmo\Exception\InvalidArgumentException("ObjectManager does not support filters"); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Util/Urlizer.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Util/Urlizer.php new file mode 100644 index 0000000..f3331e9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sluggable/Util/Urlizer.php @@ -0,0 +1,12 @@ +getName(); + if (array_key_exists($class, $this->disabled) && $this->disabled[$class] === true) { + return array(); + } elseif (array_key_exists($targetEntity->rootDocumentName, $this->disabled) && $this->disabled[$targetEntity->rootDocumentName] === true) { + return array(); + } + + $config = $this->getListener()->getConfiguration($this->getDocumentManager(), $targetEntity->name); + + if (!isset($config['softDeleteable']) || !$config['softDeleteable']) { + return array(); + } + + $column = $targetEntity->fieldMappings[$config['fieldName']]; + + if (isset($config['timeAware']) && $config['timeAware']) { + return array( + '$or' => array( + array($column['fieldName'] => null), + array($column['fieldName'] => array('$gt' => new \DateTime('now'))), + ), + ); + } + + return array( + $column['fieldName'] => null, + ); + } + + protected function getListener() + { + if ($this->listener === null) { + $em = $this->getDocumentManager(); + $evm = $em->getEventManager(); + + foreach ($evm->getListeners() as $listeners) { + foreach ($listeners as $listener) { + if ($listener instanceof SoftDeleteableListener) { + $this->listener = $listener; + + break 2; + } + } + } + + if ($this->listener === null) { + throw new \RuntimeException('Listener "SoftDeleteableListener" was not added to the EventManager!'); + } + } + + return $this->listener; + } + + protected function getDocumentManager() + { + if ($this->documentManager === null) { + $refl = new \ReflectionProperty('Doctrine\ODM\MongoDB\Query\Filter\BsonFilter', 'dm'); + $refl->setAccessible(true); + $this->documentManager = $refl->getValue($this); + } + + return $this->documentManager; + } + + public function disableForDocument($class) + { + $this->disabled[$class] = true; + } + + public function enableForDocument($class) + { + $this->disabled[$class] = false; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Filter/SoftDeleteableFilter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Filter/SoftDeleteableFilter.php new file mode 100644 index 0000000..14fb066 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Filter/SoftDeleteableFilter.php @@ -0,0 +1,127 @@ + + * @author Gediminas Morkevicius + * @author Patrik Votoček + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class SoftDeleteableFilter extends SQLFilter +{ + /** + * @var SoftDeleteableListener + */ + protected $listener; + + /** + * @var EntityManagerInterface + */ + protected $entityManager; + + /** + * @var string[bool] + */ + protected $disabled = array(); + + /** + * @param ClassMetadata $targetEntity + * @param string $targetTableAlias + * @return string + */ + public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias) + { + $class = $targetEntity->getName(); + if (array_key_exists($class, $this->disabled) && $this->disabled[$class] === true) { + return ''; + } elseif (array_key_exists($targetEntity->rootEntityName, $this->disabled) && $this->disabled[$targetEntity->rootEntityName] === true) { + return ''; + } + + $config = $this->getListener()->getConfiguration($this->getEntityManager(), $targetEntity->name); + + if (!isset($config['softDeleteable']) || !$config['softDeleteable']) { + return ''; + } + + $conn = $this->getEntityManager()->getConnection(); + $platform = $conn->getDatabasePlatform(); + $column = $targetEntity->getQuotedColumnName($config['fieldName'], $platform); + + $addCondSql = $platform->getIsNullExpression($targetTableAlias.'.'.$column); + if (isset($config['timeAware']) && $config['timeAware']) { + $now = $conn->quote(date($platform->getDateTimeFormatString())); // should use UTC in database and PHP + $addCondSql = "({$addCondSql} OR {$targetTableAlias}.{$column} > {$now})"; + } + + return $addCondSql; + } + + /** + * @param string $class + */ + public function disableForEntity($class) + { + $this->disabled[$class] = true; + } + + /** + * @param string $class + */ + public function enableForEntity($class) + { + $this->disabled[$class] = false; + } + + /** + * @return SoftDeleteableListener + * @throws \RuntimeException + */ + protected function getListener() + { + if ($this->listener === null) { + $em = $this->getEntityManager(); + $evm = $em->getEventManager(); + + foreach ($evm->getListeners() as $listeners) { + foreach ($listeners as $listener) { + if ($listener instanceof SoftDeleteableListener) { + $this->listener = $listener; + + break 2; + } + } + } + + if ($this->listener === null) { + throw new \RuntimeException('Listener "SoftDeleteableListener" was not added to the EventManager!'); + } + } + + return $this->listener; + } + + /** + * @return EntityManagerInterface + */ + protected function getEntityManager() + { + if ($this->entityManager === null) { + $refl = new \ReflectionProperty('Doctrine\ORM\Query\Filter\SQLFilter', 'em'); + $refl->setAccessible(true); + $this->entityManager = $refl->getValue($this); + } + + return $this->entityManager; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..c54ffa9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Annotation.php @@ -0,0 +1,59 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to define that this object is loggable + */ + const SOFT_DELETEABLE = 'Gedmo\\Mapping\\Annotation\\SoftDeleteable'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // class annotations + if ($class !== null && $annot = $this->reader->getClassAnnotation($class, self::SOFT_DELETEABLE)) { + $config['softDeleteable'] = true; + + Validator::validateField($meta, $annot->fieldName); + + $config['fieldName'] = $annot->fieldName; + + $config['timeAware'] = false; + if (isset($annot->timeAware)) { + if (!is_bool($annot->timeAware)) { + throw new InvalidMappingException("timeAware must be boolean. ".gettype($annot->timeAware)." provided."); + } + $config['timeAware'] = $annot->timeAware; + } + + $config['hardDelete'] = true; + if (isset($annot->hardDelete)) { + if (!is_bool($annot->hardDelete)) { + throw new InvalidMappingException("hardDelete must be boolean. ".gettype($annot->hardDelete)." provided."); + } + $config['hardDelete'] = $annot->hardDelete; + } + } + + $this->validateFullMetadata($meta, $config); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..d9ad758 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Xml.php @@ -0,0 +1,59 @@ + + * @author Gediminas Morkevicius + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + $xmlDoctrine = $xml; + $xml = $xml->children(self::GEDMO_NAMESPACE_URI); + + if (in_array($xmlDoctrine->getName(), array('mapped-superclass', 'entity', 'document', 'embedded-document'))) { + if (isset($xml->{'soft-deleteable'})) { + $field = $this->_getAttribute($xml->{'soft-deleteable'}, 'field-name'); + + if (!$field) { + throw new InvalidMappingException('Field name for SoftDeleteable class is mandatory.'); + } + + Validator::validateField($meta, $field); + + $config['softDeleteable'] = true; + $config['fieldName'] = $field; + + $config['timeAware'] = false; + if ($this->_isAttributeSet($xml->{'soft-deleteable'}, 'time-aware')) { + $config['timeAware'] = $this->_getBooleanAttribute($xml->{'soft-deleteable'}, 'time-aware'); + } + + $config['hardDelete'] = true; + if ($this->_isAttributeSet($xml->{'soft-deleteable'}, 'hard-delete')) { + $config['hardDelete'] = $this->_getBooleanAttribute($xml->{'soft-deleteable'}, 'hard-delete'); + } + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..01e93f0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Driver/Yaml.php @@ -0,0 +1,76 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['gedmo'])) { + $classMapping = $mapping['gedmo']; + if (isset($classMapping['soft_deleteable'])) { + $config['softDeleteable'] = true; + + if (!isset($classMapping['soft_deleteable']['field_name'])) { + throw new InvalidMappingException('Field name for SoftDeleteable class is mandatory.'); + } + + $fieldName = $classMapping['soft_deleteable']['field_name']; + + Validator::validateField($meta, $fieldName); + + $config['fieldName'] = $fieldName; + + $config['timeAware'] = false; + if (isset($classMapping['soft_deleteable']['time_aware'])) { + if (!is_bool($classMapping['soft_deleteable']['time_aware'])) { + throw new InvalidMappingException("timeAware must be boolean. ".gettype($classMapping['soft_deleteable']['time_aware'])." provided."); + } + $config['timeAware'] = $classMapping['soft_deleteable']['time_aware']; + } + + $config['hardDelete'] = true; + if (isset($classMapping['soft_deleteable']['hard_delete'])) { + if (!is_bool($classMapping['soft_deleteable']['hard_delete'])) { + throw new InvalidMappingException("hardDelete must be boolean. ".gettype($classMapping['soft_deleteable']['hard_delete'])." provided."); + } + $config['hardDelete'] = $classMapping['soft_deleteable']['hard_delete']; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..c1e721e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,17 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements SoftDeleteableAdapter +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/SoftDeleteableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/SoftDeleteableAdapter.php new file mode 100644 index 0000000..6a67e4f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Event/SoftDeleteableAdapter.php @@ -0,0 +1,16 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface SoftDeleteableAdapter extends AdapterInterface +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Validator.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Validator.php new file mode 100644 index 0000000..0620704 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Mapping/Validator.php @@ -0,0 +1,52 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class Validator +{ + /** + * List of types which are valid for timestamp + * + * @var array + */ + public static $validTypes = array( + 'date', + 'date_immutable', + 'time', + 'time_immutable', + 'datetime', + 'datetime_immutable', + 'datetimetz', + 'datetimetz_immutable', + 'timestamp', + 'zenddate', + ); + + public static function validateField(ClassMetadata $meta, $field) + { + if ($meta->isMappedSuperclass) { + return; + } + + $fieldMapping = $meta->getFieldMapping($field); + + if (!in_array($fieldMapping['type'], self::$validTypes)) { + throw new InvalidMappingException(sprintf('Field "%s" (type "%s") must be of one of the following types: "%s" in entity %s', + $field, + $fieldMapping['type'], + implode(', ', self::$validTypes), + $meta->name)); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/Exec/MultiTableDeleteExecutor.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/Exec/MultiTableDeleteExecutor.php new file mode 100644 index 0000000..9d89854 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/Exec/MultiTableDeleteExecutor.php @@ -0,0 +1,48 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class MultiTableDeleteExecutor extends BaseMultiTableDeleteExecutor +{ + /** + * {@inheritDoc} + */ + public function __construct(Node $AST, $sqlWalker, ClassMetadataInfo $meta, AbstractPlatform $platform, array $config) + { + parent::__construct($AST, $sqlWalker); + + $reflProp = new \ReflectionProperty(get_class($this), '_sqlStatements'); + $reflProp->setAccessible(true); + + $sqlStatements = $reflProp->getValue($this); + + foreach ($sqlStatements as $index => $stmt) { + $matches = array(); + preg_match('/DELETE FROM (\w+) .+/', $stmt, $matches); + + if (isset($matches[1]) && $meta->getQuotedTableName($platform) === $matches[1]) { + $sqlStatements[$index] = str_replace('DELETE FROM', 'UPDATE', $stmt); + $sqlStatements[$index] = str_replace('WHERE', 'SET '.$config['fieldName'].' = "'.date('Y-m-d H:i:s').'" WHERE', $sqlStatements[$index]); + } else { + // We have to avoid the removal of registers of child entities of a SoftDeleteable entity + unset($sqlStatements[$index]); + } + } + + $reflProp->setValue($this, $sqlStatements); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/SoftDeleteableWalker.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/SoftDeleteableWalker.php new file mode 100644 index 0000000..bfa8615 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Query/TreeWalker/SoftDeleteableWalker.php @@ -0,0 +1,141 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class SoftDeleteableWalker extends SqlWalker +{ + protected $conn; + protected $platform; + protected $listener; + protected $configuration; + protected $alias; + protected $deletedAtField; + protected $meta; + + /** + * {@inheritDoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + parent::__construct($query, $parserResult, $queryComponents); + + $this->conn = $this->getConnection(); + $this->platform = $this->conn->getDatabasePlatform(); + $this->listener = $this->getSoftDeleteableListener(); + $this->extractComponents($queryComponents); + } + + /** + * {@inheritDoc} + */ + public function getExecutor($AST) + { + switch (true) { + case ($AST instanceof DeleteStatement): + $primaryClass = $this->getEntityManager()->getClassMetadata($AST->deleteClause->abstractSchemaName); + + return ($primaryClass->isInheritanceTypeJoined()) + ? new MultiTableDeleteExecutor($AST, $this, $this->meta, $this->platform, $this->configuration) + : new SingleTableDeleteUpdateExecutor($AST, $this); + default: + throw new \Gedmo\Exception\UnexpectedValueException('SoftDeleteable walker should be used only on delete statement'); + } + } + + /** + * Change a DELETE clause for an UPDATE clause + * + * @param DeleteClause $deleteClause + * + * @return string The SQL. + */ + public function walkDeleteClause(DeleteClause $deleteClause) + { + $em = $this->getEntityManager(); + $class = $em->getClassMetadata($deleteClause->abstractSchemaName); + $tableName = $class->getTableName(); + $this->setSQLTableAlias($tableName, $tableName, $deleteClause->aliasIdentificationVariable); + $quotedTableName = $class->getQuotedTableName($this->platform); + $quotedColumnName = $class->getQuotedColumnName($this->deletedAtField, $this->platform); + + $sql = 'UPDATE '.$quotedTableName.' SET '.$quotedColumnName.' = '.$this->conn->quote(date( + $this->platform->getDateTimeFormatString() + )); + + return $sql; + } + + /** + * Get the currently used SoftDeleteableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return SoftDeleteableListener + */ + private function getSoftDeleteableListener() + { + if (is_null($this->listener)) { + $em = $this->getEntityManager(); + + foreach ($em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof SoftDeleteableListener) { + $this->listener = $listener; + break; + } + } + if ($this->listener) { + break; + } + } + + if (is_null($this->listener)) { + throw new \Gedmo\Exception\RuntimeException('The SoftDeleteable listener could not be found.'); + } + } + + return $this->listener; + } + + /** + * Search for components in the delete clause + * + * @param array $queryComponents + * + * @return void + */ + private function extractComponents(array $queryComponents) + { + $em = $this->getEntityManager(); + + foreach ($queryComponents as $alias => $comp) { + if (!isset($comp['metadata'])) { + continue; + } + $meta = $comp['metadata']; + $config = $this->listener->getConfiguration($em, $meta->name); + if ($config && isset($config['softDeleteable']) && $config['softDeleteable']) { + $this->configuration = $config; + $this->deletedAtField = $config['fieldName']; + $this->meta = $meta; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteable.php new file mode 100644 index 0000000..6faa3d0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteable.php @@ -0,0 +1,27 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface SoftDeleteable +{ + // this interface is not necessary to implement + + /** + * @gedmo:SoftDeleteable + * to mark the class as SoftDeleteable use class annotation @gedmo:SoftDeleteable + * this object will be able to be soft deleted + * example: + * + * @gedmo:SoftDeleteable + * class MyEntity + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteableListener.php new file mode 100644 index 0000000..5caad85 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/SoftDeleteableListener.php @@ -0,0 +1,119 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class SoftDeleteableListener extends MappedEventSubscriber +{ + /** + * Pre soft-delete event + * + * @var string + */ + const PRE_SOFT_DELETE = "preSoftDelete"; + + /** + * Post soft-delete event + * + * @var string + */ + const POST_SOFT_DELETE = "postSoftDelete"; + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array( + 'loadClassMetadata', + 'onFlush', + ); + } + + /** + * If it's a SoftDeleteable object, update the "deletedAt" field + * and skip the removal of the object + * + * @param EventArgs $args + * + * @return void + */ + public function onFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + $evm = $om->getEventManager(); + + //getScheduledDocumentDeletions + foreach ($ea->getScheduledObjectDeletions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + + if (isset($config['softDeleteable']) && $config['softDeleteable']) { + $reflProp = $meta->getReflectionProperty($config['fieldName']); + $oldValue = $reflProp->getValue($object); + $date = new \DateTime(); + + // Remove `$oldValue instanceof \DateTime` check when PHP version is bumped to >=5.5 + if (isset($config['hardDelete']) && $config['hardDelete'] && ($oldValue instanceof \DateTime || $oldValue instanceof \DateTimeInterface) && $oldValue <= $date) { + continue; // want to hard delete + } + + $evm->dispatchEvent( + self::PRE_SOFT_DELETE, + $ea->createLifecycleEventArgsInstance($object, $om) + ); + + + $reflProp->setValue($object, $date); + + $om->persist($object); + $uow->propertyChanged($object, $config['fieldName'], $oldValue, $date); + if ($uow instanceof MongoDBUnitOfWork && !method_exists($uow, 'scheduleExtraUpdate')) { + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + } else { + $uow->scheduleExtraUpdate($object, array( + $config['fieldName'] => array($oldValue, $date), + )); + } + + $evm->dispatchEvent( + self::POST_SOFT_DELETE, + $ea->createLifecycleEventArgsInstance($object, $om) + ); + } + } + } + + /** + * Maps additional metadata + * + * @param EventArgs $eventArgs + * + * @return void + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteable.php new file mode 100644 index 0000000..8296b03 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteable.php @@ -0,0 +1,51 @@ += 5.4 + * + * @author Wesley van Opdorp + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait SoftDeleteable +{ + /** + * @var \DateTime + */ + protected $deletedAt; + + /** + * Sets deletedAt. + * + * @param \DateTime|null $deletedAt + * + * @return $this + */ + public function setDeletedAt(\DateTime $deletedAt = null) + { + $this->deletedAt = $deletedAt; + + return $this; + } + + /** + * Returns deletedAt. + * + * @return \DateTime + */ + public function getDeletedAt() + { + return $this->deletedAt; + } + + /** + * Is deleted? + * + * @return bool + */ + public function isDeleted() + { + return null !== $this->deletedAt; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableDocument.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableDocument.php new file mode 100644 index 0000000..5e004b7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableDocument.php @@ -0,0 +1,54 @@ += 5.4 + * + * @author Wesley van Opdorp + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait SoftDeleteableDocument +{ + /** + * @var \DateTime + * @ODM\Field(type="date") + */ + protected $deletedAt; + + /** + * Sets deletedAt. + * + * @param \DateTime|null $deletedAt + * + * @return $this + */ + public function setDeletedAt(\DateTime $deletedAt = null) + { + $this->deletedAt = $deletedAt; + + return $this; + } + + /** + * Returns deletedAt. + * + * @return \DateTime + */ + public function getDeletedAt() + { + return $this->deletedAt; + } + + /** + * Is deleted? + * + * @return bool + */ + public function isDeleted() + { + return null !== $this->deletedAt; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableEntity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableEntity.php new file mode 100644 index 0000000..b2948d0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/SoftDeleteable/Traits/SoftDeleteableEntity.php @@ -0,0 +1,54 @@ += 5.4 + * + * @author Wesley van Opdorp + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait SoftDeleteableEntity +{ + /** + * @var \DateTime + * @ORM\Column(type="datetime", nullable=true) + */ + protected $deletedAt; + + /** + * Sets deletedAt. + * + * @param \DateTime|null $deletedAt + * + * @return $this + */ + public function setDeletedAt(\DateTime $deletedAt = null) + { + $this->deletedAt = $deletedAt; + + return $this; + } + + /** + * Returns deletedAt. + * + * @return \DateTime + */ + public function getDeletedAt() + { + return $this->deletedAt; + } + + /** + * Is deleted? + * + * @return bool + */ + public function isDeleted() + { + return null !== $this->deletedAt; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Entity/Repository/SortableRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Entity/Repository/SortableRepository.php new file mode 100644 index 0000000..27fd020 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Entity/Repository/SortableRepository.php @@ -0,0 +1,91 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class SortableRepository extends EntityRepository +{ + /** + * Sortable listener on event manager + * + * @var SortableListener + */ + protected $listener = null; + + protected $config = null; + protected $meta = null; + + public function __construct(EntityManagerInterface $em, ClassMetadata $class) + { + parent::__construct($em, $class); + $sortableListener = null; + foreach ($em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof SortableListener) { + $sortableListener = $listener; + break; + } + } + if ($sortableListener) { + break; + } + } + + if (is_null($sortableListener)) { + throw new \Gedmo\Exception\InvalidMappingException('This repository can be attached only to ORM sortable listener'); + } + + $this->listener = $sortableListener; + $this->meta = $this->getClassMetadata(); + $this->config = $this->listener->getConfiguration($this->_em, $this->meta->name); + } + + public function getBySortableGroupsQuery(array $groupValues = array()) + { + return $this->getBySortableGroupsQueryBuilder($groupValues)->getQuery(); + } + + public function getBySortableGroupsQueryBuilder(array $groupValues = array()) + { + $groups = isset($this->config['groups']) ? array_combine(array_values($this->config['groups']), array_keys($this->config['groups'])) : array(); + foreach ($groupValues as $name => $value) { + if (!in_array($name, $this->config['groups'])) { + throw new \InvalidArgumentException('Sortable group "'.$name.'" is not defined in Entity '.$this->meta->name); + } + unset($groups[$name]); + } + if (count($groups) > 0) { + throw new \InvalidArgumentException( + 'You need to specify values for the following groups to select by sortable groups: '.implode(", ", array_keys($groups))); + } + + $qb = $this->createQueryBuilder('n'); + $qb->orderBy('n.'.$this->config['position']); + $i = 1; + foreach ($groupValues as $group => $value) { + $qb->andWhere('n.'.$group.' = :group'.$i) + ->setParameter('group'.$i, $value); + $i++; + } + + return $qb; + } + + public function getBySortableGroups(array $groupValues = array()) + { + $query = $this->getBySortableGroupsQuery($groupValues); + + return $query->getResult(); + } + +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..c277a26 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Annotation.php @@ -0,0 +1,88 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to mark field as one which will store node position + */ + const POSITION = 'Gedmo\\Mapping\\Annotation\\SortablePosition'; + + /** + * Annotation to mark field as sorting group + */ + const GROUP = 'Gedmo\\Mapping\\Annotation\\SortableGroup'; + + /** + * List of types which are valid for position fields + * + * @var array + */ + protected $validTypes = array( + 'int', + 'integer', + 'smallint', + 'bigint', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + + // position + if ($this->reader->getPropertyAnnotation($property, self::POSITION)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'position' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Sortable position field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['position'] = $field; + } + + // group + if ($this->reader->getPropertyAnnotation($property, self::GROUP)) { + $field = $property->getName(); + if (!$meta->hasField($field) && !$meta->hasAssociation($field)) { + throw new InvalidMappingException("Unable to find 'group' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!isset($config['groups'])) { + $config['groups'] = array(); + } + $config['groups'][] = $field; + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (!isset($config['position'])) { + throw new InvalidMappingException("Missing property: 'position' in class - {$meta->name}"); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..4675aa0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Xml.php @@ -0,0 +1,107 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * List of types which are valid for position field + * + * @var array + */ + private $validTypes = array( + 'int', + 'integer', + 'smallint', + 'bigint', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + + if (isset($xml->field)) { + foreach ($xml->field as $mappingDoctrine) { + $mapping = $mappingDoctrine->children(self::GEDMO_NAMESPACE_URI); + + $field = $this->_getAttribute($mappingDoctrine, 'name'); + if (isset($mapping->{'sortable-position'})) { + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Sortable position field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['position'] = $field; + } + } + $this->readSortableGroups($xml->field, $config, 'name'); + } + + // Search for sortable-groups in association mappings + if (isset($xml->{'many-to-one'})) { + $this->readSortableGroups($xml->{'many-to-one'}, $config); + } + + // Search for sortable-groups in association mappings + if (isset($xml->{'many-to-many'})) { + $this->readSortableGroups($xml->{'many-to-many'}, $config); + } + + if (!$meta->isMappedSuperclass && $config) { + if (!isset($config['position'])) { + throw new InvalidMappingException("Missing property: 'position' in class - {$meta->name}"); + } + } + } + + /** + * @param \SimpleXMLElement[] $mapping + * @param array $config + * @param string $fieldAttr + */ + private function readSortableGroups($mapping, array &$config, $fieldAttr = 'field') + { + foreach ($mapping as $mappingDoctrine) { + $map = $mappingDoctrine->children(self::GEDMO_NAMESPACE_URI); + + $field = $this->_getAttribute($mappingDoctrine, $fieldAttr); + if (isset($map->{'sortable-group'})) { + if (!isset($config['groups'])) { + $config['groups'] = array(); + } + $config['groups'][] = $field; + } + } + } + + /** + * Checks if $field type is valid as Sortable Position field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..e47000a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Driver/Yaml.php @@ -0,0 +1,108 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * List of types which are valid for position fields + * + * @var array + */ + private $validTypes = array( + 'int', + 'integer', + 'smallint', + 'bigint', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('sortablePosition', $fieldMapping['gedmo'])) { + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Sortable position field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['position'] = $field; + } + } + } + $this->readSortableGroups($mapping['fields'], $config); + } + if (isset($mapping['manyToOne'])) { + $this->readSortableGroups($mapping['manyToOne'], $config); + } + if (isset($mapping['manyToMany'])) { + $this->readSortableGroups($mapping['manyToMany'], $config); + } + + if (!$meta->isMappedSuperclass && $config) { + if (!isset($config['position'])) { + throw new InvalidMappingException("Missing property: 'position' in class - {$meta->name}"); + } + } + } + + private function readSortableGroups($mapping, array &$config) + { + foreach ($mapping as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('sortableGroup', $fieldMapping['gedmo'])) { + if (!isset($config['groups'])) { + $config['groups'] = array(); + } + $config['groups'][] = $field; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + /** + * Checks if $field type is valid as SortablePosition field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..773a837 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,64 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements SortableAdapter +{ + public function getMaxPosition(array $config, $meta, $groups) + { + $dm = $this->getObjectManager(); + + $qb = $dm->createQueryBuilder($config['useObjectClass']); + foreach ($groups as $group => $value) { + if (is_object($value) && !$dm->getMetadataFactory()->isTransient(ClassUtils::getClass($value))) { + $qb->field($group)->references($value); + } else { + $qb->field($group)->equals($value); + } + } + $qb->sort($config['position'], 'desc'); + $document = $qb->getQuery()->getSingleResult(); + + if ($document) { + return $meta->getReflectionProperty($config['position'])->getValue($document); + } + + return -1; + } + + public function updatePositions($relocation, $delta, $config) + { + $dm = $this->getObjectManager(); + + $delta = array_map('intval', $delta); + + $qb = $dm->createQueryBuilder($config['useObjectClass']); + $qb->update(); + $qb->multiple(true); + $qb->field($config['position'])->inc($delta['delta']); + $qb->field($config['position'])->gte($delta['start']); + if ($delta['stop'] > 0) { + $qb->field($config['position'])->lt($delta['stop']); + } + foreach ($relocation['groups'] as $group => $value) { + if (is_object($value) && !$dm->getMetadataFactory()->isTransient(ClassUtils::getClass($value))) { + $qb->field($group)->references($value); + } else { + $qb->field($group)->equals($value); + } + } + + $qb->getQuery()->execute(); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..8272348 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,104 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements SortableAdapter +{ + public function getMaxPosition(array $config, $meta, $groups) + { + $em = $this->getObjectManager(); + + $qb = $em->createQueryBuilder(); + $qb->select('MAX(n.'.$config['position'].')') + ->from($config['useObjectClass'], 'n'); + $this->addGroupWhere($qb, $groups); + $query = $qb->getQuery(); + $query->useQueryCache(false); + $query->useResultCache(false); + $res = $query->getResult(); + + return $res[0][1]; + } + + private function addGroupWhere(QueryBuilder $qb, $groups) + { + $i = 1; + foreach ($groups as $group => $value) { + if (is_null($value)) { + $qb->andWhere($qb->expr()->isNull('n.'.$group)); + } else { + $qb->andWhere('n.'.$group.' = :group__'.$i); + $qb->setParameter('group__'.$i, $value); + } + $i++; + } + } + + public function updatePositions($relocation, $delta, $config) + { + $sign = $delta['delta'] < 0 ? "-" : "+"; + $absDelta = abs($delta['delta']); + $dql = "UPDATE {$relocation['name']} n"; + $dql .= " SET n.{$config['position']} = n.{$config['position']} {$sign} {$absDelta}"; + $dql .= " WHERE n.{$config['position']} >= {$delta['start']}"; + // if not null, false or 0 + if ($delta['stop'] > 0) { + $dql .= " AND n.{$config['position']} < {$delta['stop']}"; + } + $i = -1; + $params = array(); + foreach ($relocation['groups'] as $group => $value) { + if (is_null($value)) { + $dql .= " AND n.{$group} IS NULL"; + } else { + $dql .= " AND n.{$group} = :val___".(++$i); + $params['val___'.$i] = $value; + } + } + + // add excludes + if (!empty($delta['exclude'])) { + $meta = $this->getObjectManager()->getClassMetadata($relocation['name']); + if (count($meta->identifier) == 1) { + // if we only have one identifier, we can use IN syntax, for better performance + $excludedIds = array(); + foreach ($delta['exclude'] as $entity) { + if ($id = $meta->getFieldValue($entity, $meta->identifier[0])) { + $excludedIds[] = $id; + } + } + if (!empty($excludedIds)) { + $params['excluded'] = $excludedIds; + $dql .= " AND n.{$meta->identifier[0]} NOT IN (:excluded)"; + } + } else if (count($meta->identifier) > 1) { + foreach ($delta['exclude'] as $entity) { + $j = 0; + $dql .= " AND NOT ("; + foreach ($meta->getIdentifierValues($entity) as $id => $value) { + $dql .= ($j > 0 ? " AND " : "") . "n.{$id} = :val___".(++$i); + $params['val___'.$i] = $value; + $j++; + } + $dql .= ")"; + } + } + } + + $em = $this->getObjectManager(); + $q = $em->createQuery($dql); + $q->setParameters($params); + $q->getSingleScalarResult(); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/SortableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/SortableAdapter.php new file mode 100644 index 0000000..dba55cf --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Mapping/Event/SortableAdapter.php @@ -0,0 +1,16 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface SortableAdapter extends AdapterInterface +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Sortable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Sortable.php new file mode 100644 index 0000000..6e757dc --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/Sortable.php @@ -0,0 +1,42 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Sortable +{ + // use now annotations instead of predefined methods, this interface is not necessary + + /** + * @gedmo:SortablePosition - to mark property which will hold the item position use annotation @gedmo:SortablePosition + * This property has to be numeric. The position index can be negative and will be counted from right to left. + * + * example: + * + * @gedmo:SortablePosition + * @Column(type="int") + * $position + * + * @gedmo:SortableGroup + * @Column(type="string", length=64) + * $category + * + */ + + /** + * @gedmo:SortableGroup - to group node sorting by a property use annotation @gedmo:SortableGroup on this property + * + * example: + * + * @gedmo:SortableGroup + * @Column(type="string", length=64) + * $category + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php new file mode 100644 index 0000000..b327bfa --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Sortable/SortableListener.php @@ -0,0 +1,620 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class SortableListener extends MappedEventSubscriber +{ + private $relocations = array(); + private $persistenceNeeded = false; + private $maxPositions = array(); + + /** + * Specifies the list of events to listen + * + * @return array + */ + public function getSubscribedEvents() + { + return array( + 'onFlush', + 'loadClassMetadata', + 'prePersist', + 'postPersist', + 'preUpdate', + 'postRemove', + 'postFlush', + ); + } + + /** + * Maps additional metadata + * + * @param EventArgs $args + */ + public function loadClassMetadata(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $args->getClassMetadata()); + } + + /** + * Collect position updates on objects being updated during flush + * if they require changing. + * + * Persisting of positions is done later during prePersist, preUpdate and postRemove + * events, otherwise the queries won't be executed within the transaction. + * + * The synchronization of the objects in memory is done in postFlush. This + * ensures that the positions have been successfully persisted to database. + * + * @param EventArgs $args + */ + public function onFlush(EventArgs $args) + { + $this->persistenceNeeded = true; + + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + // process all objects being deleted + foreach ($ea->getScheduledObjectDeletions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($config = $this->getConfiguration($om, $meta->name)) { + $this->processDeletion($ea, $config, $meta, $object); + } + } + + // process all objects being updated + foreach ($ea->getScheduledObjectUpdates($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($config = $this->getConfiguration($om, $meta->name)) { + $this->processUpdate($ea, $config, $meta, $object); + } + } + + // process all objects being inserted + foreach ($ea->getScheduledObjectInsertions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($config = $this->getConfiguration($om, $meta->name)) { + $this->processInsert($ea, $config, $meta, $object); + } + } + } + + /** + * Update maxPositions as needed + * + * @param EventArgs $args + */ + public function prePersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($config = $this->getConfiguration($om, $meta->name)) { + // Get groups + $groups = $this->getGroups($meta, $config, $object); + + // Get hash + $hash = $this->getHash($groups, $config); + + // Get max position + if (!isset($this->maxPositions[$hash])) { + $this->maxPositions[$hash] = $this->getMaxPosition($ea, $meta, $config, $object); + } + } + } + + public function postPersist(EventArgs $args) + { + // persist position updates here, so that the update queries + // are executed within transaction + $this->persistRelocations($this->getEventAdapter($args)); + } + + public function preUpdate(EventArgs $args) + { + // persist position updates here, so that the update queries + // are executed within transaction + $this->persistRelocations($this->getEventAdapter($args)); + } + + public function postRemove(EventArgs $args) + { + // persist position updates here, so that the update queries + // are executed within transaction + $this->persistRelocations($this->getEventAdapter($args)); + } + + /** + * Computes node positions and updates the sort field in memory and in the db + * + * @param SortableAdapter $ea + * @param array $config + * @param ClassMetadata $meta + * @param object $object + */ + protected function processInsert(SortableAdapter $ea, array $config, $meta, $object) + { + $em = $ea->getObjectManager(); + $uow = $em->getUnitOfWork(); + + $old = $meta->getReflectionProperty($config['position'])->getValue($object); + $newPosition = $meta->getReflectionProperty($config['position'])->getValue($object); + + if (is_null($newPosition)) { + $newPosition = -1; + } + + // Get groups + $groups = $this->getGroups($meta, $config, $object); + + // Get hash + $hash = $this->getHash($groups, $config); + + // Get max position + if (!isset($this->maxPositions[$hash])) { + $this->maxPositions[$hash] = $this->getMaxPosition($ea, $meta, $config, $object); + } + + // Compute position if it is negative + if ($newPosition < 0) { + $newPosition += $this->maxPositions[$hash] + 2; // position == -1 => append at end of list + if ($newPosition < 0) { + $newPosition = 0; + } + } + + // Set position to max position if it is too big + $newPosition = min(array($this->maxPositions[$hash] + 1, $newPosition)); + + // Compute relocations + // New inserted entities should not be relocated by position update, so we exclude it. + // Otherwise they could be relocated unintentionally. + $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, -1, +1, array($object)); + + // Apply existing relocations + $applyDelta = 0; + if (isset($this->relocations[$hash])) { + foreach ($this->relocations[$hash]['deltas'] as $delta) { + if ($delta['start'] <= $newPosition + && ($delta['stop'] > $newPosition || $delta['stop'] < 0)) { + $applyDelta += $delta['delta']; + } + } + } + $newPosition += $applyDelta; + + // Add relocations + call_user_func_array(array($this, 'addRelocation'), $relocation); + + // Set new position + if ($old < 0 || is_null($old)) { + $this->setFieldValue($ea, $object, $config['position'], $old, $newPosition); + } + } + + /** + * Computes node positions and updates the sort field in memory and in the db + * + * @param SortableAdapter $ea + * @param array $config + * @param ClassMetadata $meta + * @param object $object + */ + protected function processUpdate(SortableAdapter $ea, array $config, $meta, $object) + { + $em = $ea->getObjectManager(); + $uow = $em->getUnitOfWork(); + + $changed = false; + $groupHasChanged = false; + $changeSet = $ea->getObjectChangeSet($uow, $object); + + // Get groups + $groups = $this->getGroups($meta, $config, $object); + + // handle old groups + $oldGroups = $groups; + foreach (array_keys($groups) as $group) { + if (array_key_exists($group, $changeSet)) { + $changed = true; + $oldGroups[$group] = $changeSet[$group][0]; + } + } + + if ($changed) { + $oldHash = $this->getHash($oldGroups, $config); + $this->maxPositions[$oldHash] = $this->getMaxPosition($ea, $meta, $config, $object, $oldGroups); + if (array_key_exists($config['position'], $changeSet)) { + $oldPosition = $changeSet[$config['position']][0]; + } else { + $oldPosition = $meta->getReflectionProperty($config['position'])->getValue($object); + } + $this->addRelocation($oldHash, $config['useObjectClass'], $oldGroups, $oldPosition + 1, $this->maxPositions[$oldHash] + 1, -1); + $groupHasChanged = true; + } + + // Get hash + $hash = $this->getHash($groups, $config); + + // Get max position + if (!isset($this->maxPositions[$hash])) { + $this->maxPositions[$hash] = $this->getMaxPosition($ea, $meta, $config, $object); + } + + if (array_key_exists($config['position'], $changeSet)) { + if ($changed && -1 === $this->maxPositions[$hash]) { + // position has changed + // the group of element has changed + // and the target group has no children before + $oldPosition = -1; + $newPosition = -1; + } else { + // position was manually updated + $oldPosition = $changeSet[$config['position']][0]; + $newPosition = $changeSet[$config['position']][1]; + $changed = $changed || $oldPosition != $newPosition; + } + } elseif ($changed) { + $newPosition = $oldPosition; + } + + if ($groupHasChanged) { + $oldPosition = -1; + } + if (!$changed) { + return; + } + + // Compute position if it is negative + if ($newPosition < 0) { + if ($oldPosition === -1) { + $newPosition += $this->maxPositions[$hash] + 2; // position == -1 => append at end of list + } else { + $newPosition += $this->maxPositions[$hash] + 1; // position == -1 => append at end of list + } + + if ($newPosition < 0) { + $newPosition = 0; + } + } elseif ($newPosition > $this->maxPositions[$hash]) { + if ($groupHasChanged) { + $newPosition = $this->maxPositions[$hash] + 1; + } else { + $newPosition = $this->maxPositions[$hash]; + } + } else { + $newPosition = min(array($this->maxPositions[$hash], $newPosition)); + } + + // Compute relocations + /* + CASE 1: shift backwards + |----0----|----1----|----2----|----3----|----4----| + |--node1--|--node2--|--node3--|--node4--|--node5--| + Update node4: setPosition(1) + --> Update position + 1 where position in [1,3) + |--node1--|--node4--|--node2--|--node3--|--node5--| + CASE 2: shift forward + |----0----|----1----|----2----|----3----|----4----| + |--node1--|--node2--|--node3--|--node4--|--node5--| + Update node2: setPosition(3) + --> Update position - 1 where position in (1,3] + |--node1--|--node3--|--node4--|--node2--|--node5--| + */ + $relocation = null; + if ($oldPosition === -1) { + // special case when group changes + $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, -1, +1); + } elseif ($newPosition < $oldPosition) { + $relocation = array($hash, $config['useObjectClass'], $groups, $newPosition, $oldPosition, +1); + } elseif ($newPosition > $oldPosition) { + $relocation = array($hash, $config['useObjectClass'], $groups, $oldPosition + 1, $newPosition + 1, -1); + } + + // Apply existing relocations + $applyDelta = 0; + if (isset($this->relocations[$hash])) { + foreach ($this->relocations[$hash]['deltas'] as $delta) { + if ($delta['start'] <= $newPosition + && ($delta['stop'] > $newPosition || $delta['stop'] < 0)) { + $applyDelta += $delta['delta']; + } + } + } + $newPosition += $applyDelta; + + if ($relocation) { + // Add relocation + call_user_func_array(array($this, 'addRelocation'), $relocation); + } + + // Set new position + $this->setFieldValue($ea, $object, $config['position'], $oldPosition, $newPosition); + } + + /** + * Computes node positions and updates the sort field in memory and in the db + * + * @param SortableAdapter $ea + * @param array $config + * @param ClassMetadata $meta + * @param object $object + */ + protected function processDeletion(SortableAdapter $ea, array $config, $meta, $object) + { + $position = $meta->getReflectionProperty($config['position'])->getValue($object); + + // Get groups + $groups = $this->getGroups($meta, $config, $object); + + // Get hash + $hash = $this->getHash($groups, $config); + + // Get max position + if (!isset($this->maxPositions[$hash])) { + $this->maxPositions[$hash] = $this->getMaxPosition($ea, $meta, $config, $object); + } + + // Add relocation + $this->addRelocation($hash, $config['useObjectClass'], $groups, $position, -1, -1); + } + + /** + * Persists relocations to database. + * @param SortableAdapter $ea + */ + protected function persistRelocations(SortableAdapter $ea) + { + if (!$this->persistenceNeeded) { + return; + } + + $em = $ea->getObjectManager(); + foreach ($this->relocations as $hash => $relocation) { + $config = $this->getConfiguration($em, $relocation['name']); + foreach ($relocation['deltas'] as $delta) { + if ($delta['start'] > $this->maxPositions[$hash] || $delta['delta'] == 0) { + continue; + } + $ea->updatePositions($relocation, $delta, $config); + } + } + + $this->persistenceNeeded = false; + } + + /** + * Sync objects in memory + */ + public function postFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $em = $ea->getObjectManager(); + + $updatedObjects = []; + + foreach ($this->relocations as $hash => $relocation) { + $config = $this->getConfiguration($em, $relocation['name']); + foreach ($relocation['deltas'] as $delta) { + if ($delta['start'] > $this->maxPositions[$hash] || $delta['delta'] == 0) { + continue; + } + + $meta = $em->getClassMetadata($relocation['name']); + + // now walk through the unit of work in memory objects and sync those + $uow = $em->getUnitOfWork(); + foreach ($uow->getIdentityMap() as $className => $objects) { + // for inheritance mapped classes, only root is always in the identity map + if ($className !== $ea->getRootObjectClass($meta) || !$this->getConfiguration($em, $className)) { + continue; + } + foreach ($objects as $object) { + if ($object instanceof Proxy && !$object->__isInitialized__) { + continue; + } + + $changeSet = $ea->getObjectChangeSet($uow, $object); + + // if the entity's position is already changed, stop now + if (array_key_exists($config['position'], $changeSet)) { + continue; + } + + // if the entity's group has changed, we stop now + $groups = $this->getGroups($meta, $config, $object); + foreach (array_keys($groups) as $group) { + if (array_key_exists($group, $changeSet)) { + continue 2; + } + } + + $oid = spl_object_hash($object); + $pos = $meta->getReflectionProperty($config['position'])->getValue($object); + $matches = $pos >= $delta['start']; + $matches = $matches && ($delta['stop'] <= 0 || $pos < $delta['stop']); + $value = reset($relocation['groups']); + while ($matches && ($group = key($relocation['groups']))) { + $gr = $meta->getReflectionProperty($group)->getValue($object); + if (null === $value) { + $matches = $gr === null; + } elseif (is_object($gr) && is_object($value) && $gr !== $value) { + // Special case for equal objects but different instances. + // If the object implements Comparable interface we can use its compareTo method + // Otherwise we fallback to normal object comparison + if ($gr instanceof Comparable) { + $matches = $gr->compareTo($value); + } else { + $matches = $gr == $value; + } + } else { + $matches = $gr === $value; + } + $value = next($relocation['groups']); + } + if ($matches) { + // We cannot use `$this->setFieldValue()` here, because it will create a change set, that will + // prevent from other relocations being executed on this object. + // We just update the object value and will create the change set later. + if (!isset($updatedObjects[$oid])) { + $updatedObjects[$oid] = array( + 'object' => $object, + 'field' => $config['position'], + 'oldValue' => $pos, + ); + } + $updatedObjects[$oid]['newValue'] = $pos + $delta['delta']; + + $meta->getReflectionProperty($config['position'])->setValue($object, $updatedObjects[$oid]['newValue']); + } + } + } + } + + foreach ($updatedObjects as $updateData) { + $this->setFieldValue($ea, $updateData['object'], $updateData['field'], $updateData['oldValue'], $updateData['newValue']); + } + + // Clear relocations + unset($this->relocations[$hash]); + unset($this->maxPositions[$hash]); // unset only if relocations has been processed + } + } + + protected function getHash($groups, array $config) + { + $data = $config['useObjectClass']; + foreach ($groups as $group => $val) { + if ($val instanceof \DateTime) { + $val = $val->format('c'); + } elseif (is_object($val)) { + $val = spl_object_hash($val); + } + $data .= $group.$val; + } + + return md5($data); + } + + protected function getMaxPosition(SortableAdapter $ea, $meta, $config, $object, array $groups = array()) + { + $em = $ea->getObjectManager(); + $uow = $em->getUnitOfWork(); + $maxPos = null; + + // Get groups + if (!sizeof($groups)) { + $groups = $this->getGroups($meta, $config, $object); + } + + // Get hash + $hash = $this->getHash($groups, $config); + + // Check for cached max position + if (isset($this->maxPositions[$hash])) { + return $this->maxPositions[$hash]; + } + + // Check for groups that are associations. If the value is an object and is + // scheduled for insert, it has no identifier yet and is obviously new + // see issue #226 + foreach ($groups as $val) { + if (is_object($val) && ($uow->isScheduledForInsert($val) || !$em->getMetadataFactory()->isTransient(ClassUtils::getClass($val)) && $uow::STATE_MANAGED !== $ea->getObjectState($uow, $val))) { + return -1; + } + } + + $maxPos = $ea->getMaxPosition($config, $meta, $groups); + if (is_null($maxPos)) { + $maxPos = -1; + } + + return intval($maxPos); + } + + /** + * Add a relocation rule + * + * @param string $hash The hash of the sorting group + * @param string $class The object class + * @param array $groups The sorting groups + * @param int $start Inclusive index to start relocation from + * @param int $stop Exclusive index to stop relocation at + * @param int $delta The delta to add to relocated nodes + * @param array $exclude Objects to be excluded from relocation + */ + protected function addRelocation($hash, $class, $groups, $start, $stop, $delta, array $exclude = array()) + { + if (!array_key_exists($hash, $this->relocations)) { + $this->relocations[$hash] = array('name' => $class, 'groups' => $groups, 'deltas' => array()); + } + + try { + $newDelta = array('start' => $start, 'stop' => $stop, 'delta' => $delta, 'exclude' => $exclude); + array_walk($this->relocations[$hash]['deltas'], function (&$val, $idx, $needle) { + if ($val['start'] == $needle['start'] && $val['stop'] == $needle['stop']) { + $val['delta'] += $needle['delta']; + $val['exclude'] = array_merge($val['exclude'], $needle['exclude']); + throw new \Exception("Found delta. No need to add it again."); + // For every deletion relocation add newly created object to the list of excludes + // otherwise position update queries will run for created objects as well. + } elseif (-1 == $val['delta'] && 1 == $needle['delta']) { + $val['exclude'] = array_merge($val['exclude'], $needle['exclude']); + } + }, $newDelta); + $this->relocations[$hash]['deltas'][] = $newDelta; + } catch (\Exception $e) { + } + } + + /** + * + * @param array $config + * @param ClassMetadata $meta + * @param object $object + * + * @return array + */ + protected function getGroups($meta, $config, $object) + { + $groups = array(); + if (isset($config['groups'])) { + foreach ($config['groups'] as $group) { + $groups[$group] = $meta->getReflectionProperty($group)->getValue($object); + } + } + + return $groups; + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..bfd2a46 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Annotation.php @@ -0,0 +1,87 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation field is timestampable + */ + const TIMESTAMPABLE = 'Gedmo\\Mapping\\Annotation\\Timestampable'; + + /** + * List of types which are valid for timestamp + * + * @var array + */ + protected $validTypes = array( + 'date', + 'date_immutable', + 'time', + 'time_immutable', + 'datetime', + 'datetime_immutable', + 'datetimetz', + 'datetimetz_immutable', + 'timestamp', + 'zenddate', + 'vardatetime', + 'integer', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + if ($timestampable = $this->reader->getPropertyAnnotation($property, self::TIMESTAMPABLE)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find timestampable [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}"); + } + if (!in_array($timestampable->on, array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + if ($timestampable->on == 'change') { + if (!isset($timestampable->field)) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + if (is_array($timestampable->field) && isset($timestampable->value)) { + throw new InvalidMappingException("Timestampable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $timestampable->field, + 'value' => $timestampable->value, + ); + } + // properties are unique and mapper checks that, no risk here + $config[$timestampable->on][] = $field; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..a67c6c2 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Xml.php @@ -0,0 +1,106 @@ + + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * List of types which are valid for timestamp + * + * @var array + */ + private $validTypes = array( + 'date', + 'date_immutable', + 'time', + 'time_immutable', + 'datetime', + 'datetime_immutable', + 'datetimetz', + 'datetimetz_immutable', + 'timestamp', + 'zenddate', + 'vardatetime', + 'integer', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $mapping + */ + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping->field)) { + /** + * @var \SimpleXmlElement $fieldMapping + */ + foreach ($mapping->field as $fieldMapping) { + $fieldMappingDoctrine = $fieldMapping; + $fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($fieldMapping->timestampable)) { + /** + * @var \SimpleXmlElement $data + */ + $data = $fieldMapping->timestampable; + + $field = $this->_getAttribute($fieldMappingDoctrine, 'name'); + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}"); + } + if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($this->_getAttribute($data, 'on') == 'change') { + if (!$this->_isAttributeSet($data, 'field')) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $this->_getAttribute($data, 'field'); + $valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("Timestampable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$this->_getAttribute($data, 'on')][] = $field; + } + } + } + } + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..558b996 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Driver/Yaml.php @@ -0,0 +1,107 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * List of types which are valid for timestamp + * + * @var array + */ + private $validTypes = array( + 'date', + 'date_immutable', + 'time', + 'time_immutable', + 'datetime', + 'datetime_immutable', + 'datetimetz', + 'datetimetz_immutable', + 'timestamp', + 'zenddate', + 'vardatetime', + 'integer', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo']['timestampable'])) { + $mappingProperty = $fieldMapping['gedmo']['timestampable']; + if (!$this->isValidField($meta, $field)) { + throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'date', 'datetime' or 'time' in class - {$meta->name}"); + } + if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) { + throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}"); + } + + if ($mappingProperty['on'] == 'change') { + if (!isset($mappingProperty['field'])) { + throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}"); + } + $trackedFieldAttribute = $mappingProperty['field']; + $valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null; + if (is_array($trackedFieldAttribute) && null !== $valueAttribute) { + throw new InvalidMappingException("Timestampable extension does not support multiple value changeset detection yet."); + } + $field = array( + 'field' => $field, + 'trackedField' => $trackedFieldAttribute, + 'value' => $valueAttribute, + ); + } + $config[$mappingProperty['on']][] = $field; + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + protected function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..d23a432 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,36 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements TimestampableAdapter +{ + /** + * {@inheritDoc} + */ + public function getDateValue($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + if (isset($mapping['type']) && $mapping['type'] === 'timestamp') { + return time(); + } + if (isset($mapping['type']) && $mapping['type'] == 'zenddate') { + return new \Zend_Date(); + } + if (isset($mapping['type']) && in_array($mapping['type'], array('date_immutable', 'time_immutable', 'datetime_immutable', 'datetimetz_immutable'), true)) { + return new \DateTimeImmutable(); + } + + return \DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', '')) + ->setTimeZone(new \DateTimeZone(date_default_timezone_get())); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..f833390 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,36 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements TimestampableAdapter +{ + /** + * {@inheritDoc} + */ + public function getDateValue($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + if (isset($mapping['type']) && $mapping['type'] === 'integer') { + return time(); + } + if (isset($mapping['type']) && $mapping['type'] == 'zenddate') { + return new \Zend_Date(); + } + if (isset($mapping['type']) && in_array($mapping['type'], array('date_immutable', 'time_immutable', 'datetime_immutable', 'datetimetz_immutable'), true)) { + return new \DateTimeImmutable(); + } + + return \DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', '')) + ->setTimeZone(new \DateTimeZone(date_default_timezone_get())); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/TimestampableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/TimestampableAdapter.php new file mode 100644 index 0000000..48005cd --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Mapping/Event/TimestampableAdapter.php @@ -0,0 +1,25 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface TimestampableAdapter extends AdapterInterface +{ + /** + * Get the date value + * + * @param object $meta + * @param string $field + * + * @return mixed + */ + public function getDateValue($meta, $field); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Timestampable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Timestampable.php new file mode 100644 index 0000000..6c3ff15 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Timestampable.php @@ -0,0 +1,50 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Timestampable +{ + // timestampable expects annotations on properties + + /** + * @gedmo:Timestampable(on="create") + * dates which should be updated on insert only + */ + + /** + * @gedmo:Timestampable(on="update") + * dates which should be updated on update and insert + */ + + /** + * @gedmo:Timestampable(on="change", field="field", value="value") + * dates which should be updated on changed "property" + * value and become equal to given "value" + */ + + /** + * @gedmo:Timestampable(on="change", field="field") + * dates which should be updated on changed "property" + */ + + /** + * @gedmo:Timestampable(on="change", fields={"field1", "field2"}) + * dates which should be updated if at least one of the given fields changed + */ + + /** + * example + * + * @gedmo:Timestampable(on="create") + * @Column(type="date") + * $created + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/TimestampableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/TimestampableListener.php new file mode 100644 index 0000000..59d8469 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/TimestampableListener.php @@ -0,0 +1,36 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TimestampableListener extends AbstractTrackingListener +{ + /** + * @param ClassMetadata $meta + * @param string $field + * @param TimestampableAdapter $eventAdapter + * @return mixed + */ + protected function getFieldValue($meta, $field, $eventAdapter) + { + return $eventAdapter->getDateValue($meta, $field); + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/Timestampable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/Timestampable.php new file mode 100644 index 0000000..f15afbc --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/Timestampable.php @@ -0,0 +1,68 @@ += 5.4 + * + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait Timestampable +{ + /** + * @var \DateTime + */ + protected $createdAt; + + /** + * @var \DateTime + */ + protected $updatedAt; + + /** + * Sets createdAt. + * + * @param \DateTime $createdAt + * @return $this + */ + public function setCreatedAt(\DateTime $createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * Returns createdAt. + * + * @return \DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * Sets updatedAt. + * + * @param \DateTime $updatedAt + * @return $this + */ + public function setUpdatedAt(\DateTime $updatedAt) + { + $this->updatedAt = $updatedAt; + + return $this; + } + + /** + * Returns updatedAt. + * + * @return \DateTime + */ + public function getUpdatedAt() + { + return $this->updatedAt; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableDocument.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableDocument.php new file mode 100644 index 0000000..1964fa7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableDocument.php @@ -0,0 +1,75 @@ += 5.4 + * + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait TimestampableDocument +{ + /** + * @var \DateTime + * @Gedmo\Timestampable(on="create") + * @ODM\Field(type="date") + */ + protected $createdAt; + + /** + * @var \DateTime + * @Gedmo\Timestampable(on="update") + * @ODM\Field(type="date") + */ + protected $updatedAt; + + /** + * Sets createdAt. + * + * @param \DateTime $createdAt + * @return $this + */ + public function setCreatedAt(\DateTime $createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * Returns createdAt. + * + * @return \DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * Sets updatedAt. + * + * @param \DateTime $updatedAt + * @return $this + */ + public function setUpdatedAt(\DateTime $updatedAt) + { + $this->updatedAt = $updatedAt; + + return $this; + } + + /** + * Returns updatedAt. + * + * @return \Datetime + */ + public function getUpdatedAt() + { + return $this->updatedAt; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableEntity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableEntity.php new file mode 100644 index 0000000..9f638c9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Timestampable/Traits/TimestampableEntity.php @@ -0,0 +1,75 @@ += 5.4 + * + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait TimestampableEntity +{ + /** + * @var \DateTime + * @Gedmo\Timestampable(on="create") + * @ORM\Column(type="datetime") + */ + protected $createdAt; + + /** + * @var \DateTime + * @Gedmo\Timestampable(on="update") + * @ORM\Column(type="datetime") + */ + protected $updatedAt; + + /** + * Sets createdAt. + * + * @param \DateTime $createdAt + * @return $this + */ + public function setCreatedAt(\DateTime $createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * Returns createdAt. + * + * @return \DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * Sets updatedAt. + * + * @param \DateTime $updatedAt + * @return $this + */ + public function setUpdatedAt(\DateTime $updatedAt) + { + $this->updatedAt = $updatedAt; + + return $this; + } + + /** + * Returns updatedAt. + * + * @return \DateTime + */ + public function getUpdatedAt() + { + return $this->updatedAt; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Logging/DBAL/QueryAnalyzer.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Logging/DBAL/QueryAnalyzer.php new file mode 100644 index 0000000..d5c7dde --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Logging/DBAL/QueryAnalyzer.php @@ -0,0 +1,248 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class QueryAnalyzer implements SQLLogger +{ + /** + * Used database platform + * + * @var AbstractPlatform + */ + protected $platform; + + /** + * Start time of currently executed query + * + * @var integer + */ + private $queryStartTime = null; + + /** + * Total execution time of all queries + * + * @var integer + */ + private $totalExecutionTime = 0; + + /** + * List of queries executed + * + * @var array + */ + private $queries = array(); + + /** + * Query execution times indexed + * in same order as queries + * + * @var array + */ + private $queryExecutionTimes = array(); + + /** + * Initialize log listener with database + * platform, which is needed for parameter + * conversion + * + * @param AbstractPlatform $platform + */ + public function __construct(AbstractPlatform $platform) + { + $this->platform = $platform; + } + + /** + * {@inheritdoc} + */ + public function startQuery($sql, array $params = null, array $types = null) + { + $this->queryStartTime = microtime(true); + $this->queries[] = $this->generateSql($sql, $params, $types); + } + + /** + * {@inheritdoc} + */ + public function stopQuery() + { + $ms = round(microtime(true) - $this->queryStartTime, 4) * 1000; + $this->queryExecutionTimes[] = $ms; + $this->totalExecutionTime += $ms; + } + + /** + * Clean all collected data + * + * @return QueryAnalyzer + */ + public function cleanUp() + { + $this->queries = array(); + $this->queryExecutionTimes = array(); + $this->totalExecutionTime = 0; + + return $this; + } + + /** + * Dump the statistics of executed queries + * + * @param boolean $dumpOnlySql + * + * @return string + */ + public function getOutput($dumpOnlySql = false) + { + $output = ''; + if (!$dumpOnlySql) { + $output .= 'Platform: '.$this->platform->getName().PHP_EOL; + $output .= 'Executed queries: '.count($this->queries).', total time: '.$this->totalExecutionTime.' ms'.PHP_EOL; + } + foreach ($this->queries as $index => $sql) { + if (!$dumpOnlySql) { + $output .= 'Query('.($index+1).') - '.$this->queryExecutionTimes[$index].' ms'.PHP_EOL; + } + $output .= $sql.';'.PHP_EOL; + } + $output .= PHP_EOL; + + return $output; + } + + /** + * Index of the slowest query executed + * + * @return integer + */ + public function getSlowestQueryIndex() + { + $index = 0; + $slowest = 0; + foreach ($this->queryExecutionTimes as $i => $time) { + if ($time > $slowest) { + $slowest = $time; + $index = $i; + } + } + + return $index; + } + + /** + * Get total execution time of queries + * + * @return integer + */ + public function getTotalExecutionTime() + { + return $this->totalExecutionTime; + } + + /** + * Get all queries + * + * @return array + */ + public function getExecutedQueries() + { + return $this->queries; + } + + /** + * Get number of executed queries + * + * @return integer + */ + public function getNumExecutedQueries() + { + return count($this->queries); + } + + /** + * Get all query execution times + * + * @return array + */ + public function getExecutionTimes() + { + return $this->queryExecutionTimes; + } + + /** + * Create the SQL with mapped parameters + * + * @param string $sql + * @param null|array $params + * @param null|array $types + * + * @return string + */ + private function generateSql($sql, $params, $types) + { + if (null === $params || !count($params)) { + return $sql; + } + $converted = $this->getConvertedParams($params, $types); + if (is_int(key($params))) { + $index = key($converted); + $sql = preg_replace_callback('@\?@sm', function ($match) use (&$index, $converted) { + return $converted[$index++]; + }, $sql); + } else { + foreach ($converted as $key => $value) { + $sql = str_replace(':'.$key, $value, $sql); + } + } + + return $sql; + } + + /** + * Get the converted parameter list + * + * @param array $params + * @param array $types + * + * @return array + */ + private function getConvertedParams($params, $types) + { + $result = array(); + foreach ($params as $position => $value) { + if (isset($types[$position])) { + $type = $types[$position]; + if (is_string($type)) { + $type = Type::getType($type); + } + if ($type instanceof Type) { + $value = $type->convertToDatabaseValue($value, $this->platform); + } + } else { + // Remove `$value instanceof \DateTime` check when PHP version is bumped to >=5.5 + if (is_object($value) && ($value instanceof \DateTime || $value instanceof \DateTimeInterface)) { + $value = $value->format($this->platform->getDateTimeFormatString()); + } elseif (!is_null($value)) { + $type = Type::getType(gettype($value)); + $value = $type->convertToDatabaseValue($value, $this->platform); + } + } + if (is_string($value)) { + $value = "'{$value}'"; + } elseif (is_null($value)) { + $value = 'NULL'; + } + $result[$position] = $value; + } + + return $result; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/AbstractWrapper.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/AbstractWrapper.php new file mode 100644 index 0000000..09bc4b9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/AbstractWrapper.php @@ -0,0 +1,100 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class AbstractWrapper implements WrapperInterface +{ + /** + * Object metadata + * + * @var object + */ + protected $meta; + + /** + * Wrapped object + * + * @var object + */ + protected $object; + + /** + * Object manager instance + * + * @var \Doctrine\Common\Persistence\ObjectManager + */ + protected $om; + + /** + * List of wrapped object references + * + * @var array + */ + private static $wrappedObjectReferences; + + /** + * Wrap object factory method + * + * @param object $object + * @param ObjectManager $om + * + * @throws \Gedmo\Exception\UnsupportedObjectManagerException + * + * @return \Gedmo\Tool\WrapperInterface + */ + public static function wrap($object, ObjectManager $om) + { + if ($om instanceof EntityManagerInterface) { + return new EntityWrapper($object, $om); + } elseif ($om instanceof DocumentManager) { + return new MongoDocumentWrapper($object, $om); + } + throw new UnsupportedObjectManagerException('Given object manager is not managed by wrapper'); + } + + public static function clear() + { + self::$wrappedObjectReferences = array(); + } + + /** + * {@inheritDoc} + */ + public function getObject() + { + return $this->object; + } + + /** + * {@inheritDoc} + */ + public function getMetadata() + { + return $this->meta; + } + + /** + * {@inheritDoc} + */ + public function populate(array $data) + { + foreach ($data as $field => $value) { + $this->setPropertyValue($field, $value); + } + + return $this; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/EntityWrapper.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/EntityWrapper.php new file mode 100644 index 0000000..ab1c2fe --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/EntityWrapper.php @@ -0,0 +1,138 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class EntityWrapper extends AbstractWrapper +{ + /** + * Entity identifier + * + * @var array + */ + private $identifier; + + /** + * True if entity or proxy is loaded + * + * @var boolean + */ + private $initialized = false; + + /** + * Wrap entity + * + * @param object $entity + * @param \Doctrine\ORM\EntityManagerInterface $em + */ + public function __construct($entity, EntityManagerInterface $em) + { + $this->om = $em; + $this->object = $entity; + $this->meta = $em->getClassMetadata(get_class($this->object)); + } + + /** + * {@inheritDoc} + */ + public function getPropertyValue($property) + { + $this->initialize(); + + return $this->meta->getReflectionProperty($property)->getValue($this->object); + } + + /** + * {@inheritDoc} + */ + public function setPropertyValue($property, $value) + { + $this->initialize(); + $this->meta->getReflectionProperty($property)->setValue($this->object, $value); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function hasValidIdentifier() + { + return (null !== $this->getIdentifier()); + } + + /** + * {@inheritDoc} + */ + public function getRootObjectName() + { + return $this->meta->rootEntityName; + } + + /** + * {@inheritDoc} + */ + public function getIdentifier($single = true) + { + if (null === $this->identifier) { + if ($this->object instanceof Proxy) { + $uow = $this->om->getUnitOfWork(); + if ($uow->isInIdentityMap($this->object)) { + $this->identifier = $uow->getEntityIdentifier($this->object); + } else { + $this->initialize(); + } + } + if (null === $this->identifier) { + $this->identifier = array(); + $incomplete = false; + foreach ($this->meta->identifier as $name) { + $this->identifier[$name] = $this->getPropertyValue($name); + if (null === $this->identifier[$name]) { + $incomplete = true; + } + } + if ($incomplete) { + $this->identifier = null; + } + } + } + if ($single && is_array($this->identifier)) { + return reset($this->identifier); + } + + return $this->identifier; + } + + /** + * Initialize the entity if it is proxy + * required when is detached or not initialized + */ + protected function initialize() + { + if (!$this->initialized) { + if ($this->object instanceof Proxy) { + if (!$this->object->__isInitialized__) { + $this->object->__load(); + } + } + } + } + + /** + * {@inheritDoc} + */ + public function isEmbeddedAssociation($field) + { + return false; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/MongoDocumentWrapper.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/MongoDocumentWrapper.php new file mode 100644 index 0000000..c0a9f63 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/Wrapper/MongoDocumentWrapper.php @@ -0,0 +1,137 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class MongoDocumentWrapper extends AbstractWrapper +{ + /** + * Document identifier + * + * @var mixed + */ + private $identifier; + + /** + * True if document or proxy is loaded + * + * @var boolean + */ + private $initialized = false; + + /** + * Wrap document + * + * @param object $document + * @param \Doctrine\ODM\MongoDB\DocumentManager $dm + */ + public function __construct($document, DocumentManager $dm) + { + $this->om = $dm; + $this->object = $document; + $this->meta = $dm->getClassMetadata(get_class($this->object)); + } + + /** + * {@inheritDoc} + */ + public function getPropertyValue($property) + { + $this->initialize(); + + return $this->meta->getReflectionProperty($property)->getValue($this->object); + } + + /** + * {@inheritDoc} + */ + public function getRootObjectName() + { + return $this->meta->rootDocumentName; + } + + /** + * {@inheritDoc} + */ + public function setPropertyValue($property, $value) + { + $this->initialize(); + $this->meta->getReflectionProperty($property)->setValue($this->object, $value); + + return $this; + } + + /** + * {@inheritDoc} + */ + public function hasValidIdentifier() + { + return (bool) $this->getIdentifier(); + } + + /** + * {@inheritDoc} + */ + public function getIdentifier($single = true) + { + if (!$this->identifier) { + if ($this->object instanceof Proxy) { + $uow = $this->om->getUnitOfWork(); + if ($uow->isInIdentityMap($this->object)) { + $this->identifier = (string) $uow->getDocumentIdentifier($this->object); + } else { + $this->initialize(); + } + } + if (!$this->identifier) { + $this->identifier = (string) $this->getPropertyValue($this->meta->identifier); + } + } + + return $this->identifier; + } + + /** + * Initialize the document if it is proxy + * required when is detached or not initialized + */ + protected function initialize() + { + if (!$this->initialized) { + if ($this->object instanceof Proxy) { + $uow = $this->om->getUnitOfWork(); + if (!$this->object->__isInitialized__) { + $persister = $uow->getDocumentPersister($this->meta->name); + $identifier = null; + if ($uow->isInIdentityMap($this->object)) { + $identifier = $this->getIdentifier(); + } else { + // this may not happen but in case + $reflProperty = new \ReflectionProperty($this->object, 'identifier'); + $reflProperty->setAccessible(true); + $identifier = $reflProperty->getValue($this->object); + } + $this->object->__isInitialized__ = true; + $persister->load($identifier, $this->object); + } + } + } + } + + /** + * {@inheritDoc} + */ + public function isEmbeddedAssociation($field) + { + return $this->getMetadata()->isSingleValuedEmbed($field); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/WrapperInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/WrapperInterface.php new file mode 100644 index 0000000..adde457 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tool/WrapperInterface.php @@ -0,0 +1,87 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface WrapperInterface +{ + /** + * Get currently wrapped object + * etc.: entity, document + * + * @return object + */ + public function getObject(); + + /** + * Extract property value from object + * + * @param string $property + * + * @return mixed + */ + public function getPropertyValue($property); + + /** + * Set the property + * + * @param string $property + * @param mixed $value + * + * @return \Gedmo\Tool\WrapperInterface + */ + public function setPropertyValue($property, $value); + + /** + * Populates the object with given property values + * + * @param array $data + * + * @return static + */ + public function populate(array $data); + + /** + * Checks if identifier is valid + * + * @return boolean + */ + public function hasValidIdentifier(); + + /** + * Get metadata + * + * @return object + */ + public function getMetadata(); + + /** + * Get the object identifier, single or composite + * + * @param boolean $single + * + * @return array|mixed + */ + public function getIdentifier($single = true); + + /** + * Get root object class name + * + * @return string + */ + public function getRootObjectName(); + + /** + * Chechks if association is embedded + * + * @param string $field + * + * @return bool + */ + public function isEmbeddedAssociation($field); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractPersonalTranslation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractPersonalTranslation.php new file mode 100644 index 0000000..df4a6bb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractPersonalTranslation.php @@ -0,0 +1,153 @@ +id; + } + + /** + * Set locale + * + * @param string $locale + * + * @return static + */ + public function setLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + /** + * Get locale + * + * @return string + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Set field + * + * @param string $field + * + * @return static + */ + public function setField($field) + { + $this->field = $field; + + return $this; + } + + /** + * Get field + * + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * Set object related + * + * @param object $object + * + * @return static + */ + public function setObject($object) + { + $this->object = $object; + + return $this; + } + + /** + * Get object related + * + * @return string + */ + public function getObject() + { + return $this->object; + } + + /** + * Set content + * + * @param string $content + * + * @return static + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + return $this->content; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractTranslation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractTranslation.php new file mode 100644 index 0000000..f8d3bf3 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/MappedSuperclass/AbstractTranslation.php @@ -0,0 +1,185 @@ +id; + } + + /** + * Set locale + * + * @param string $locale + * + * @return static + */ + public function setLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + /** + * Get locale + * + * @return string + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Set field + * + * @param string $field + * + * @return static + */ + public function setField($field) + { + $this->field = $field; + + return $this; + } + + /** + * Get field + * + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * Set object class + * + * @param string $objectClass + * + * @return static + */ + public function setObjectClass($objectClass) + { + $this->objectClass = $objectClass; + + return $this; + } + + /** + * Get objectClass + * + * @return string + */ + public function getObjectClass() + { + return $this->objectClass; + } + + /** + * Set foreignKey + * + * @param string $foreignKey + * + * @return static + */ + public function setForeignKey($foreignKey) + { + $this->foreignKey = $foreignKey; + + return $this; + } + + /** + * Get foreignKey + * + * @return string + */ + public function getForeignKey() + { + return $this->foreignKey; + } + + /** + * Set content + * + * @param string $content + * + * @return static + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + return $this->content; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Repository/TranslationRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Repository/TranslationRepository.php new file mode 100644 index 0000000..8f83004 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Repository/TranslationRepository.php @@ -0,0 +1,254 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TranslationRepository extends DocumentRepository +{ + /** + * Current TranslatableListener instance used + * in EntityManager + * + * @var TranslatableListener + */ + private $listener; + + /** + * {@inheritdoc} + */ + public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $class) + { + if ($class->getReflectionClass()->isSubclassOf('Gedmo\Translatable\Document\MappedSuperclass\AbstractPersonalTranslation')) { + throw new \Gedmo\Exception\UnexpectedValueException('This repository is useless for personal translations'); + } + parent::__construct($dm, $uow, $class); + } + + /** + * Makes additional translation of $document $field into $locale + * using $value + * + * @param object $document + * @param string $field + * @param string $locale + * @param mixed $value + * + * @return static + */ + public function translate($document, $field, $locale, $value) + { + $meta = $this->dm->getClassMetadata(get_class($document)); + $listener = $this->getTranslatableListener(); + $config = $listener->getConfiguration($this->dm, $meta->name); + if (!isset($config['fields']) || !in_array($field, $config['fields'])) { + throw new \Gedmo\Exception\InvalidArgumentException("Document: {$meta->name} does not translate field - {$field}"); + } + $modRecordValue = (!$listener->getPersistDefaultLocaleTranslation() && $locale === $listener->getDefaultLocale()) + || $listener->getTranslatableLocale($document, $meta, $this->getDocumentManager()) === $locale + ; + if ($modRecordValue) { + $meta->getReflectionProperty($field)->setValue($document, $value); + $this->dm->persist($document); + } else { + if (isset($config['translationClass'])) { + $class = $config['translationClass']; + } else { + $ea = new TranslatableAdapterODM(); + $class = $listener->getTranslationClass($ea, $config['useObjectClass']); + } + $foreignKey = $meta->getReflectionProperty($meta->identifier)->getValue($document); + $objectClass = $config['useObjectClass']; + $transMeta = $this->dm->getClassMetadata($class); + $trans = $this->findOneBy(compact('locale', 'field', 'objectClass', 'foreignKey')); + if (!$trans) { + $trans = $transMeta->newInstance(); + $transMeta->getReflectionProperty('foreignKey')->setValue($trans, $foreignKey); + $transMeta->getReflectionProperty('objectClass')->setValue($trans, $objectClass); + $transMeta->getReflectionProperty('field')->setValue($trans, $field); + $transMeta->getReflectionProperty('locale')->setValue($trans, $locale); + } + $mapping = $meta->getFieldMapping($field); + $type = $this->getType($mapping['type']); + $transformed = $type->convertToDatabaseValue($value); + $transMeta->getReflectionProperty('content')->setValue($trans, $transformed); + if ($this->dm->getUnitOfWork()->isInIdentityMap($document)) { + $this->dm->persist($trans); + } else { + $oid = spl_object_hash($document); + $listener->addPendingTranslationInsert($oid, $trans); + } + } + + return $this; + } + + /** + * Loads all translations with all translatable + * fields from the given entity + * + * @param object $document + * + * @return array list of translations in locale groups + */ + public function findTranslations($document) + { + $result = array(); + $wrapped = new MongoDocumentWrapper($document, $this->dm); + if ($wrapped->hasValidIdentifier()) { + $documentId = $wrapped->getIdentifier(); + + $translationMeta = $this->getClassMetadata(); // table inheritance support + + $config = $this + ->getTranslatableListener() + ->getConfiguration($this->dm, $wrapped->getMetadata()->name); + + if (!$config) { + return $result; + } + + $documentClass = $config['useObjectClass']; + + $translationClass = isset($config['translationClass']) ? + $config['translationClass'] : + $translationMeta->rootDocumentName; + + $qb = $this->dm->createQueryBuilder($translationClass); + $q = $qb->field('foreignKey')->equals($documentId) + ->field('objectClass')->equals($documentClass) + ->field('content')->exists(true)->notEqual(null) + ->sort('locale', 'asc') + ->getQuery(); + + $q->setHydrate(false); + $data = $q->execute(); + if ($data instanceof Cursor) { + $data = $data->toArray(); + } + if ($data && is_array($data) && count($data)) { + foreach ($data as $row) { + $result[$row['locale']][$row['field']] = $row['content']; + } + } + } + + return $result; + } + + /** + * Find the object $class by the translated field. + * Result is the first occurrence of translated field. + * Query can be slow, since there are no indexes on such + * columns + * + * @param string $field + * @param string $value + * @param string $class + * + * @return object - instance of $class or null if not found + */ + public function findObjectByTranslatedField($field, $value, $class) + { + $document = null; + $meta = $this->dm->getClassMetadata($class); + if ($meta->hasField($field)) { + $qb = $this->createQueryBuilder(); + $q = $qb->field('field')->equals($field) + ->field('objectClass')->equals($meta->rootDocumentName) + ->field('content')->equals($value) + ->getQuery(); + + $q->setHydrate(false); + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = $result->toArray(); + } + $id = count($result) ? $result[0]['foreignKey'] : null; + if ($id) { + $document = $this->dm->find($class, $id); + } + } + + return $document; + } + + /** + * Loads all translations with all translatable + * fields by a given document primary key + * + * @param mixed $id - primary key value of document + * + * @return array + */ + public function findTranslationsByObjectId($id) + { + $result = array(); + if ($id) { + $qb = $this->createQueryBuilder(); + $q = $qb->field('foreignKey')->equals($id) + ->field('content')->exists(true)->notEqual(null) + ->sort('locale', 'asc') + ->getQuery(); + + $q->setHydrate(false); + $data = $q->execute(); + + if ($data instanceof Cursor) { + $data = $data->toArray(); + } + if ($data && is_array($data) && count($data)) { + foreach ($data as $row) { + $result[$row['locale']][$row['field']] = $row['content']; + } + } + } + + return $result; + } + + /** + * Get the currently used TranslatableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return TranslatableListener + */ + private function getTranslatableListener() + { + if (!$this->listener) { + foreach ($this->dm->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof TranslatableListener) { + return $this->listener = $listener; + } + } + } + + throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found'); + } + + return $this->listener; + } + + private function getType($type) + { + // due to change in ODM beta 9 + return class_exists('Doctrine\ODM\MongoDB\Types\Type') ? \Doctrine\ODM\MongoDB\Types\Type::getType($type) + : \Doctrine\ODM\MongoDB\Mapping\Types\Type::getType($type); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Translation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Translation.php new file mode 100644 index 0000000..a82a444 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document/Translation.php @@ -0,0 +1,30 @@ +id; + } + + /** + * Set locale + * + * @param string $locale + * + * @return static + */ + public function setLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + /** + * Get locale + * + * @return string + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Set field + * + * @param string $field + * + * @return static + */ + public function setField($field) + { + $this->field = $field; + + return $this; + } + + /** + * Get field + * + * @return string $field + */ + public function getField() + { + return $this->field; + } + + /** + * Set object related + * + * @param object $object + * + * @return static + */ + public function setObject($object) + { + $this->object = $object; + + return $this; + } + + /** + * Get related object + * + * @return object + */ + public function getObject() + { + return $this->object; + } + + /** + * Set content + * + * @param string $content + * + * @return static + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + return $this->content; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass/AbstractTranslation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass/AbstractTranslation.php new file mode 100644 index 0000000..598f0cf --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass/AbstractTranslation.php @@ -0,0 +1,187 @@ +id; + } + + /** + * Set locale + * + * @param string $locale + * + * @return static + */ + public function setLocale($locale) + { + $this->locale = $locale; + + return $this; + } + + /** + * Get locale + * + * @return string + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Set field + * + * @param string $field + * + * @return static + */ + public function setField($field) + { + $this->field = $field; + + return $this; + } + + /** + * Get field + * + * @return string + */ + public function getField() + { + return $this->field; + } + + /** + * Set object class + * + * @param string $objectClass + * + * @return static + */ + public function setObjectClass($objectClass) + { + $this->objectClass = $objectClass; + + return $this; + } + + /** + * Get objectClass + * + * @return string + */ + public function getObjectClass() + { + return $this->objectClass; + } + + /** + * Set foreignKey + * + * @param string $foreignKey + * + * @return static + */ + public function setForeignKey($foreignKey) + { + $this->foreignKey = $foreignKey; + + return $this; + } + + /** + * Get foreignKey + * + * @return string + */ + public function getForeignKey() + { + return $this->foreignKey; + } + + /** + * Set content + * + * @param string $content + * + * @return static + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + return $this->content; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php new file mode 100644 index 0000000..16622e0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Repository/TranslationRepository.php @@ -0,0 +1,249 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TranslationRepository extends EntityRepository +{ + /** + * Current TranslatableListener instance used + * in EntityManager + * + * @var TranslatableListener + */ + private $listener; + + /** + * {@inheritdoc} + */ + public function __construct(EntityManagerInterface $em, ClassMetadata $class) + { + if ($class->getReflectionClass()->isSubclassOf('Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation')) { + throw new \Gedmo\Exception\UnexpectedValueException('This repository is useless for personal translations'); + } + parent::__construct($em, $class); + } + + /** + * Makes additional translation of $entity $field into $locale + * using $value + * + * @param object $entity + * @param string $field + * @param string $locale + * @param mixed $value + * + * @throws \Gedmo\Exception\InvalidArgumentException + * + * @return static + */ + public function translate($entity, $field, $locale, $value) + { + $meta = $this->_em->getClassMetadata(get_class($entity)); + $listener = $this->getTranslatableListener(); + $config = $listener->getConfiguration($this->_em, $meta->name); + if (!isset($config['fields']) || !in_array($field, $config['fields'])) { + throw new \Gedmo\Exception\InvalidArgumentException("Entity: {$meta->name} does not translate field - {$field}"); + } + $needsPersist = true; + if ($locale === $listener->getTranslatableLocale($entity, $meta, $this->getEntityManager())) { + $meta->getReflectionProperty($field)->setValue($entity, $value); + $this->_em->persist($entity); + } else { + if (isset($config['translationClass'])) { + $class = $config['translationClass']; + } else { + $ea = new TranslatableAdapterORM(); + $class = $listener->getTranslationClass($ea, $config['useObjectClass']); + } + $foreignKey = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName())->getValue($entity); + $objectClass = $config['useObjectClass']; + $transMeta = $this->_em->getClassMetadata($class); + $trans = $this->findOneBy(compact('locale', 'objectClass', 'field', 'foreignKey')); + if (!$trans) { + $trans = $transMeta->newInstance(); + $transMeta->getReflectionProperty('foreignKey')->setValue($trans, $foreignKey); + $transMeta->getReflectionProperty('objectClass')->setValue($trans, $objectClass); + $transMeta->getReflectionProperty('field')->setValue($trans, $field); + $transMeta->getReflectionProperty('locale')->setValue($trans, $locale); + } + if ($listener->getDefaultLocale() != $listener->getTranslatableLocale($entity, $meta, $this->getEntityManager()) && + $locale === $listener->getDefaultLocale()) { + $listener->setTranslationInDefaultLocale(spl_object_hash($entity), $field, $trans); + $needsPersist = $listener->getPersistDefaultLocaleTranslation(); + } + $type = Type::getType($meta->getTypeOfField($field)); + $transformed = $type->convertToDatabaseValue($value, $this->_em->getConnection()->getDatabasePlatform()); + $transMeta->getReflectionProperty('content')->setValue($trans, $transformed); + if ($needsPersist) { + if ($this->_em->getUnitOfWork()->isInIdentityMap($entity)) { + $this->_em->persist($trans); + } else { + $oid = spl_object_hash($entity); + $listener->addPendingTranslationInsert($oid, $trans); + } + } + } + + return $this; + } + + /** + * Loads all translations with all translatable + * fields from the given entity + * + * @param object $entity Must implement Translatable + * + * @return array list of translations in locale groups + */ + public function findTranslations($entity) + { + $result = array(); + $wrapped = new EntityWrapper($entity, $this->_em); + if ($wrapped->hasValidIdentifier()) { + $entityId = $wrapped->getIdentifier(); + $config = $this + ->getTranslatableListener() + ->getConfiguration($this->_em, $wrapped->getMetadata()->name); + + if (!$config) { + return $result; + } + + $entityClass = $config['useObjectClass']; + $translationMeta = $this->getClassMetadata(); // table inheritance support + + $translationClass = isset($config['translationClass']) ? + $config['translationClass'] : + $translationMeta->rootEntityName; + + $qb = $this->_em->createQueryBuilder(); + $qb->select('trans.content, trans.field, trans.locale') + ->from($translationClass, 'trans') + ->where('trans.foreignKey = :entityId', 'trans.objectClass = :entityClass') + ->orderBy('trans.locale'); + $q = $qb->getQuery(); + $data = $q->execute( + compact('entityId', 'entityClass'), + Query::HYDRATE_ARRAY + ); + + if ($data && is_array($data) && count($data)) { + foreach ($data as $row) { + $result[$row['locale']][$row['field']] = $row['content']; + } + } + } + + return $result; + } + + /** + * Find the entity $class by the translated field. + * Result is the first occurrence of translated field. + * Query can be slow, since there are no indexes on such + * columns + * + * @param string $field + * @param string $value + * @param string $class + * + * @return object - instance of $class or null if not found + */ + public function findObjectByTranslatedField($field, $value, $class) + { + $entity = null; + $meta = $this->_em->getClassMetadata($class); + $translationMeta = $this->getClassMetadata(); // table inheritance support + if ($meta->hasField($field)) { + $dql = "SELECT trans.foreignKey FROM {$translationMeta->rootEntityName} trans"; + $dql .= ' WHERE trans.objectClass = :class'; + $dql .= ' AND trans.field = :field'; + $dql .= ' AND trans.content = :value'; + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('class', 'field', 'value')); + $q->setMaxResults(1); + $result = $q->getArrayResult(); + $id = count($result) ? $result[0]['foreignKey'] : null; + + if ($id) { + $entity = $this->_em->find($class, $id); + } + } + + return $entity; + } + + /** + * Loads all translations with all translatable + * fields by a given entity primary key + * + * @param mixed $id - primary key value of an entity + * + * @return array + */ + public function findTranslationsByObjectId($id) + { + $result = array(); + if ($id) { + $translationMeta = $this->getClassMetadata(); // table inheritance support + $qb = $this->_em->createQueryBuilder(); + $qb->select('trans.content, trans.field, trans.locale') + ->from($translationMeta->rootEntityName, 'trans') + ->where('trans.foreignKey = :entityId') + ->orderBy('trans.locale'); + $q = $qb->getQuery(); + $data = $q->execute( + array('entityId' => $id), + Query::HYDRATE_ARRAY + ); + + if ($data && is_array($data) && count($data)) { + foreach ($data as $row) { + $result[$row['locale']][$row['field']] = $row['content']; + } + } + } + + return $result; + } + + /** + * Get the currently used TranslatableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return TranslatableListener + */ + private function getTranslatableListener() + { + if (!$this->listener) { + foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof TranslatableListener) { + return $this->listener = $listener; + } + } + } + + throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found'); + } + + return $this->listener; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Translation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Translation.php new file mode 100644 index 0000000..09cd696 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/Translation.php @@ -0,0 +1,30 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ObjectHydrator extends BaseObjectHydrator +{ + /** + * State of skipOnLoad for listener between hydrations + * + * @see ObjectHydrator::prepare() + * @see ObjectHydrator::cleanup() + * + * @var bool + */ + private $savedSkipOnLoad; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $listener = $this->getTranslatableListener(); + $this->savedSkipOnLoad = $listener->isSkipOnLoad(); + $listener->setSkipOnLoad(true); + parent::prepare(); + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + parent::cleanup(); + $listener = $this->getTranslatableListener(); + $listener->setSkipOnLoad($this->savedSkipOnLoad !== null ? $this->savedSkipOnLoad : false); + } + + /** + * Get the currently used TranslatableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return TranslatableListener + */ + protected function getTranslatableListener() + { + $translatableListener = null; + foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof TranslatableListener) { + $translatableListener = $listener; + break; + } + } + if ($translatableListener) { + break; + } + } + + if (is_null($translatableListener)) { + throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found'); + } + + return $translatableListener; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php new file mode 100644 index 0000000..2e85c9f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Hydrator/ORM/SimpleObjectHydrator.php @@ -0,0 +1,78 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class SimpleObjectHydrator extends BaseSimpleObjectHydrator +{ + /** + * State of skipOnLoad for listener between hydrations + * + * @see SimpleObjectHydrator::prepare() + * @see SimpleObjectHydrator::cleanup() + * + * @var bool + */ + private $savedSkipOnLoad; + + /** + * {@inheritdoc} + */ + protected function prepare() + { + $listener = $this->getTranslatableListener(); + $this->savedSkipOnLoad = $listener->isSkipOnLoad(); + $listener->setSkipOnLoad(true); + parent::prepare(); + } + + /** + * {@inheritdoc} + */ + protected function cleanup() + { + parent::cleanup(); + $listener = $this->getTranslatableListener(); + $listener->setSkipOnLoad($this->savedSkipOnLoad !== null ? $this->savedSkipOnLoad : false); + } + + /** + * Get the currently used TranslatableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return TranslatableListener + */ + protected function getTranslatableListener() + { + $translatableListener = null; + foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof TranslatableListener) { + $translatableListener = $listener; + break; + } + } + if ($translatableListener) { + break; + } + } + + if (is_null($translatableListener)) { + throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found'); + } + + return $translatableListener; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..5d36fec --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Annotation.php @@ -0,0 +1,114 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to identity translation entity to be used for translation storage + */ + const ENTITY_CLASS = 'Gedmo\\Mapping\\Annotation\\TranslationEntity'; + + /** + * Annotation to identify field as translatable + */ + const TRANSLATABLE = 'Gedmo\\Mapping\\Annotation\\Translatable'; + + /** + * Annotation to identify field which can store used locale or language + * alias is LANGUAGE + */ + const LOCALE = 'Gedmo\\Mapping\\Annotation\\Locale'; + + /** + * Annotation to identify field which can store used locale or language + * alias is LOCALE + */ + const LANGUAGE = 'Gedmo\\Mapping\\Annotation\\Language'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + // class annotations + if ($annot = $this->reader->getClassAnnotation($class, self::ENTITY_CLASS)) { + if (!$cl = $this->getRelatedClassName($meta, $annot->class)) { + throw new InvalidMappingException("Translation class: {$annot->class} does not exist."); + } + $config['translationClass'] = $cl; + } + + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + // translatable property + if ($translatable = $this->reader->getPropertyAnnotation($property, self::TRANSLATABLE)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find translatable [{$field}] as mapped property in entity - {$meta->name}"); + } + // fields cannot be overrided and throws mapping exception + $config['fields'][] = $field; + if (isset($translatable->fallback)) { + $config['fallback'][$field] = $translatable->fallback; + } + } + // locale property + if ($this->reader->getPropertyAnnotation($property, self::LOCALE)) { + $field = $property->getName(); + if ($meta->hasField($field)) { + throw new InvalidMappingException("Locale field [{$field}] should not be mapped as column property in entity - {$meta->name}, since it makes no sense"); + } + $config['locale'] = $field; + } elseif ($this->reader->getPropertyAnnotation($property, self::LANGUAGE)) { + $field = $property->getName(); + if ($meta->hasField($field)) { + throw new InvalidMappingException("Language field [{$field}] should not be mapped as column property in entity - {$meta->name}, since it makes no sense"); + } + $config['locale'] = $field; + } + } + + // Embedded entity + if (property_exists($meta, 'embeddedClasses') && $meta->embeddedClasses) { + foreach ($meta->embeddedClasses as $propertyName => $embeddedClassInfo) { + $embeddedClass = new \ReflectionClass($embeddedClassInfo['class']); + foreach ($embeddedClass->getProperties() as $embeddedProperty) { + if ($translatable = $this->reader->getPropertyAnnotation($embeddedProperty, self::TRANSLATABLE)) { + $field = $propertyName . '.' . $embeddedProperty->getName(); + + $config['fields'][] = $field; + if (isset($translatable->fallback)) { + $config['fallback'][$field] = $translatable->fallback; + } + } + } + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Translatable does not support composite identifiers in class - {$meta->name}"); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..d0101e4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Xml.php @@ -0,0 +1,105 @@ + + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + $xmlDoctrine = $xml; + + $xml = $xml->children(self::GEDMO_NAMESPACE_URI); + + if (($xmlDoctrine->getName() == 'entity' || $xmlDoctrine->getName() == 'mapped-superclass')) { + if ($xml->count() && isset($xml->translation)) { + /** + * @var \SimpleXmlElement $data + */ + $data = $xml->translation; + if ($this->_isAttributeSet($data, 'locale')) { + $config['locale'] = $this->_getAttribute($data, 'locale'); + } elseif ($this->_isAttributeSet($data, 'language')) { + $config['locale'] = $this->_getAttribute($data, 'language'); + } + if ($this->_isAttributeSet($data, 'entity')) { + $entity = $this->_getAttribute($data, 'entity'); + if (!$cl = $this->getRelatedClassName($meta, $entity)) { + throw new InvalidMappingException("Translation entity class: {$entity} does not exist."); + } + $config['translationClass'] = $cl; + } + } + } + + if (property_exists($meta, 'embeddedClasses') && $meta->embeddedClasses) { + foreach ($meta->embeddedClasses as $propertyName => $embeddedClassInfo) { + $xmlEmbeddedClass = $this->_getMapping($embeddedClassInfo['class']); + $this->inspectElementsForTranslatableFields($xmlEmbeddedClass, $config, $propertyName); + } + } + + if ($xmlDoctrine->{'attribute-overrides'}->count() > 0) { + foreach ($xmlDoctrine->{'attribute-overrides'}->{'attribute-override'} as $overrideMapping) { + $this->buildFieldConfiguration($this->_getAttribute($overrideMapping, 'name'), $overrideMapping->field, $config); + } + } + + $this->inspectElementsForTranslatableFields($xmlDoctrine, $config); + + if (!$meta->isMappedSuperclass && $config) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Translatable does not support composite identifiers in class - {$meta->name}"); + } + } + } + + private function inspectElementsForTranslatableFields(\SimpleXMLElement $xml, array &$config, $prefix = null) + { + if (!isset($xml->field)) { + return; + } + + foreach ($xml->field as $mapping) { + $mappingDoctrine = $mapping; + + $fieldName = $this->_getAttribute($mappingDoctrine, 'name'); + if ($prefix !== null) { + $fieldName = $prefix . '.' . $fieldName; + } + $this->buildFieldConfiguration($fieldName, $mapping, $config); + } + } + + private function buildFieldConfiguration($fieldName, \SimpleXMLElement $mapping, array &$config) + { + $mapping = $mapping->children(self::GEDMO_NAMESPACE_URI); + if ($mapping->count() > 0 && isset($mapping->translatable)) { + $config['fields'][] = $fieldName; + /** @var \SimpleXmlElement $data */ + $data = $mapping->translatable; + if ($this->_isAttributeSet($data, 'fallback')) { + $config['fallback'][$fieldName] = 'true' == $this->_getAttribute($data, 'fallback') ? true : false; + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..4ee454e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Driver/Yaml.php @@ -0,0 +1,88 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['gedmo'])) { + $classMapping = $mapping['gedmo']; + if (isset($classMapping['translation']['entity'])) { + $translationEntity = $classMapping['translation']['entity']; + if (!$cl = $this->getRelatedClassName($meta, $translationEntity)) { + throw new InvalidMappingException("Translation entity class: {$translationEntity} does not exist."); + } + $config['translationClass'] = $cl; + } + if (isset($classMapping['translation']['locale'])) { + $config['locale'] = $classMapping['translation']['locale']; + } elseif (isset($classMapping['translation']['language'])) { + $config['locale'] = $classMapping['translation']['language']; + } + } + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + $this->buildFieldConfiguration($field, $fieldMapping, $config); + } + } + + if (isset($mapping['attributeOverride'])) { + foreach ($mapping['attributeOverride'] as $field => $overrideMapping) { + $this->buildFieldConfiguration($field, $overrideMapping, $config); + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Translatable does not support composite identifiers in class - {$meta->name}"); + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } + + private function buildFieldConfiguration($field, $fieldMapping, array &$config) + { + if (is_array($fieldMapping) && isset($fieldMapping['gedmo'])) { + if (in_array('translatable', $fieldMapping['gedmo']) || isset($fieldMapping['gedmo']['translatable'])) { + // fields cannot be overrided and throws mapping exception + $config['fields'][] = $field; + if (isset($fieldMapping['gedmo']['translatable']['fallback'])) { + $config['fallback'][$field] = $fieldMapping['gedmo']['translatable']['fallback']; + } + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..6c2ad4e --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,205 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements TranslatableAdapter +{ + /** + * {@inheritDoc} + */ + public function usesPersonalTranslation($translationClassName) + { + return $this + ->getObjectManager() + ->getClassMetadata($translationClassName) + ->getReflectionClass() + ->isSubclassOf('Gedmo\Translatable\Document\MappedSuperclass\AbstractPersonalTranslation') + ; + } + + /** + * {@inheritDoc} + */ + public function getDefaultTranslationClass() + { + return 'Gedmo\\Translatable\\Document\\Translation'; + } + + /** + * {@inheritDoc} + */ + public function loadTranslations($object, $translationClass, $locale, $objectClass) + { + $dm = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $dm); + $result = array(); + + if ($this->usesPersonalTranslation($translationClass)) { + // first try to load it using collection + foreach ($wrapped->getMetadata()->fieldMappings as $mapping) { + $isRightCollection = isset($mapping['association']) + && $mapping['association'] === ClassMetadataInfo::REFERENCE_MANY + && $mapping['targetDocument'] === $translationClass + && $mapping['mappedBy'] === 'object' + ; + if ($isRightCollection) { + $collection = $wrapped->getPropertyValue($mapping['fieldName']); + foreach ($collection as $trans) { + if ($trans->getLocale() === $locale) { + $result[] = array( + 'field' => $trans->getField(), + 'content' => $trans->getContent(), + ); + } + } + + return $result; + } + } + $q = $dm + ->createQueryBuilder($translationClass) + ->field('object.$id')->equals($wrapped->getIdentifier()) + ->field('locale')->equals($locale) + ->getQuery() + ; + } else { + // load translated content for all translatable fields + // construct query + $q = $dm + ->createQueryBuilder($translationClass) + ->field('foreignKey')->equals($wrapped->getIdentifier()) + ->field('locale')->equals($locale) + ->field('objectClass')->equals($objectClass) + ->getQuery() + ; + } + $q->setHydrate(false); + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = $result->toArray(); + } + + return $result; + } + + /** + * {@inheritDoc} + */ + public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass, $objectClass) + { + $dm = $this->getObjectManager(); + $qb = $dm + ->createQueryBuilder($translationClass) + ->field('locale')->equals($locale) + ->field('field')->equals($field) + ->limit(1) + ; + if ($this->usesPersonalTranslation($translationClass)) { + $qb->field('object.$id')->equals($wrapped->getIdentifier()); + } else { + $qb->field('foreignKey')->equals($wrapped->getIdentifier()); + $qb->field('objectClass')->equals($objectClass); + } + $q = $qb->getQuery(); + $result = $q->execute(); + if ($result instanceof Cursor) { + $result = current($result->toArray()); + } + + return $result; + } + + /** + * {@inheritDoc} + */ + public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass, $objectClass) + { + $dm = $this->getObjectManager(); + $qb = $dm + ->createQueryBuilder($transClass) + ->remove() + ; + if ($this->usesPersonalTranslation($transClass)) { + $qb->field('object.$id')->equals($wrapped->getIdentifier()); + } else { + $qb->field('foreignKey')->equals($wrapped->getIdentifier()); + $qb->field('objectClass')->equals($objectClass); + } + $q = $qb->getQuery(); + + return $q->execute(); + } + + /** + * {@inheritDoc} + */ + public function insertTranslationRecord($translation) + { + $dm = $this->getObjectManager(); + $meta = $dm->getClassMetadata(get_class($translation)); + $collection = $dm->getDocumentCollection($meta->name); + $data = array(); + + foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) { + if (!$meta->isIdentifier($fieldName)) { + $data[$meta->fieldMappings[$fieldName]['name']] = $reflProp->getValue($translation); + } + } + + if (!$collection->insert($data)) { + throw new \Gedmo\Exception\RuntimeException('Failed to insert new Translation record'); + } + } + + /** + * {@inheritDoc} + */ + public function getTranslationValue($object, $field, $value = false) + { + $dm = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $dm); + $meta = $wrapped->getMetadata(); + $mapping = $meta->getFieldMapping($field); + $type = $this->getType($mapping['type']); + if ($value === false) { + $value = $wrapped->getPropertyValue($field); + } + + return $type->convertToDatabaseValue($value); + } + + /** + * {@inheritDoc} + */ + public function setTranslationValue($object, $field, $value) + { + $dm = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $dm); + $meta = $wrapped->getMetadata(); + $mapping = $meta->getFieldMapping($field); + $type = $this->getType($mapping['type']); + + $value = $type->convertToPHPValue($value); + $wrapped->setPropertyValue($field, $value); + } + + private function getType($type) + { + // due to change in ODM beta 9 + return class_exists('Doctrine\ODM\MongoDB\Types\Type') ? \Doctrine\ODM\MongoDB\Types\Type::getType($type) + : \Doctrine\ODM\MongoDB\Mapping\Types\Type::getType($type); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..d5c2028 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,260 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements TranslatableAdapter +{ + /** + * {@inheritDoc} + */ + public function usesPersonalTranslation($translationClassName) + { + return $this + ->getObjectManager() + ->getClassMetadata($translationClassName) + ->getReflectionClass() + ->isSubclassOf('Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation') + ; + } + + /** + * {@inheritDoc} + */ + public function getDefaultTranslationClass() + { + return 'Gedmo\\Translatable\\Entity\\Translation'; + } + + /** + * {@inheritDoc} + */ + public function loadTranslations($object, $translationClass, $locale, $objectClass) + { + $em = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $em); + $result = array(); + if ($this->usesPersonalTranslation($translationClass)) { + // first try to load it using collection + $found = false; + foreach ($wrapped->getMetadata()->associationMappings as $assoc) { + $isRightCollection = $assoc['targetEntity'] === $translationClass + && $assoc['mappedBy'] === 'object' + && $assoc['type'] === ClassMetadataInfo::ONE_TO_MANY + ; + if ($isRightCollection) { + $collection = $wrapped->getPropertyValue($assoc['fieldName']); + foreach ($collection as $trans) { + if ($trans->getLocale() === $locale) { + $result[] = array( + 'field' => $trans->getField(), + 'content' => $trans->getContent(), + ); + } + } + $found = true; + break; + } + } + // if collection is not set, fetch it through relation + if (!$found) { + $dql = 'SELECT t.content, t.field FROM '.$translationClass.' t'; + $dql .= ' WHERE t.locale = :locale'; + $dql .= ' AND t.object = :object'; + + $q = $em->createQuery($dql); + $q->setParameters(compact('object', 'locale')); + $result = $q->getArrayResult(); + } + } else { + // load translated content for all translatable fields + $objectId = $this->foreignKey($wrapped->getIdentifier(), $translationClass); + // construct query + $dql = 'SELECT t.content, t.field FROM '.$translationClass.' t'; + $dql .= ' WHERE t.foreignKey = :objectId'; + $dql .= ' AND t.locale = :locale'; + $dql .= ' AND t.objectClass = :objectClass'; + // fetch results + $q = $em->createQuery($dql); + $q->setParameters(compact('objectId', 'locale', 'objectClass')); + $result = $q->getArrayResult(); + } + + return $result; + } + + /** + * Transforms foreigh key of translation to appropriate PHP value + * to prevent database level cast + * + * @param $key - foreign key value + * @param $className - translation class name + * @return transformed foreign key + */ + private function foreignKey($key, $className) + { + $em = $this->getObjectManager(); + $meta = $em->getClassMetadata($className); + $type = Type::getType($meta->getTypeOfField('foreignKey')); + switch ($type->getName()) { + case Type::BIGINT: + case Type::INTEGER: + case Type::SMALLINT: + return intval($key); + default: + return (string)$key; + } + } + + /** + * {@inheritDoc} + */ + public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass, $objectClass) + { + $em = $this->getObjectManager(); + // first look in identityMap, will save one SELECT query + foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $objects) { + if ($className === $translationClass) { + foreach ($objects as $trans) { + $isRequestedTranslation = !$trans instanceof Proxy + && $trans->getLocale() === $locale + && $trans->getField() === $field + ; + if ($isRequestedTranslation) { + if ($this->usesPersonalTranslation($translationClass)) { + $isRequestedTranslation = $trans->getObject() === $wrapped->getObject(); + } else { + $objectId = $this->foreignKey($wrapped->getIdentifier(), $translationClass); + $isRequestedTranslation = $trans->getForeignKey() === $objectId + && $trans->getObjectClass() === $wrapped->getMetadata()->name + ; + } + } + if ($isRequestedTranslation) { + return $trans; + } + } + } + } + + $qb = $em->createQueryBuilder(); + $qb->select('trans') + ->from($translationClass, 'trans') + ->where( + 'trans.locale = :locale', + 'trans.field = :field' + ) + ; + $qb->setParameters(compact('locale', 'field')); + if ($this->usesPersonalTranslation($translationClass)) { + $qb->andWhere('trans.object = :object'); + if ($wrapped->getIdentifier()) { + $qb->setParameter('object', $wrapped->getObject()); + } else { + $qb->setParameter('object', null); + } + } else { + $qb->andWhere('trans.foreignKey = :objectId'); + $qb->andWhere('trans.objectClass = :objectClass'); + $qb->setParameter('objectId', $this->foreignKey($wrapped->getIdentifier(), $translationClass)); + $qb->setParameter('objectClass', $objectClass); + } + $q = $qb->getQuery(); + $q->setMaxResults(1); + $result = $q->getResult(); + + if ($result) { + return array_shift($result); + } + + return null; + } + + /** + * {@inheritDoc} + */ + public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass, $objectClass) + { + $qb = $this + ->getObjectManager() + ->createQueryBuilder() + ->delete($transClass, 'trans') + ; + if ($this->usesPersonalTranslation($transClass)) { + $qb->where('trans.object = :object'); + $qb->setParameter('object', $wrapped->getObject()); + } else { + $qb->where( + 'trans.foreignKey = :objectId', + 'trans.objectClass = :class' + ); + $qb->setParameter('objectId', $this->foreignKey($wrapped->getIdentifier(), $transClass)); + $qb->setParameter('class', $objectClass); + } + + return $qb->getQuery()->getSingleScalarResult(); + } + + /** + * {@inheritDoc} + */ + public function insertTranslationRecord($translation) + { + $em = $this->getObjectManager(); + $meta = $em->getClassMetadata(get_class($translation)); + $data = array(); + + foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) { + if (!$meta->isIdentifier($fieldName)) { + $data[$meta->getColumnName($fieldName)] = $reflProp->getValue($translation); + } + } + + $table = $meta->getTableName(); + if (!$em->getConnection()->insert($table, $data)) { + throw new \Gedmo\Exception\RuntimeException('Failed to insert new Translation record'); + } + } + + /** + * {@inheritDoc} + */ + public function getTranslationValue($object, $field, $value = false) + { + $em = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $em); + $meta = $wrapped->getMetadata(); + $type = Type::getType($meta->getTypeOfField($field)); + if ($value === false) { + $value = $wrapped->getPropertyValue($field); + } + + return $type->convertToDatabaseValue($value, $em->getConnection()->getDatabasePlatform()); + } + + /** + * {@inheritDoc} + */ + public function setTranslationValue($object, $field, $value) + { + $em = $this->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $em); + $meta = $wrapped->getMetadata(); + $type = Type::getType($meta->getTypeOfField($field)); + $value = $type->convertToPHPValue($value, $em->getConnection()->getDatabasePlatform()); + $wrapped->setPropertyValue($field, $value); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/TranslatableAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/TranslatableAdapter.php new file mode 100644 index 0000000..dfd66c9 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Mapping/Event/TranslatableAdapter.php @@ -0,0 +1,96 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface TranslatableAdapter extends AdapterInterface +{ + /** + * Checks if $translationClassName is a subclass + * of personal translation + * + * @param string $translationClassName + * + * @return boolean + */ + public function usesPersonalTranslation($translationClassName); + + /** + * Get default LogEntry class used to store the logs + * + * @return string + */ + public function getDefaultTranslationClass(); + + /** + * Load the translations for a given object + * + * @param object $object + * @param string $translationClass + * @param string $locale + * @param string $objectClass + * + * @return array + */ + public function loadTranslations($object, $translationClass, $locale, $objectClass); + + /** + * Search for existing translation record + * + * @param AbstractWrapper $wrapped + * @param string $locale + * @param string $field + * @param string $translationClass + * @param string $objectClass + * + * @return mixed - null if nothing is found, Translation otherwise + */ + public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass, $objectClass); + + /** + * Removes all associated translations for given object + * + * @param AbstractWrapper $wrapped + * @param string $transClass + * @param string $objectClass + */ + public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass, $objectClass); + + /** + * Inserts the translation record + * + * @param object $translation + */ + public function insertTranslationRecord($translation); + + /** + * Get the transformed value for translation + * storage + * + * @param object $object + * @param string $field + * @param mixed $value + * + * @return mixed + */ + public function getTranslationValue($object, $field, $value = false); + + /** + * Transform the value from database + * for translation + * + * @param object $object + * @param string $field + * @param mixed $value + */ + public function setTranslationValue($object, $field, $value); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php new file mode 100644 index 0000000..90272b5 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Query/TreeWalker/TranslationWalker.php @@ -0,0 +1,470 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TranslationWalker extends SqlWalker +{ + /** + * Name for translation fallback hint + * + * @internal + */ + const HINT_TRANSLATION_FALLBACKS = '__gedmo.translatable.stored.fallbacks'; + + /** + * Customized object hydrator name + * + * @internal + */ + const HYDRATE_OBJECT_TRANSLATION = '__gedmo.translatable.object.hydrator'; + + /** + * Customized object hydrator name + * + * @internal + */ + const HYDRATE_SIMPLE_OBJECT_TRANSLATION = '__gedmo.translatable.simple_object.hydrator'; + + /** + * Stores all component references from select clause + * + * @var array + */ + private $translatedComponents = array(); + + /** + * DBAL database platform + * + * @var \Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * DBAL database connection + * + * @var \Doctrine\DBAL\Connection + */ + private $conn; + + /** + * List of aliases to replace with translation + * content reference + * + * @var array + */ + private $replacements = array(); + + /** + * List of joins for translated components in query + * + * @var array + */ + private $components = array(); + + /** + * {@inheritDoc} + */ + public function __construct($query, $parserResult, array $queryComponents) + { + parent::__construct($query, $parserResult, $queryComponents); + $this->conn = $this->getConnection(); + $this->platform = $this->getConnection()->getDatabasePlatform(); + $this->listener = $this->getTranslatableListener(); + $this->extractTranslatedComponents($queryComponents); + } + + /** + * {@inheritDoc} + */ + public function getExecutor($AST) + { + if (!$AST instanceof SelectStatement) { + throw new \Gedmo\Exception\UnexpectedValueException('Translation walker should be used only on select statement'); + } + $this->prepareTranslatedComponents(); + + return new SingleSelectExecutor($AST, $this); + } + + /** + * {@inheritDoc} + */ + public function walkSelectStatement(SelectStatement $AST) + { + $result = parent::walkSelectStatement($AST); + if (!count($this->translatedComponents)) { + return $result; + } + + $hydrationMode = $this->getQuery()->getHydrationMode(); + if ($hydrationMode === Query::HYDRATE_OBJECT) { + $this->getQuery()->setHydrationMode(self::HYDRATE_OBJECT_TRANSLATION); + $this->getEntityManager()->getConfiguration()->addCustomHydrationMode( + self::HYDRATE_OBJECT_TRANSLATION, + 'Gedmo\\Translatable\\Hydrator\\ORM\\ObjectHydrator' + ); + $this->getQuery()->setHint(Query::HINT_REFRESH, true); + } elseif ($hydrationMode === Query::HYDRATE_SIMPLEOBJECT) { + $this->getQuery()->setHydrationMode(self::HYDRATE_SIMPLE_OBJECT_TRANSLATION); + $this->getEntityManager()->getConfiguration()->addCustomHydrationMode( + self::HYDRATE_SIMPLE_OBJECT_TRANSLATION, + 'Gedmo\\Translatable\\Hydrator\\ORM\\SimpleObjectHydrator' + ); + $this->getQuery()->setHint(Query::HINT_REFRESH, true); + } + + return $result; + } + + /** + * {@inheritDoc} + */ + public function walkSelectClause($selectClause) + { + $result = parent::walkSelectClause($selectClause); + $result = $this->replace($this->replacements, $result); + + return $result; + } + + /** + * {@inheritDoc} + */ + public function walkFromClause($fromClause) + { + $result = parent::walkFromClause($fromClause); + $result .= $this->joinTranslations($fromClause); + + return $result; + } + + /** + * {@inheritDoc} + */ + public function walkWhereClause($whereClause) + { + $result = parent::walkWhereClause($whereClause); + + return $this->replace($this->replacements, $result); + } + + /** + * {@inheritDoc} + */ + public function walkHavingClause($havingClause) + { + $result = parent::walkHavingClause($havingClause); + + return $this->replace($this->replacements, $result); + } + + /** + * {@inheritDoc} + */ + public function walkOrderByClause($orderByClause) + { + $result = parent::walkOrderByClause($orderByClause); + + return $this->replace($this->replacements, $result); + } + + /** + * {@inheritDoc} + */ + public function walkSubselect($subselect) + { + $result = parent::walkSubselect($subselect); + + return $result; + } + + /** + * {@inheritDoc} + */ + public function walkSubselectFromClause($subselectFromClause) + { + $result = parent::walkSubselectFromClause($subselectFromClause); + $result .= $this->joinTranslations($subselectFromClause); + + return $result; + } + + /** + * {@inheritDoc} + */ + public function walkSimpleSelectClause($simpleSelectClause) + { + $result = parent::walkSimpleSelectClause($simpleSelectClause); + + return $this->replace($this->replacements, $result); + } + + /** + * {@inheritDoc} + */ + public function walkGroupByClause($groupByClause) + { + $result = parent::walkGroupByClause($groupByClause); + + return $this->replace($this->replacements, $result); + } + + /** + * Walks from clause, and creates translation joins + * for the translated components + * + * @param \Doctrine\ORM\Query\AST\FromClause $from + * @return string + */ + private function joinTranslations($from) + { + $result = ''; + foreach ($from->identificationVariableDeclarations as $decl) { + if ($decl->rangeVariableDeclaration instanceof RangeVariableDeclaration) { + if (isset($this->components[$decl->rangeVariableDeclaration->aliasIdentificationVariable])) { + $result .= $this->components[$decl->rangeVariableDeclaration->aliasIdentificationVariable]; + } + } + if (isset($decl->joinVariableDeclarations)) { + foreach ($decl->joinVariableDeclarations as $joinDecl) { + if ($joinDecl->join instanceof Join) { + if (isset($this->components[$joinDecl->join->aliasIdentificationVariable])) { + $result .= $this->components[$joinDecl->join->aliasIdentificationVariable]; + } + } + } + } else { + // based on new changes + foreach ($decl->joins as $join) { + if ($join instanceof Join) { + if (isset($this->components[$join->joinAssociationDeclaration->aliasIdentificationVariable])) { + $result .= $this->components[$join->joinAssociationDeclaration->aliasIdentificationVariable]; + } + } + } + } + } + + return $result; + } + + /** + * Creates a left join list for translations + * on used query components + * + * @todo: make it cleaner + * @return string + */ + private function prepareTranslatedComponents() + { + $q = $this->getQuery(); + $locale = $q->getHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE); + if (!$locale) { + // use from listener + $locale = $this->listener->getListenerLocale(); + } + $defaultLocale = $this->listener->getDefaultLocale(); + if ($locale === $defaultLocale && !$this->listener->getPersistDefaultLocaleTranslation()) { + // Skip preparation as there's no need to translate anything + return; + } + $em = $this->getEntityManager(); + $ea = new TranslatableEventAdapter(); + $ea->setEntityManager($em); + $quoteStrategy = $em->getConfiguration()->getQuoteStrategy(); + $joinStrategy = $q->getHint(TranslatableListener::HINT_INNER_JOIN) ? 'INNER' : 'LEFT'; + + foreach ($this->translatedComponents as $dqlAlias => $comp) { + /** @var ClassMetadata $meta */ + $meta = $comp['metadata']; + $config = $this->listener->getConfiguration($em, $meta->name); + $transClass = $this->listener->getTranslationClass($ea, $meta->name); + $transMeta = $em->getClassMetadata($transClass); + $transTable = $quoteStrategy->getTableName($transMeta, $this->platform); + foreach ($config['fields'] as $field) { + $compTblAlias = $this->walkIdentificationVariable($dqlAlias, $field); + $tblAlias = $this->getSQLTableAlias('trans'.$compTblAlias.$field); + $sql = " {$joinStrategy} JOIN ".$transTable.' '.$tblAlias; + $sql .= ' ON '.$tblAlias.'.'.$quoteStrategy->getColumnName('locale', $transMeta, $this->platform) + .' = '.$this->conn->quote($locale); + $sql .= ' AND '.$tblAlias.'.'.$quoteStrategy->getColumnName('field', $transMeta, $this->platform) + .' = '.$this->conn->quote($field); + $identifier = $meta->getSingleIdentifierFieldName(); + $idColName = $quoteStrategy->getColumnName($identifier, $meta, $this->platform); + if ($ea->usesPersonalTranslation($transClass)) { + $sql .= ' AND '.$tblAlias.'.'.$transMeta->getSingleAssociationJoinColumnName('object') + .' = '.$compTblAlias.'.'.$idColName; + } else { + $sql .= ' AND '.$tblAlias.'.'.$quoteStrategy->getColumnName('objectClass', $transMeta, $this->platform) + .' = '.$this->conn->quote($config['useObjectClass']); + + $mappingFK = $transMeta->getFieldMapping('foreignKey'); + $mappingPK = $meta->getFieldMapping($identifier); + $fkColName = $this->getCastedForeignKey($compTblAlias.'.'.$idColName, $mappingFK['type'], $mappingPK['type']); + $sql .= ' AND '.$tblAlias.'.'.$quoteStrategy->getColumnName('foreignKey', $transMeta, $this->platform) + .' = '.$fkColName; + } + isset($this->components[$dqlAlias]) ? $this->components[$dqlAlias] .= $sql : $this->components[$dqlAlias] = $sql; + + $originalField = $compTblAlias.'.'.$quoteStrategy->getColumnName($field, $meta, $this->platform); + $substituteField = $tblAlias.'.'.$quoteStrategy->getColumnName('content', $transMeta, $this->platform); + + // Treat translation as original field type + $fieldMapping = $meta->getFieldMapping($field); + if ((($this->platform instanceof MySqlPlatform) && + in_array($fieldMapping["type"], array("decimal"))) || + (!($this->platform instanceof MySqlPlatform) && + !in_array($fieldMapping["type"], array("datetime", "datetimetz", "date", "time")))) { + $type = Type::getType($fieldMapping["type"]); + $substituteField = 'CAST('.$substituteField.' AS '.$type->getSQLDeclaration($fieldMapping, $this->platform).')'; + } + + // Fallback to original if was asked for + if (($this->needsFallback() && (!isset($config['fallback'][$field]) || $config['fallback'][$field])) + || (!$this->needsFallback() && isset($config['fallback'][$field]) && $config['fallback'][$field]) + ) { + $substituteField = 'COALESCE('.$substituteField.', '.$originalField.')'; + } + + $this->replacements[$originalField] = $substituteField; + } + } + } + + /** + * Checks if translation fallbacks are needed + * + * @return boolean + */ + private function needsFallback() + { + $q = $this->getQuery(); + $fallback = $q->getHint(TranslatableListener::HINT_FALLBACK); + if (false === $fallback) { + // non overrided + $fallback = $this->listener->getTranslationFallback(); + } + + // applies fallbacks to scalar hydration as well + return (bool) $fallback; + } + + /** + * Search for translated components in the select clause + * + * @param array $queryComponents + */ + private function extractTranslatedComponents(array $queryComponents) + { + $em = $this->getEntityManager(); + foreach ($queryComponents as $alias => $comp) { + if (!isset($comp['metadata'])) { + continue; + } + $meta = $comp['metadata']; + $config = $this->listener->getConfiguration($em, $meta->name); + if ($config && isset($config['fields'])) { + $this->translatedComponents[$alias] = $comp; + } + } + } + + /** + * Get the currently used TranslatableListener + * + * @throws \Gedmo\Exception\RuntimeException - if listener is not found + * + * @return TranslatableListener + */ + private function getTranslatableListener() + { + $em = $this->getEntityManager(); + foreach ($em->getEventManager()->getListeners() as $event => $listeners) { + foreach ($listeners as $hash => $listener) { + if ($listener instanceof TranslatableListener) { + return $listener; + } + } + } + + throw new \Gedmo\Exception\RuntimeException('The translation listener could not be found'); + } + + /** + * Replaces given sql $str with required + * results + * + * @param array $repl + * @param string $str + * + * @return string + */ + private function replace(array $repl, $str) + { + foreach ($repl as $target => $result) { + $str = preg_replace_callback('/(\s|\()('.$target.')(,?)(\s|\)|$)/smi', function ($m) use ($result) { + return $m[1].$result.$m[3].$m[4]; + }, $str); + } + + return $str; + } + + /** + * Casts a foreign key if needed + * @NOTE: personal translations manages that for themselves. + * + * @param $component - a column with an alias to cast + * @param $typeFK - translation table foreign key type + * @param $typePK - primary key type which references translation table + * @return string - modified $component if needed + */ + private function getCastedForeignKey($component, $typeFK, $typePK) + { + // the keys are of same type + if ($typeFK === $typePK) { + return $component; + } + + // try to look at postgres casting + if ($this->platform instanceof PostgreSqlPlatform) { + switch ($typeFK) { + case 'string': + case 'guid': + // need to cast to VARCHAR + $component = $component . '::VARCHAR'; + break; + } + } + + // @TODO may add the same thing for MySQL for performance to match index + + return $component; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Translatable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Translatable.php new file mode 100644 index 0000000..44fabaf --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Translatable.php @@ -0,0 +1,34 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Translatable +{ + // use now annotations instead of predefined methods, this interface is not necessary + + /** + * @gedmo:TranslationEntity + * to specify custom translation class use + * class annotation @gedmo:TranslationEntity(class="your\class") + */ + + /** + * @gedmo:Translatable + * to mark the field as translatable, + * these fields will be translated + */ + + /** + * @gedmo:Locale OR @gedmo:Language + * to mark the field as locale used to override global + * locale settings from TranslatableListener + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php new file mode 100644 index 0000000..981d3f1 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/TranslatableListener.php @@ -0,0 +1,796 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TranslatableListener extends MappedEventSubscriber +{ + /** + * Query hint to override the fallback of translations + * integer 1 for true, 0 false + */ + const HINT_FALLBACK = 'gedmo.translatable.fallback'; + + /** + * Query hint to override the fallback locale + */ + const HINT_TRANSLATABLE_LOCALE = 'gedmo.translatable.locale'; + + /** + * Query hint to use inner join strategy for translations + */ + const HINT_INNER_JOIN = 'gedmo.translatable.inner_join.translations'; + + /** + * Locale which is set on this listener. + * If Entity being translated has locale defined it + * will override this one + * + * @var string + */ + protected $locale = 'en_US'; + + /** + * Default locale, this changes behavior + * to not update the original record field if locale + * which is used for updating is not default. This + * will load the default translation in other locales + * if record is not translated yet + * + * @var string + */ + private $defaultLocale = 'en_US'; + + /** + * If this is set to false, when if entity does + * not have a translation for requested locale + * it will show a blank value + * + * @var boolean + */ + private $translationFallback = false; + + /** + * List of translations which do not have the foreign + * key generated yet - MySQL case. These translations + * will be updated with new keys on postPersist event + * + * @var array + */ + private $pendingTranslationInserts = array(); + + /** + * Currently in case if there is TranslationQueryWalker + * in charge. We need to skip issuing additional queries + * on load + * + * @var boolean + */ + private $skipOnLoad = false; + + /** + * Tracks locale the objects currently translated in + * + * @var array + */ + private $translatedInLocale = array(); + + /** + * Whether or not, to persist default locale + * translation or keep it in original record + * + * @var boolean + */ + private $persistDefaultLocaleTranslation = false; + + /** + * Tracks translation object for default locale + * + * @var array + */ + private $translationInDefaultLocale = array(); + + /** + * Specifies the list of events to listen + * + * @return array + */ + public function getSubscribedEvents() + { + return array( + 'postLoad', + 'postPersist', + 'preFlush', + 'onFlush', + 'loadClassMetadata', + ); + } + + /** + * Set to skip or not onLoad event + * + * @param boolean $bool + * + * @return static + */ + public function setSkipOnLoad($bool) + { + $this->skipOnLoad = (bool) $bool; + + return $this; + } + + /** + * Whether or not, to persist default locale + * translation or keep it in original record + * + * @param boolean $bool + * + * @return static + */ + public function setPersistDefaultLocaleTranslation($bool) + { + $this->persistDefaultLocaleTranslation = (bool) $bool; + + return $this; + } + + /** + * Check if should persist default locale + * translation or keep it in original record + * + * @return boolean + */ + public function getPersistDefaultLocaleTranslation() + { + return (bool) $this->persistDefaultLocaleTranslation; + } + + /** + * Add additional $translation for pending $oid object + * which is being inserted + * + * @param string $oid + * @param object $translation + */ + public function addPendingTranslationInsert($oid, $translation) + { + $this->pendingTranslationInserts[$oid][] = $translation; + } + + /** + * Maps additional metadata + * + * @param EventArgs $eventArgs + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * Get the translation class to be used + * for the object $class + * + * @param TranslatableAdapter $ea + * @param string $class + * + * @return string + */ + public function getTranslationClass(TranslatableAdapter $ea, $class) + { + return isset(self::$configurations[$this->name][$class]['translationClass']) ? + self::$configurations[$this->name][$class]['translationClass'] : + $ea->getDefaultTranslationClass() + ; + } + + /** + * Enable or disable translation fallback + * to original record value + * + * @param boolean $bool + * + * @return static + */ + public function setTranslationFallback($bool) + { + $this->translationFallback = (bool) $bool; + + return $this; + } + + /** + * Weather or not is using the translation + * fallback to original record + * + * @return boolean + */ + public function getTranslationFallback() + { + return $this->translationFallback; + } + + /** + * Set the locale to use for translation listener + * + * @param string $locale + * + * @return static + */ + public function setTranslatableLocale($locale) + { + $this->validateLocale($locale); + $this->locale = $locale; + + return $this; + } + + /** + * Sets the default locale, this changes behavior + * to not update the original record field if locale + * which is used for updating is not default + * + * @param string $locale + * + * @return static + */ + public function setDefaultLocale($locale) + { + $this->validateLocale($locale); + $this->defaultLocale = $locale; + + return $this; + } + + /** + * Gets the default locale + * + * @return string + */ + public function getDefaultLocale() + { + return $this->defaultLocale; + } + + /** + * Get currently set global locale, used + * extensively during query execution + * + * @return string + */ + public function getListenerLocale() + { + return $this->locale; + } + + /** + * Gets the locale to use for translation. Loads object + * defined locale first.. + * + * @param object $object + * @param object $meta + * @param object $om + * + * @throws \Gedmo\Exception\RuntimeException - if language or locale property is not + * found in entity + * @return string + */ + public function getTranslatableLocale($object, $meta, $om = null) + { + $locale = $this->locale; + if (isset(self::$configurations[$this->name][$meta->name]['locale'])) { + /** @var \ReflectionClass $class */ + $class = $meta->getReflectionClass(); + $reflectionProperty = $class->getProperty(self::$configurations[$this->name][$meta->name]['locale']); + if (!$reflectionProperty) { + $column = self::$configurations[$this->name][$meta->name]['locale']; + throw new \Gedmo\Exception\RuntimeException("There is no locale or language property ({$column}) found on object: {$meta->name}"); + } + $reflectionProperty->setAccessible(true); + $value = $reflectionProperty->getValue($object); + if (is_object($value) && method_exists($value, '__toString')) { + $value = (string) $value; + } + if ($this->isValidLocale($value)) { + $locale = $value; + } + } elseif ($om instanceof DocumentManager) { + list($mapping, $parentObject) = $om->getUnitOfWork()->getParentAssociation($object); + if ($parentObject != null) { + $parentMeta = $om->getClassMetadata(get_class($parentObject)); + $locale = $this->getTranslatableLocale($parentObject, $parentMeta, $om); + } + } + + return $locale; + } + + /** + * Handle translation changes in default locale + * + * This has to be done in the preFlush because, when an entity has been loaded + * in a different locale, no changes will be detected. + * + * @param EventArgs $args + */ + public function preFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + foreach ($this->translationInDefaultLocale as $oid => $fields) { + $trans = reset($fields); + if ($ea->usesPersonalTranslation(get_class($trans))) { + $entity = $trans->getObject(); + } else { + $entity = $uow->tryGetById($trans->getForeignKey(), $trans->getObjectClass()); + } + + if (!$entity) { + continue; + } + + try { + $uow->scheduleForUpdate($entity); + } catch (ORMInvalidArgumentException $e) { + foreach ($fields as $field => $trans) { + $this->removeTranslationInDefaultLocale($oid, $field); + } + } + } + } + + /** + * Looks for translatable objects being inserted or updated + * for further processing + * + * @param EventArgs $args + */ + public function onFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + // check all scheduled inserts for Translatable objects + foreach ($ea->getScheduledObjectInsertions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + if (isset($config['fields'])) { + $this->handleTranslatableObjectUpdate($ea, $object, true); + } + } + // check all scheduled updates for Translatable entities + foreach ($ea->getScheduledObjectUpdates($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + if (isset($config['fields'])) { + $this->handleTranslatableObjectUpdate($ea, $object, false); + } + } + // check scheduled deletions for Translatable entities + foreach ($ea->getScheduledObjectDeletions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + if (isset($config['fields'])) { + $wrapped = AbstractWrapper::wrap($object, $om); + $transClass = $this->getTranslationClass($ea, $meta->name); + $ea->removeAssociatedTranslations($wrapped, $transClass, $config['useObjectClass']); + } + } + } + + /** + * Checks for inserted object to update their translation + * foreign keys + * + * @param EventArgs $args + */ + public function postPersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + // check if entity is tracked by translatable and without foreign key + if ($this->getConfiguration($om, $meta->name) && count($this->pendingTranslationInserts)) { + $oid = spl_object_hash($object); + if (array_key_exists($oid, $this->pendingTranslationInserts)) { + // load the pending translations without key + $wrapped = AbstractWrapper::wrap($object, $om); + $objectId = $wrapped->getIdentifier(); + $translationClass = $this->getTranslationClass($ea, get_class($object)); + foreach ($this->pendingTranslationInserts[$oid] as $translation) { + if ($ea->usesPersonalTranslation($translationClass)) { + $translation->setObject($objectId); + } else { + $translation->setForeignKey($objectId); + } + $ea->insertTranslationRecord($translation); + } + unset($this->pendingTranslationInserts[$oid]); + } + } + } + + /** + * After object is loaded, listener updates the translations + * by currently used locale + * + * @param EventArgs $args + */ + public function postLoad(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + if (isset($config['fields'])) { + $locale = $this->getTranslatableLocale($object, $meta, $om); + $oid = spl_object_hash($object); + $this->translatedInLocale[$oid] = $locale; + } + + if ($this->skipOnLoad) { + return; + } + + if (isset($config['fields']) && ($locale !== $this->defaultLocale || $this->persistDefaultLocaleTranslation)) { + // fetch translations + $translationClass = $this->getTranslationClass($ea, $config['useObjectClass']); + $result = $ea->loadTranslations( + $object, + $translationClass, + $locale, + $config['useObjectClass'] + ); + // translate object's translatable properties + foreach ($config['fields'] as $field) { + $translated = ''; + $is_translated = false; + foreach ((array) $result as $entry) { + if ($entry['field'] == $field) { + $translated = $entry['content']; + $is_translated = true; + break; + } + } + // update translation + if ($is_translated + || (!$this->translationFallback && (!isset($config['fallback'][$field]) || !$config['fallback'][$field])) + || ($this->translationFallback && isset($config['fallback'][$field]) && !$config['fallback'][$field]) + ) { + $ea->setTranslationValue($object, $field, $translated); + // ensure clean changeset + $ea->setOriginalObjectProperty( + $om->getUnitOfWork(), + $oid, + $field, + $meta->getReflectionProperty($field)->getValue($object) + ); + } + } + } + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } + + /** + * Validates the given locale + * + * @param string $locale - locale to validate + * + * @throws \Gedmo\Exception\InvalidArgumentException if locale is not valid + */ + protected function validateLocale($locale) + { + if (!$this->isValidLocale($locale)) { + throw new \Gedmo\Exception\InvalidArgumentException('Locale or language cannot be empty and must be set through Listener or Entity'); + } + } + + /** + * Check if the given locale is valid + * + * @param string $locale - locale to check + * + * @return bool + */ + private function isValidlocale($locale) + { + return is_string($locale) && strlen($locale); + } + + /** + * Creates the translation for object being flushed + * + * @param TranslatableAdapter $ea + * @param object $object + * @param boolean $isInsert + * + * @throws \UnexpectedValueException - if locale is not valid, or + * primary key is composite, missing or invalid + */ + private function handleTranslatableObjectUpdate(TranslatableAdapter $ea, $object, $isInsert) + { + $om = $ea->getObjectManager(); + $wrapped = AbstractWrapper::wrap($object, $om); + $meta = $wrapped->getMetadata(); + $config = $this->getConfiguration($om, $meta->name); + // no need cache, metadata is loaded only once in MetadataFactoryClass + $translationClass = $this->getTranslationClass($ea, $config['useObjectClass']); + $translationMetadata = $om->getClassMetadata($translationClass); + + // check for the availability of the primary key + $objectId = $wrapped->getIdentifier(); + // load the currently used locale + $locale = $this->getTranslatableLocale($object, $meta, $om); + + $uow = $om->getUnitOfWork(); + $oid = spl_object_hash($object); + $changeSet = $ea->getObjectChangeSet($uow, $object); + $translatableFields = $config['fields']; + foreach ($translatableFields as $field) { + $wasPersistedSeparetely = false; + $skip = isset($this->translatedInLocale[$oid]) && $locale === $this->translatedInLocale[$oid]; + $skip = $skip && !isset($changeSet[$field]) && !$this->getTranslationInDefaultLocale($oid, $field); + if ($skip) { + continue; // locale is same and nothing changed + } + $translation = null; + foreach ($ea->getScheduledObjectInsertions($uow) as $trans) { + if ($locale !== $this->defaultLocale + && get_class($trans) === $translationClass + && $trans->getLocale() === $this->defaultLocale + && $trans->getField() === $field + && $this->belongsToObject($ea, $trans, $object)) { + $this->setTranslationInDefaultLocale($oid, $field, $trans); + break; + } + } + + // lookup persisted translations + foreach ($ea->getScheduledObjectInsertions($uow) as $trans) { + if (get_class($trans) !== $translationClass + || $trans->getLocale() !== $locale + || $trans->getField() !== $field) { + continue; + } + + if ($ea->usesPersonalTranslation($translationClass)) { + $wasPersistedSeparetely = $trans->getObject() === $object; + } else { + $wasPersistedSeparetely = $trans->getObjectClass() === $config['useObjectClass'] + && $trans->getForeignKey() === $objectId; + } + + if ($wasPersistedSeparetely) { + $translation = $trans; + break; + } + } + + // check if translation already is created + if (!$isInsert && !$translation) { + $translation = $ea->findTranslation( + $wrapped, + $locale, + $field, + $translationClass, + $config['useObjectClass'] + ); + } + + // create new translation if translation not already created and locale is different from default locale, otherwise, we have the date in the original record + $persistNewTranslation = !$translation + && ($locale !== $this->defaultLocale || $this->persistDefaultLocaleTranslation) + ; + if ($persistNewTranslation) { + $translation = $translationMetadata->newInstance(); + $translation->setLocale($locale); + $translation->setField($field); + if ($ea->usesPersonalTranslation($translationClass)) { + $translation->setObject($object); + } else { + $translation->setObjectClass($config['useObjectClass']); + $translation->setForeignKey($objectId); + } + } + + if ($translation) { + // set the translated field, take value using reflection + $content = $ea->getTranslationValue($object, $field); + $translation->setContent($content); + // check if need to update in database + $transWrapper = AbstractWrapper::wrap($translation, $om); + if (((is_null($content) && !$isInsert) || is_bool($content) || is_int($content) || is_string($content) || !empty($content)) && ($isInsert || !$transWrapper->getIdentifier() || isset($changeSet[$field]))) { + if ($isInsert && !$objectId && !$ea->usesPersonalTranslation($translationClass)) { + // if we do not have the primary key yet available + // keep this translation in memory to insert it later with foreign key + $this->pendingTranslationInserts[spl_object_hash($object)][] = $translation; + } else { + // persist and compute change set for translation + if ($wasPersistedSeparetely) { + $ea->recomputeSingleObjectChangeset($uow, $translationMetadata, $translation); + } else { + $om->persist($translation); + $uow->computeChangeSet($translationMetadata, $translation); + } + } + } + } + + if ($isInsert && $this->getTranslationInDefaultLocale($oid, $field) !== null) { + // We can't rely on object field value which is created in non-default locale. + // If we provide translation for default locale as well, the latter is considered to be trusted + // and object content should be overridden. + $wrapped->setPropertyValue($field, $this->getTranslationInDefaultLocale($oid, $field)->getContent()); + $ea->recomputeSingleObjectChangeset($uow, $meta, $object); + $this->removeTranslationInDefaultLocale($oid, $field); + } + } + $this->translatedInLocale[$oid] = $locale; + // check if we have default translation and need to reset the translation + if (!$isInsert && strlen($this->defaultLocale)) { + $this->validateLocale($this->defaultLocale); + $modifiedChangeSet = $changeSet; + foreach ($changeSet as $field => $changes) { + if (in_array($field, $translatableFields)) { + if ($locale !== $this->defaultLocale) { + $ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]); + unset($modifiedChangeSet[$field]); + } + } + } + $ea->recomputeSingleObjectChangeset($uow, $meta, $object); + // cleanup current changeset only if working in a another locale different than de default one, otherwise the changeset will always be reverted + if ($locale !== $this->defaultLocale) { + $ea->clearObjectChangeSet($uow, $oid); + // recompute changeset only if there are changes other than reverted translations + if ($modifiedChangeSet || $this->hasTranslationsInDefaultLocale($oid)) { + foreach ($modifiedChangeSet as $field => $changes) { + $ea->setOriginalObjectProperty($uow, $oid, $field, $changes[0]); + } + foreach ($translatableFields as $field) { + if ($this->getTranslationInDefaultLocale($oid, $field) !== null) { + $wrapped->setPropertyValue($field, $this->getTranslationInDefaultLocale($oid, $field)->getContent()); + $this->removeTranslationInDefaultLocale($oid, $field); + } + } + $ea->recomputeSingleObjectChangeset($uow, $meta, $object); + } + } + } + } + + /** + * Sets translation object which represents translation in default language. + * + * @param string $oid hash of basic entity + * @param string $field field of basic entity + * @param mixed $trans Translation object + */ + public function setTranslationInDefaultLocale($oid, $field, $trans) + { + if (!isset($this->translationInDefaultLocale[$oid])) { + $this->translationInDefaultLocale[$oid] = array(); + } + $this->translationInDefaultLocale[$oid][$field] = $trans; + } + + /** + * @return boolean + */ + public function isSkipOnLoad() + { + return $this->skipOnLoad; + } + + /** + * Removes translation object which represents translation in default language. + * This is for internal use only. + * + * @param string $oid hash of the basic entity + * @param string $field field of basic entity + */ + private function removeTranslationInDefaultLocale($oid, $field) + { + if (isset($this->translationInDefaultLocale[$oid])) { + if (isset($this->translationInDefaultLocale[$oid][$field])) { + unset($this->translationInDefaultLocale[$oid][$field]); + } + if (! $this->translationInDefaultLocale[$oid]) { + // We removed the final remaining elements from the + // translationInDefaultLocale[$oid] array, so we might as well + // completely remove the entry at $oid. + unset($this->translationInDefaultLocale[$oid]); + } + } + } + + /** + * Gets translation object which represents translation in default language. + * This is for internal use only. + * + * @param string $oid hash of the basic entity + * @param string $field field of basic entity + * + * @return mixed Returns translation object if it exists or NULL otherwise + */ + private function getTranslationInDefaultLocale($oid, $field) + { + if (array_key_exists($oid, $this->translationInDefaultLocale)) { + if (array_key_exists($field, $this->translationInDefaultLocale[$oid])) { + $ret = $this->translationInDefaultLocale[$oid][$field]; + } else { + $ret = null; + } + } else { + $ret = null; + } + + return $ret; + } + + /** + * Check if object has any translation object which represents translation in default language. + * This is for internal use only. + * + * @param string $oid hash of the basic entity + * + * @return bool + */ + public function hasTranslationsInDefaultLocale($oid) + { + return array_key_exists($oid, $this->translationInDefaultLocale); + } + + /** + * Checks if the translation entity belongs to the object in question + * + * @param TranslatableAdapter $ea + * @param object $trans + * @param object $object + * + * @return boolean + */ + private function belongsToObject(TranslatableAdapter $ea, $trans, $object) + { + if ($ea->usesPersonalTranslation(get_class($trans))) { + return $trans->getObject() === $object; + } + + return ($trans->getForeignKey() === $object->getId() + && ($trans->getObjectClass() === get_class($object))); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Document/Translation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Document/Translation.php new file mode 100644 index 0000000..da695c4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Document/Translation.php @@ -0,0 +1,55 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * + * @MappedSuperclass + */ +abstract class Translation extends BaseTranslation +{ + /** + * @Id + */ + protected $id; + + /** + * @var string $locale + * + * @ODM\Field(type="string") + */ + protected $locale; + + /** + * @var string $property + * + * @ODM\Field(type="string") + */ + protected $property; + + /** + * @var string $value + * + * @ODM\Field(type="string") + */ + protected $value; + + /** + * Get id + * + * @return integer $id + */ + public function getId() + { + return $this->id; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity/Translation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity/Translation.php new file mode 100644 index 0000000..0b5ba9b --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Entity/Translation.php @@ -0,0 +1,60 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * + * @MappedSuperclass + */ +abstract class Translation extends BaseTranslation +{ + /** + * @var integer $id + * + * @Column(type="integer") + * @Id + * @GeneratedValue + */ + protected $id; + + /** + * @var string $locale + * + * @Column(type="string", length=8) + */ + protected $locale; + + /** + * @var string $property + * + * @Column(type="string", length=32) + */ + protected $property; + + /** + * @var string $value + * + * @Column(type="text", nullable=true) + */ + protected $value; + + /** + * Get id + * + * @return integer $id + */ + public function getId() + { + return $this->id; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Translation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Translation.php new file mode 100644 index 0000000..e30e96d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/Translation.php @@ -0,0 +1,101 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class Translation implements TranslationInterface +{ + protected $translatable; + protected $locale; + protected $property; + protected $value; + + /** + * Set translatable + * + * @param string $translatable + */ + public function setTranslatable($translatable) + { + $this->translatable = $translatable; + } + + /** + * Get translatable + * + * @return string + */ + public function getTranslatable() + { + return $this->translatable; + } + + /** + * Set locale + * + * @param string $locale + */ + public function setLocale($locale) + { + $this->locale = $locale; + } + + /** + * Get locale + * + * @return string + */ + public function getLocale() + { + return $this->locale; + } + + /** + * Set property + * + * @param string $property + */ + public function setProperty($property) + { + $this->property = $property; + } + + /** + * Get property + * + * @return string + */ + public function getProperty() + { + return $this->property; + } + + /** + * Set value + * + * @param string $value + * + * @return static + */ + public function setValue($value) + { + $this->value = $value; + + return $this; + } + + /** + * Get value + * + * @return string + */ + public function getValue() + { + return $this->value; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationInterface.php new file mode 100644 index 0000000..7ad2611 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationInterface.php @@ -0,0 +1,70 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface TranslationInterface +{ + /** + * Set translatable + * + * @param string $translatable + */ + public function setTranslatable($translatable); + + /** + * Get translatable + * + * @return string + */ + public function getTranslatable(); + + /** + * Set locale + * + * @param string $locale + */ + public function setLocale($locale); + + /** + * Get locale + * + * @return string + */ + public function getLocale(); + + /** + * Set property + * + * @param string $property + */ + public function setProperty($property); + + /** + * Get property + * + * @return string + */ + public function getProperty(); + + /** + * Set value + * + * @param string $value + * + * @return static + */ + public function setValue($value); + + /** + * Get value + * + * @return string + */ + public function getValue(); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationProxy.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationProxy.php new file mode 100644 index 0000000..6ae7363 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Translator/TranslationProxy.php @@ -0,0 +1,174 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TranslationProxy +{ + protected $locale; + protected $translatable; + protected $properties = array(); + protected $class; + /** + * @var Collection|TranslationInterface[] + */ + protected $coll; + + /** + * Initializes translations collection + * + * @param object $translatable object to translate + * @param string $locale translation name + * @param array $properties object properties to translate + * @param string $class translation entity|document class + * @param Collection $coll translations collection + * + * @throws \InvalidArgumentException Translation class doesn't implement TranslationInterface + */ + public function __construct($translatable, $locale, array $properties, $class, Collection $coll) + { + $this->translatable = $translatable; + $this->locale = $locale; + $this->properties = $properties; + $this->class = $class; + $this->coll = $coll; + + $translationClass = new \ReflectionClass($class); + if (!$translationClass->implementsInterface('Gedmo\Translator\TranslationInterface')) { + throw new \InvalidArgumentException(sprintf( + 'Translation class should implement Gedmo\Translator\TranslationInterface, "%s" given', + $class + )); + } + } + + public function __call($method, $arguments) + { + $matches = array(); + if (preg_match('/^(set|get)(.*)$/', $method, $matches)) { + $property = lcfirst($matches[2]); + + if (in_array($property, $this->properties)) { + switch ($matches[1]) { + case 'get': + return $this->getTranslatedValue($property); + case 'set': + if (isset($arguments[0])) { + $this->setTranslatedValue($property, $arguments[0]); + + return $this; + } + } + } + } + + $return = call_user_func_array(array($this->translatable, $method), $arguments); + + if ($this->translatable === $return) { + return $this; + } + + return $return; + } + + public function __get($property) + { + if (in_array($property, $this->properties)) { + if (method_exists($this, $getter = 'get'.ucfirst($property))) { + return $this->$getter; + } + + return $this->getTranslatedValue($property); + } + + return $this->translatable->$property; + } + + public function __set($property, $value) + { + if (in_array($property, $this->properties)) { + if (method_exists($this, $setter = 'set'.ucfirst($property))) { + return $this->$setter($value); + } + + return $this->setTranslatedValue($property, $value); + } + + $this->translatable->$property = $value; + } + + public function __isset($property) + { + return in_array($property, $this->properties); + } + + /** + * Returns locale name for the current translation proxy instance. + * + * @return string + */ + public function getProxyLocale() + { + return $this->locale; + } + + /** + * Returns translated value for specific property. + * + * @param string $property property name + * + * @return mixed + */ + public function getTranslatedValue($property) + { + return $this + ->findOrCreateTranslationForProperty($property, $this->getProxyLocale()) + ->getValue(); + } + + /** + * Sets translated value for specific property. + * + * @param string $property property name + * @param string $value value + */ + public function setTranslatedValue($property, $value) + { + $this + ->findOrCreateTranslationForProperty($property, $this->getProxyLocale()) + ->setValue($value); + } + + /** + * Finds existing or creates new translation for specified property + * + * @param string $property object property name + * @param string $locale locale name + * + * @return Translation + */ + private function findOrCreateTranslationForProperty($property, $locale) + { + foreach ($this->coll as $translation) { + if ($locale === $translation->getLocale() && $property === $translation->getProperty()) { + return $translation; + } + } + + /** @var TranslationInterface $translation */ + $translation = new $this->class; + $translation->setTranslatable($this->translatable); + $translation->setProperty($property); + $translation->setLocale($locale); + $this->coll->add($translation); + + return $translation; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/AbstractTreeRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/AbstractTreeRepository.php new file mode 100644 index 0000000..2ee454c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/AbstractTreeRepository.php @@ -0,0 +1,199 @@ +getEventManager()->getListeners() as $listeners) { + foreach ($listeners as $listener) { + if ($listener instanceof \Gedmo\Tree\TreeListener) { + $treeListener = $listener; + break; + } + } + if ($treeListener) { + break; + } + } + + if (is_null($treeListener)) { + throw new \Gedmo\Exception\InvalidMappingException('This repository can be attached only to ODM MongoDB tree listener'); + } + + $this->listener = $treeListener; + if (!$this->validate()) { + throw new \Gedmo\Exception\InvalidMappingException('This repository cannot be used for tree type: '.$treeListener->getStrategy($em, $class->name)->getName()); + } + + $this->repoUtils = new RepositoryUtils($this->dm, $this->getClassMetadata(), $this->listener, $this); + } + + /** + * Sets the RepositoryUtilsInterface instance + * + * @param \Gedmo\Tree\RepositoryUtilsInterface $repoUtils + * + * @return $this + */ + public function setRepoUtils(RepositoryUtilsInterface $repoUtils) + { + $this->repoUtils = $repoUtils; + + return $this; + } + + /** + * Returns the RepositoryUtilsInterface instance + * + * @return \Gedmo\Tree\RepositoryUtilsInterface|null + */ + public function getRepoUtils() + { + return $this->repoUtils; + } + + /** + * {@inheritDoc} + */ + public function childrenHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->repoUtils->childrenHierarchy($node, $direct, $options, $includeNode); + } + + /** + * {@inheritDoc} + */ + public function buildTree(array $nodes, array $options = array()) + { + return $this->repoUtils->buildTree($nodes, $options); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::setChildrenIndex + */ + public function setChildrenIndex($childrenIndex) + { + $this->repoUtils->setChildrenIndex($childrenIndex); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::getChildrenIndex + */ + public function getChildrenIndex() + { + return $this->repoUtils->getChildrenIndex(); + } + + /** + * {@inheritDoc} + */ + public function buildTreeArray(array $nodes) + { + return $this->repoUtils->buildTreeArray($nodes); + } + + /** + * Checks if current repository is right + * for currently used tree strategy + * + * @return bool + */ + abstract protected function validate(); + + /** + * Get all root nodes query builder + * + * @param string - Sort by field + * @param string - Sort direction ("asc" or "desc") + * + * @return \Doctrine\MongoDB\Query\Builder - QueryBuilder object + */ + abstract public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc'); + + /** + * Get all root nodes query + * + * @param string - Sort by field + * @param string - Sort direction ("asc" or "desc") + * + * @return \Doctrine\MongoDB\Query\Query - Query object + */ + abstract public function getRootNodesQuery($sortByField = null, $direction = 'asc'); + + /** + * Returns a QueryBuilder configured to return an array of nodes suitable for buildTree method + * + * @param object $node - Root node + * @param bool $direct - Obtain direct children? + * @param array $options - Options + * @param boolean $includeNode - Include node in results? + * + * @return \Doctrine\MongoDB\Query\Builder - QueryBuilder object + */ + abstract public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = array(), $includeNode = false); + + /** + * Returns a Query configured to return an array of nodes suitable for buildTree method + * + * @param object $node - Root node + * @param bool $direct - Obtain direct children? + * @param array $options - Options + * @param boolean $includeNode - Include node in results? + * + * @return \Doctrine\MongoDB\Query\Query - Query object + */ + abstract public function getNodesHierarchyQuery($node = null, $direct = false, array $options = array(), $includeNode = false); + + /** + * Get list of children followed by given $node. This returns a QueryBuilder object + * + * @param object $node - if null, all tree nodes will be taken + * @param boolean $direct - true to take only direct children + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param bool $includeNode - Include the root node in results? + * + * @return \Doctrine\MongoDB\Query\Builder - QueryBuilder object + */ + abstract public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false); + + /** + * Get list of children followed by given $node. This returns a Query + * + * @param object $node - if null, all tree nodes will be taken + * @param boolean $direct - true to take only direct children + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param bool $includeNode - Include the root node in results? + * + * @return \Doctrine\MongoDB\Query\Query - Query object + */ + abstract public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/MaterializedPathRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/MaterializedPathRepository.php new file mode 100644 index 0000000..c4d8059 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Document/MongoDB/Repository/MaterializedPathRepository.php @@ -0,0 +1,205 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class MaterializedPathRepository extends AbstractTreeRepository +{ + /** + * Get tree query builder + * + * @param object $rootNode + * + * @return \Doctrine\ODM\MongoDB\Query\Builder + */ + public function getTreeQueryBuilder($rootNode = null) + { + return $this->getChildrenQueryBuilder($rootNode, false, null, 'asc', true); + } + + /** + * Get tree query + * + * @param object $rootNode + * + * @return \Doctrine\ODM\MongoDB\Query\Query + */ + public function getTreeQuery($rootNode = null) + { + return $this->getTreeQueryBuilder($rootNode)->getQuery(); + } + + /** + * Get tree + * + * @param object $rootNode + * + * @return \Doctrine\ODM\MongoDB\Cursor + */ + public function getTree($rootNode = null) + { + return $this->getTreeQuery($rootNode)->execute(); + } + + /** + * {@inheritDoc} + */ + public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc') + { + return $this->getChildrenQueryBuilder(null, true, $sortByField, $direction); + } + + /** + * {@inheritDoc} + */ + public function getRootNodesQuery($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQueryBuilder($sortByField, $direction)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getRootNodes($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQuery($sortByField, $direction)->execute(); + } + + /** + * {@inheritDoc} + */ + public function childCount($node = null, $direct = false) + { + $meta = $this->getClassMetadata(); + + if (is_object($node)) { + if (!($node instanceof $meta->name)) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + + $wrapped = new MongoDocumentWrapper($node, $this->dm); + + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + } + + $qb = $this->getChildrenQueryBuilder($node, $direct); + + $qb->count(); + + return (int) $qb->getQuery()->execute(); + } + + /** + * {@inheritDoc} + */ + public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'asc', $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->dm, $meta->name); + $separator = preg_quote($config['path_separator']); + $qb = $this->dm->createQueryBuilder() + ->find($meta->name); + $regex = false; + + if (is_object($node) && $node instanceof $meta->name) { + $node = new MongoDocumentWrapper($node, $this->dm); + $nodePath = preg_quote($node->getPropertyValue($config['path'])); + + if ($direct) { + $regex = sprintf('/^%s([^%s]+%s)'.($includeNode ? '?' : '').'$/', + $nodePath, + $separator, + $separator); + } else { + $regex = sprintf('/^%s(.+)'.($includeNode ? '?' : '').'/', + $nodePath); + } + } elseif ($direct) { + $regex = sprintf('/^([^%s]+)'.($includeNode ? '?' : '').'%s$/', + $separator, + $separator); + } + + if ($regex) { + $qb->field($config['path'])->equals(new \MongoRegex($regex)); + } + + $qb->sort(is_null($sortByField) ? $config['path'] : $sortByField, $direction === 'asc' ? 'asc' : 'desc'); + + return $qb; + } + + /** + * G{@inheritDoc} + */ + public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'asc', $includeNode = false) + { + return $this->getChildrenQueryBuilder($node, $direct, $sortByField, $direction, $includeNode)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getChildren($node = null, $direct = false, $sortByField = null, $direction = 'asc', $includeNode = false) + { + return $this->getChildrenQuery($node, $direct, $sortByField, $direction, $includeNode)->execute(); + } + + /** + * {@inheritDoc} + */ + public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $sortBy = array( + 'field' => null, + 'dir' => 'asc', + ); + + if (isset($options['childSort'])) { + $sortBy = array_merge($sortBy, $options['childSort']); + } + + return $this->getChildrenQueryBuilder($node, $direct, $sortBy['field'], $sortBy['dir'], $includeNode); + } + + /** + * {@inheritDoc} + */ + public function getNodesHierarchyQuery($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->getNodesHierarchyQueryBuilder($node, $direct, $options, $includeNode)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getNodesHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $query = $this->getNodesHierarchyQuery($node, $direct, $options, $includeNode); + $query->setHydrate(false); + + return $query->toArray(); + } + + /** + * {@inheritdoc} + */ + protected function validate() + { + return $this->listener->getStrategy($this->dm, $this->getClassMetadata()->name)->getName() === Strategy::MATERIALIZED_PATH; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass/AbstractClosure.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass/AbstractClosure.php new file mode 100644 index 0000000..ef20c96 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/MappedSuperclass/AbstractClosure.php @@ -0,0 +1,117 @@ +id; + } + + /** + * Set ancestor + * + * @param object $ancestor + * + * @return static + */ + public function setAncestor($ancestor) + { + $this->ancestor = $ancestor; + + return $this; + } + + /** + * Get ancestor + * + * @return object + */ + public function getAncestor() + { + return $this->ancestor; + } + + /** + * Set descendant + * + * @param object $descendant + * + * @return static + */ + public function setDescendant($descendant) + { + $this->descendant = $descendant; + + return $this; + } + + /** + * Get descendant + * + * @return object + */ + public function getDescendant() + { + return $this->descendant; + } + + /** + * Set depth + * + * @param integer $depth + * + * @return static + */ + public function setDepth($depth) + { + $this->depth = $depth; + + return $this; + } + + /** + * Get depth + * + * @return integer + */ + public function getDepth() + { + return $this->depth; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/AbstractTreeRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/AbstractTreeRepository.php new file mode 100644 index 0000000..35e9715 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/AbstractTreeRepository.php @@ -0,0 +1,248 @@ +getEventManager()->getListeners() as $listeners) { + foreach ($listeners as $listener) { + if ($listener instanceof TreeListener) { + $treeListener = $listener; + break; + } + } + if ($treeListener) { + break; + } + } + + if (is_null($treeListener)) { + throw new \Gedmo\Exception\InvalidMappingException('Tree listener was not found on your entity manager, it must be hooked into the event manager'); + } + + $this->listener = $treeListener; + if (!$this->validate()) { + throw new \Gedmo\Exception\InvalidMappingException('This repository cannot be used for tree type: '.$treeListener->getStrategy($em, $class->name)->getName()); + } + + $this->repoUtils = new RepositoryUtils($this->_em, $this->getClassMetadata(), $this->listener, $this); + } + + /** + * @return \Doctrine\ORM\QueryBuilder + */ + protected function getQueryBuilder() + { + return $this->getEntityManager()->createQueryBuilder(); + } + + /** + * Sets the RepositoryUtilsInterface instance + * + * @param \Gedmo\Tree\RepositoryUtilsInterface $repoUtils + * + * @return static + */ + public function setRepoUtils(RepositoryUtilsInterface $repoUtils) + { + $this->repoUtils = $repoUtils; + + return $this; + } + + /** + * Returns the RepositoryUtilsInterface instance + * + * @return \Gedmo\Tree\RepositoryUtilsInterface|null + */ + public function getRepoUtils() + { + return $this->repoUtils; + } + + /** + * {@inheritDoc} + */ + public function childCount($node = null, $direct = false) + { + $meta = $this->getClassMetadata(); + + if (is_object($node)) { + if (!($node instanceof $meta->name)) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + + $wrapped = new EntityWrapper($node, $this->_em); + + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + } + + $qb = $this->getChildrenQueryBuilder($node, $direct); + + // We need to remove the ORDER BY DQL part since some vendors could throw an error + // in count queries + $dqlParts = $qb->getDQLParts(); + + // We need to check first if there's an ORDER BY DQL part, because resetDQLPart doesn't + // check if its internal array has an "orderby" index + if (isset($dqlParts['orderBy'])) { + $qb->resetDQLPart('orderBy'); + } + + $aliases = $qb->getRootAliases(); + $alias = $aliases[0]; + + $qb->select('COUNT('.$alias.')'); + + return (int) $qb->getQuery()->getSingleScalarResult(); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::childrenHierarchy + */ + public function childrenHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->repoUtils->childrenHierarchy($node, $direct, $options, $includeNode); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::buildTree + */ + public function buildTree(array $nodes, array $options = array()) + { + return $this->repoUtils->buildTree($nodes, $options); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::buildTreeArray + */ + public function buildTreeArray(array $nodes) + { + return $this->repoUtils->buildTreeArray($nodes); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::setChildrenIndex + */ + public function setChildrenIndex($childrenIndex) + { + $this->repoUtils->setChildrenIndex($childrenIndex); + } + + /** + * @see \Gedmo\Tree\RepositoryUtilsInterface::getChildrenIndex + */ + public function getChildrenIndex() + { + return $this->repoUtils->getChildrenIndex(); + } + + /** + * Checks if current repository is right + * for currently used tree strategy + * + * @return bool + */ + abstract protected function validate(); + + /** + * Get all root nodes query builder + * + * @param string - Sort by field + * @param string - Sort direction ("asc" or "desc") + * + * @return \Doctrine\ORM\QueryBuilder - QueryBuilder object + */ + abstract public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc'); + + /** + * Get all root nodes query + * + * @param string - Sort by field + * @param string - Sort direction ("asc" or "desc") + * + * @return \Doctrine\ORM\Query - Query object + */ + abstract public function getRootNodesQuery($sortByField = null, $direction = 'asc'); + + /** + * Returns a QueryBuilder configured to return an array of nodes suitable for buildTree method + * + * @param object $node - Root node + * @param bool $direct - Obtain direct children? + * @param array $options - Options + * @param boolean $includeNode - Include node in results? + * + * @return \Doctrine\ORM\QueryBuilder - QueryBuilder object + */ + abstract public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = array(), $includeNode = false); + + /** + * Returns a Query configured to return an array of nodes suitable for buildTree method + * + * @param object $node - Root node + * @param bool $direct - Obtain direct children? + * @param array $options - Options + * @param boolean $includeNode - Include node in results? + * + * @return \Doctrine\ORM\Query - Query object + */ + abstract public function getNodesHierarchyQuery($node = null, $direct = false, array $options = array(), $includeNode = false); + + /** + * Get list of children followed by given $node. This returns a QueryBuilder object + * + * @param object $node - if null, all tree nodes will be taken + * @param boolean $direct - true to take only direct children + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param bool $includeNode - Include the root node in results? + * + * @return \Doctrine\ORM\QueryBuilder - QueryBuilder object + */ + abstract public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false); + + /** + * Get list of children followed by given $node. This returns a Query + * + * @param object $node - if null, all tree nodes will be taken + * @param boolean $direct - true to take only direct children + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param bool $includeNode - Include the root node in results? + * + * @return \Doctrine\ORM\Query - Query object + */ + abstract public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/ClosureTreeRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/ClosureTreeRepository.php new file mode 100644 index 0000000..bf3c1e8 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/ClosureTreeRepository.php @@ -0,0 +1,605 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class ClosureTreeRepository extends AbstractTreeRepository +{ + /** Alias for the level value used in the subquery of the getNodesHierarchy method */ + const SUBQUERY_LEVEL = 'level'; + + /** + * {@inheritDoc} + */ + public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc') + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where('node.'.$config['parent']." IS NULL"); + + if ($sortByField) { + $qb->orderBy('node.'.$sortByField, strtolower($direction) === 'asc' ? 'asc' : 'desc'); + } + + return $qb; + } + + /** + * {@inheritDoc} + */ + public function getRootNodesQuery($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQueryBuilder($sortByField, $direction)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getRootNodes($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQuery($sortByField, $direction)->getResult(); + } + + /** + * Get the Tree path query by given $node + * + * @param object $node + * + * @throws InvalidArgumentException - if input is not valid + * + * @return Query + */ + public function getPathQuery($node) + { + $meta = $this->getClassMetadata(); + if (!$node instanceof $meta->name) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $closureMeta = $this->_em->getClassMetadata($config['closure']); + + $dql = "SELECT c, node FROM {$closureMeta->name} c"; + $dql .= " INNER JOIN c.ancestor node"; + $dql .= " WHERE c.descendant = :node"; + $dql .= " ORDER BY c.depth DESC"; + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('node')); + + return $q; + } + + /** + * Get the Tree path of Nodes by given $node + * + * @param object $node + * + * @return array - list of Nodes in path + */ + public function getPath($node) + { + return array_map(function (AbstractClosure $closure) { + return $closure->getAncestor(); + }, $this->getPathQuery($node)->getResult()); + } + + /** + * @see getChildrenQueryBuilder + */ + public function childrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + + $qb = $this->getQueryBuilder(); + if ($node !== null) { + if ($node instanceof $meta->name) { + if (!$this->_em->getUnitOfWork()->isInIdentityMap($node)) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + + $where = 'c.ancestor = :node AND '; + + $qb->select('c, node') + ->from($config['closure'], 'c') + ->innerJoin('c.descendant', 'node'); + + if ($direct) { + $where .= 'c.depth = 1'; + } else { + $where .= 'c.descendant <> :node'; + } + + $qb->where($where); + + if ($includeNode) { + $qb->orWhere('c.ancestor = :node AND c.descendant = :node'); + } + } else { + throw new \InvalidArgumentException("Node is not related to this repository"); + } + } else { + $qb->select('node') + ->from($config['useObjectClass'], 'node'); + if ($direct) { + $qb->where('node.'.$config['parent'].' IS NULL'); + } + } + + if ($sortByField) { + if ($meta->hasField($sortByField) && in_array(strtolower($direction), array('asc', 'desc'))) { + $qb->orderBy('node.'.$sortByField, $direction); + } else { + throw new InvalidArgumentException("Invalid sort options specified: field - {$sortByField}, direction - {$direction}"); + } + } + + if ($node) { + $qb->setParameter('node', $node); + } + + return $qb; + } + + /** + * @see getChildrenQuery + */ + public function childrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->childrenQueryBuilder($node, $direct, $sortByField, $direction, $includeNode)->getQuery(); + } + + /** + * @see getChildren + */ + public function children($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + $result = $this->childrenQuery($node, $direct, $sortByField, $direction, $includeNode)->getResult(); + if ($node) { + $result = array_map(function (AbstractClosure $closure) { + return $closure->getDescendant(); + }, $result); + } + + return $result; + } + + /** + * {@inheritDoc} + */ + public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->childrenQueryBuilder($node, $direct, $sortByField, $direction, $includeNode); + } + + /** + * {@inheritDoc} + */ + public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->childrenQuery($node, $direct, $sortByField, $direction, $includeNode); + } + + /** + * {@inheritDoc} + */ + public function getChildren($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->children($node, $direct, $sortByField, $direction, $includeNode); + } + + /** + * Removes given $node from the tree and reparents its descendants + * + * @todo may be improved, to issue single query on reparenting + * + * @param object $node + * + * @throws \Gedmo\Exception\InvalidArgumentException + * @throws \Gedmo\Exception\RuntimeException - if something fails in transaction + */ + public function removeFromTree($node) + { + $meta = $this->getClassMetadata(); + if (!$node instanceof $meta->name) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + $wrapped = new EntityWrapper($node, $this->_em); + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $pk = $meta->getSingleIdentifierFieldName(); + $nodeId = $wrapped->getIdentifier(); + $parent = $wrapped->getPropertyValue($config['parent']); + + $dql = "SELECT node FROM {$config['useObjectClass']} node"; + $dql .= " WHERE node.{$config['parent']} = :node"; + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('node')); + $nodesToReparent = $q->getResult(); + // process updates in transaction + $this->_em->getConnection()->beginTransaction(); + try { + foreach ($nodesToReparent as $nodeToReparent) { + $id = $meta->getReflectionProperty($pk)->getValue($nodeToReparent); + $meta->getReflectionProperty($config['parent'])->setValue($nodeToReparent, $parent); + + $dql = "UPDATE {$config['useObjectClass']} node"; + $dql .= " SET node.{$config['parent']} = :parent"; + $dql .= " WHERE node.{$pk} = :id"; + + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('parent', 'id')); + $q->getSingleScalarResult(); + + $this->listener + ->getStrategy($this->_em, $meta->name) + ->updateNode($this->_em, $nodeToReparent, $node); + + $oid = spl_object_hash($nodeToReparent); + $this->_em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['parent'], $parent); + } + + $dql = "DELETE {$config['useObjectClass']} node"; + $dql .= " WHERE node.{$pk} = :nodeId"; + + $q = $this->_em->createQuery($dql); + $q->setParameters(compact('nodeId')); + $q->getSingleScalarResult(); + $this->_em->getConnection()->commit(); + } catch (\Exception $e) { + $this->_em->close(); + $this->_em->getConnection()->rollback(); + throw new \Gedmo\Exception\RuntimeException('Transaction failed: '.$e->getMessage(), null, $e); + } + // remove from identity map + $this->_em->getUnitOfWork()->removeFromIdentityMap($node); + $node = null; + } + + /** + * Process nodes and produce an array with the + * structure of the tree + * + * @param array - Array of nodes + * + * @return array - Array with tree structure + */ + public function buildTreeArray(array $nodes) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $nestedTree = array(); + $idField = $meta->getSingleIdentifierFieldName(); + $hasLevelProp = !empty($config['level']); + $levelProp = $hasLevelProp ? $config['level'] : self::SUBQUERY_LEVEL; + $childrenIndex = $this->repoUtils->getChildrenIndex(); + + if (count($nodes) > 0) { + $firstLevel = $hasLevelProp ? $nodes[0][0]['descendant'][$levelProp] : $nodes[0][$levelProp]; + $l = 1; // 1 is only an initial value. We could have a tree which has a root node with any level (subtrees) + $refs = array(); + + foreach ($nodes as $n) { + $node = $n[0]['descendant']; + $node[$childrenIndex] = array(); + $level = $hasLevelProp ? $node[$levelProp] : $n[$levelProp]; + + if ($l < $level) { + $l = $level; + } + + if ($l == $firstLevel) { + $tmp = &$nestedTree; + } else { + $tmp = &$refs[$n['parent_id']][$childrenIndex]; + } + + $key = count($tmp); + $tmp[$key] = $node; + $refs[$node[$idField]] = &$tmp[$key]; + } + + unset($refs); + } + + return $nestedTree; + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->getNodesHierarchyQuery($node, $direct, $options, $includeNode)->getArrayResult(); + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchyQuery($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->getNodesHierarchyQueryBuilder($node, $direct, $options, $includeNode)->getQuery(); + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $idField = $meta->getSingleIdentifierFieldName(); + $subQuery = ''; + $hasLevelProp = isset($config['level']) && $config['level']; + + if (!$hasLevelProp) { + $subQuery = ', (SELECT MAX(c2.depth) + 1 FROM '.$config['closure']; + $subQuery .= ' c2 WHERE c2.descendant = c.descendant GROUP BY c2.descendant) AS '.self::SUBQUERY_LEVEL; + } + + $q = $this->_em->createQueryBuilder() + ->select('c, node, p.'.$idField.' AS parent_id'.$subQuery) + ->from($config['closure'], 'c') + ->innerJoin('c.descendant', 'node') + ->leftJoin('node.parent', 'p') + ->addOrderBy(($hasLevelProp ? 'node.'.$config['level'] : self::SUBQUERY_LEVEL), 'asc'); + + if ($node !== null) { + $q->where('c.ancestor = :node'); + $q->setParameters(compact('node')); + } else { + $q->groupBy('c.descendant'); + } + + if (!$includeNode) { + $q->andWhere('c.ancestor != c.descendant'); + } + + $defaultOptions = array(); + $options = array_merge($defaultOptions, $options); + + if (isset($options['childSort']) && is_array($options['childSort']) && + isset($options['childSort']['field']) && isset($options['childSort']['dir'])) { + $q->addOrderBy( + 'node.'.$options['childSort']['field'], + strtolower($options['childSort']['dir']) == 'asc' ? 'asc' : 'desc' + ); + } + + return $q; + } + + /** + * {@inheritdoc} + */ + protected function validate() + { + return $this->listener->getStrategy($this->_em, $this->getClassMetadata()->name)->getName() === Strategy::CLOSURE; + } + + public function verify() + { + $nodeMeta = $this->getClassMetadata(); + $nodeIdField = $nodeMeta->getSingleIdentifierFieldName(); + $config = $this->listener->getConfiguration($this->_em, $nodeMeta->name); + $closureMeta = $this->_em->getClassMetadata($config['closure']); + $errors = array(); + + $q = $this->_em->createQuery(" + SELECT COUNT(node) + FROM {$nodeMeta->name} AS node + LEFT JOIN {$closureMeta->name} AS c WITH c.ancestor = node AND c.depth = 0 + WHERE c.id IS NULL + "); + + if ($missingSelfRefsCount = intval($q->getSingleScalarResult())) { + $errors[] = "Missing $missingSelfRefsCount self referencing closures"; + } + + $q = $this->_em->createQuery(" + SELECT COUNT(node) + FROM {$nodeMeta->name} AS node + INNER JOIN {$closureMeta->name} AS c1 WITH c1.descendant = node.{$config['parent']} + LEFT JOIN {$closureMeta->name} AS c2 WITH c2.descendant = node.$nodeIdField AND c2.ancestor = c1.ancestor + WHERE c2.id IS NULL AND node.$nodeIdField <> c1.ancestor + "); + + if ($missingClosuresCount = intval($q->getSingleScalarResult())) { + $errors[] = "Missing $missingClosuresCount closures"; + } + + $q = $this->_em->createQuery(" + SELECT COUNT(c1.id) + FROM {$closureMeta->name} AS c1 + LEFT JOIN {$nodeMeta->name} AS node WITH c1.descendant = node.$nodeIdField + LEFT JOIN {$closureMeta->name} AS c2 WITH c2.descendant = node.{$config['parent']} AND c2.ancestor = c1.ancestor + WHERE c2.id IS NULL AND c1.descendant <> c1.ancestor + "); + + if ($invalidClosuresCount = intval($q->getSingleScalarResult())) { + $errors[] = "Found $invalidClosuresCount invalid closures"; + } + + if (!empty($config['level'])) { + $levelField = $config['level']; + $maxResults = 1000; + $q = $this->_em->createQuery(" + SELECT node.$nodeIdField AS id, node.$levelField AS node_level, MAX(c.depth) AS closure_level + FROM {$nodeMeta->name} AS node + INNER JOIN {$closureMeta->name} AS c WITH c.descendant = node.$nodeIdField + GROUP BY node.$nodeIdField, node.$levelField + HAVING node.$levelField IS NULL OR node.$levelField <> MAX(c.depth) + 1 + ")->setMaxResults($maxResults); + + if ($invalidLevelsCount = count($q->getScalarResult())) { + $errors[] = "Found $invalidLevelsCount invalid level values"; + } + } + + return $errors ?: true; + } + + public function recover() + { + if ($this->verify() === true) { + return; + } + + $this->cleanUpClosure(); + $this->rebuildClosure(); + } + + public function rebuildClosure() + { + $nodeMeta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $nodeMeta->name); + $closureMeta = $this->_em->getClassMetadata($config['closure']); + + $insertClosures = function ($entries) use ($closureMeta) { + $closureTable = $closureMeta->getTableName(); + $ancestorColumnName = $this->getJoinColumnFieldName($closureMeta->getAssociationMapping('ancestor')); + $descendantColumnName = $this->getJoinColumnFieldName($closureMeta->getAssociationMapping('descendant')); + $depthColumnName = $closureMeta->getColumnName('depth'); + + $conn = $this->_em->getConnection(); + $conn->beginTransaction(); + foreach ($entries as $entry) { + $conn->insert($closureTable, array_combine( + array($ancestorColumnName, $descendantColumnName, $depthColumnName), + $entry + )); + } + $conn->commit(); + }; + + $buildClosures = function ($dql) use ($insertClosures) { + $newClosuresCount = 0; + $batchSize = 1000; + $q = $this->_em->createQuery($dql)->setMaxResults($batchSize)->setCacheable(false); + do { + $entries = $q->getScalarResult(); + $insertClosures($entries); + $newClosuresCount += count($entries); + } while (count($entries) > 0); + return $newClosuresCount; + }; + + $nodeIdField = $nodeMeta->getSingleIdentifierFieldName(); + $newClosuresCount = $buildClosures(" + SELECT node.id AS ancestor, node.$nodeIdField AS descendant, 0 AS depth + FROM {$nodeMeta->name} AS node + LEFT JOIN {$closureMeta->name} AS c WITH c.ancestor = node AND c.depth = 0 + WHERE c.id IS NULL + "); + $newClosuresCount += $buildClosures(" + SELECT IDENTITY(c1.ancestor) AS ancestor, node.$nodeIdField AS descendant, c1.depth + 1 AS depth + FROM {$nodeMeta->name} AS node + INNER JOIN {$closureMeta->name} AS c1 WITH c1.descendant = node.{$config['parent']} + LEFT JOIN {$closureMeta->name} AS c2 WITH c2.descendant = node.$nodeIdField AND c2.ancestor = c1.ancestor + WHERE c2.id IS NULL AND node.$nodeIdField <> c1.ancestor + "); + + return $newClosuresCount; + } + + public function cleanUpClosure() + { + $conn = $this->_em->getConnection(); + $nodeMeta = $this->getClassMetadata(); + $nodeIdField = $nodeMeta->getSingleIdentifierFieldName(); + $config = $this->listener->getConfiguration($this->_em, $nodeMeta->name); + $closureMeta = $this->_em->getClassMetadata($config['closure']); + $closureTableName = $closureMeta->getTableName(); + + $dql = " + SELECT c1.id AS id + FROM {$closureMeta->name} AS c1 + LEFT JOIN {$nodeMeta->name} AS node WITH c1.descendant = node.$nodeIdField + LEFT JOIN {$closureMeta->name} AS c2 WITH c2.descendant = node.{$config['parent']} AND c2.ancestor = c1.ancestor + WHERE c2.id IS NULL AND c1.descendant <> c1.ancestor + "; + + $deletedClosuresCount = 0; + $batchSize = 1000; + $q = $this->_em->createQuery($dql)->setMaxResults($batchSize)->setCacheable(false); + + while (($ids = $q->getScalarResult()) && !empty($ids)) { + $ids = array_map(function ($el) { + return $el['id']; + }, $ids); + $query = "DELETE FROM {$closureTableName} WHERE id IN (".implode(', ', $ids).")"; + if (!$conn->executeQuery($query)) { + throw new \RuntimeException('Failed to remove incorrect closures'); + } + $deletedClosuresCount += count($ids); + } + + return $deletedClosuresCount; + } + + public function updateLevelValues() + { + $nodeMeta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $nodeMeta->name); + $levelUpdatesCount = 0; + + if (!empty($config['level'])) { + $levelField = $config['level']; + $nodeIdField = $nodeMeta->getSingleIdentifierFieldName(); + $closureMeta = $this->_em->getClassMetadata($config['closure']); + + $batchSize = 1000; + $q = $this->_em->createQuery(" + SELECT node.$nodeIdField AS id, node.$levelField AS node_level, MAX(c.depth) AS closure_level + FROM {$nodeMeta->name} AS node + INNER JOIN {$closureMeta->name} AS c WITH c.descendant = node.$nodeIdField + GROUP BY node.$nodeIdField, node.$levelField + HAVING node.$levelField IS NULL OR node.$levelField <> MAX(c.depth) + 1 + ")->setMaxResults($batchSize)->setCacheable(false); + do { + $entries = $q->getScalarResult(); + $this->_em->getConnection()->beginTransaction(); + foreach ($entries as $entry) { + unset($entry['node_level']); + $this->_em->createQuery(" + UPDATE {$nodeMeta->name} AS node SET node.$levelField = (:closure_level + 1) WHERE node.$nodeIdField = :id + ")->execute($entry); + } + $this->_em->getConnection()->commit(); + $levelUpdatesCount += count($entries); + } while (count($entries) > 0); + } + + return $levelUpdatesCount; + } + + protected function getJoinColumnFieldName($association) + { + if (count($association['joinColumnFieldNames']) > 1) { + throw new \RuntimeException('More association on field ' . $association['fieldName']); + } + + return array_shift($association['joinColumnFieldNames']); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/MaterializedPathRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/MaterializedPathRepository.php new file mode 100644 index 0000000..a0ea549 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/MaterializedPathRepository.php @@ -0,0 +1,286 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class MaterializedPathRepository extends AbstractTreeRepository +{ + /** + * Get tree query builder + * + * @param object $rootNode + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function getTreeQueryBuilder($rootNode = null) + { + return $this->getChildrenQueryBuilder($rootNode, false, null, 'asc', true); + } + + /** + * Get tree query + * + * @param object $rootNode + * + * @return \Doctrine\ORM\Query + */ + public function getTreeQuery($rootNode = null) + { + return $this->getTreeQueryBuilder($rootNode)->getQuery(); + } + + /** + * Get tree + * + * @param object $rootNode + * + * @return array + */ + public function getTree($rootNode = null) + { + return $this->getTreeQuery($rootNode)->execute(); + } + + /** + * {@inheritDoc} + */ + public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc') + { + return $this->getChildrenQueryBuilder(null, true, $sortByField, $direction); + } + + /** + * {@inheritDoc} + */ + public function getRootNodesQuery($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQueryBuilder($sortByField, $direction)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getRootNodes($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQuery($sortByField, $direction)->execute(); + } + + /** + * Get the Tree path query builder by given $node + * + * @param object $node + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function getPathQueryBuilder($node) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $alias = 'materialized_path_entity'; + $qb = $this->getQueryBuilder() + ->select($alias) + ->from($config['useObjectClass'], $alias); + + $node = new EntityWrapper($node, $this->_em); + $nodePath = $node->getPropertyValue($config['path']); + $paths = array(); + $nodePathLength = strlen($nodePath); + $separatorMatchOffset = 0; + while ($separatorMatchOffset < $nodePathLength) { + $separatorPos = strpos($nodePath, $config['path_separator'], $separatorMatchOffset); + + if ($separatorPos === false || $separatorPos === $nodePathLength - 1) { + // last node, done + $paths[] = $nodePath; + $separatorMatchOffset = $nodePathLength; + } elseif ($separatorPos === 0) { + // path starts with separator, continue + $separatorMatchOffset = 1; + } else { + // add node + $paths[] = substr($nodePath, 0, $config['path_ends_with_separator'] ? $separatorPos + 1 : $separatorPos); + $separatorMatchOffset = $separatorPos + 1; + } + } + $qb->where($qb->expr()->in( + $alias.'.'.$config['path'], + $paths + )); + $qb->orderBy($alias.'.'.$config['level'], 'ASC'); + + return $qb; + } + + /** + * Get the Tree path query by given $node + * + * @param object $node + * + * @return \Doctrine\ORM\Query + */ + public function getPathQuery($node) + { + return $this->getPathQueryBuilder($node)->getQuery(); + } + + /** + * Get the Tree path of Nodes by given $node + * + * @param object $node + * + * @return array - list of Nodes in path + */ + public function getPath($node) + { + return $this->getPathQuery($node)->getResult(); + } + + /** + * {@inheritDoc} + */ + public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'asc', $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $separator = addcslashes($config['path_separator'], '%'); + $alias = 'materialized_path_entity'; + $path = $config['path']; + $qb = $this->getQueryBuilder() + ->select($alias) + ->from($config['useObjectClass'], $alias); + $expr = ''; + $includeNodeExpr = ''; + + if (is_object($node) && $node instanceof $meta->name) { + $node = new EntityWrapper($node, $this->_em); + $nodePath = $node->getPropertyValue($path); + $expr = $qb->expr()->andx()->add( + $qb->expr()->like( + $alias.'.'.$path, + $qb->expr()->literal( + $nodePath + .($config['path_ends_with_separator'] ? '' : $separator).'%' + ) + ) + ); + + if ($includeNode) { + $includeNodeExpr = $qb->expr()->eq($alias.'.'.$path, $qb->expr()->literal($nodePath)); + } else { + $expr->add($qb->expr()->neq($alias.'.'.$path, $qb->expr()->literal($nodePath))); + } + + if ($direct) { + $expr->add( + $qb->expr()->orx( + $qb->expr()->eq($alias.'.'.$config['level'], $qb->expr()->literal($node->getPropertyValue($config['level']))), + $qb->expr()->eq($alias.'.'.$config['level'], $qb->expr()->literal($node->getPropertyValue($config['level']) + 1)) + ) + ); + } + } elseif ($direct) { + $expr = $qb->expr()->not( + $qb->expr()->like($alias.'.'.$path, + $qb->expr()->literal( + ($config['path_starts_with_separator'] ? $separator : '') + .'%'.$separator.'%' + .($config['path_ends_with_separator'] ? $separator : '') + ) + ) + ); + } + + if ($expr) { + $qb->where('('.$expr.')'); + } + + if ($includeNodeExpr) { + $qb->orWhere('('.$includeNodeExpr.')'); + } + + $orderByField = is_null($sortByField) ? $alias.'.'.$config['path'] : $alias.'.'.$sortByField; + $orderByDir = $direction === 'asc' ? 'asc' : 'desc'; + $qb->orderBy($orderByField, $orderByDir); + + return $qb; + } + + /** + * {@inheritDoc} + */ + public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'asc', $includeNode = false) + { + return $this->getChildrenQueryBuilder($node, $direct, $sortByField, $direction, $includeNode)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getChildren($node = null, $direct = false, $sortByField = null, $direction = 'asc', $includeNode = false) + { + return $this->getChildrenQuery($node, $direct, $sortByField, $direction, $includeNode)->execute(); + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $sortBy = array( + 'field' => null, + 'dir' => 'asc', + ); + + if (isset($options['childSort'])) { + $sortBy = array_merge($sortBy, $options['childSort']); + } + + return $this->getChildrenQueryBuilder($node, $direct, $sortBy['field'], $sortBy['dir'], $includeNode); + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchyQuery($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->getNodesHierarchyQueryBuilder($node, $direct, $options, $includeNode)->getQuery(); + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $path = $config['path']; + + $nodes = $this->getNodesHierarchyQuery($node, $direct, $options, $includeNode)->getArrayResult(); + usort( + $nodes, + function ($a, $b) use ($path) { + return strcmp($a[$path], $b[$path]); + } + ); + return $nodes; + } + + /** + * {@inheritdoc} + */ + protected function validate() + { + return $this->listener->getStrategy($this->_em, $this->getClassMetadata()->name)->getName() === Strategy::MATERIALIZED_PATH; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php new file mode 100644 index 0000000..0ab5ac0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity/Repository/NestedTreeRepository.php @@ -0,0 +1,1075 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + * @method persistAsFirstChild($node) + * @method persistAsFirstChildOf($node, $parent) + * @method persistAsLastChild($node) + * @method persistAsLastChildOf($node, $parent) + * @method persistAsNextSibling($node) + * @method persistAsNextSiblingOf($node, $sibling) + * @method persistAsPrevSibling($node) + * @method persistAsPrevSiblingOf($node, $sibling) + */ +class NestedTreeRepository extends AbstractTreeRepository +{ + /** + * {@inheritDoc} + */ + public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc') + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $qb = $this->getQueryBuilder(); + $qb + ->select('node') + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->isNull('node.'.$config['parent'])) + ; + + if ($sortByField !== null) { + $qb->orderBy('node.'.$sortByField, strtolower($direction) === 'asc' ? 'asc' : 'desc'); + } else { + $qb->orderBy('node.'.$config['left'], 'ASC'); + } + + return $qb; + } + + /** + * {@inheritDoc} + */ + public function getRootNodesQuery($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQueryBuilder($sortByField, $direction)->getQuery(); + } + + /** + * {@inheritDoc} + */ + public function getRootNodes($sortByField = null, $direction = 'asc') + { + return $this->getRootNodesQuery($sortByField, $direction)->getResult(); + } + + /** + * Allows the following 'virtual' methods: + * - persistAsFirstChild($node) + * - persistAsFirstChildOf($node, $parent) + * - persistAsLastChild($node) + * - persistAsLastChildOf($node, $parent) + * - persistAsNextSibling($node) + * - persistAsNextSiblingOf($node, $sibling) + * - persistAsPrevSibling($node) + * - persistAsPrevSiblingOf($node, $sibling) + * Inherited virtual methods: + * - find* + * + * @see \Doctrine\ORM\EntityRepository + * + * @throws InvalidArgumentException - If arguments are invalid + * @throws \BadMethodCallException - If the method called is an invalid find* or persistAs* method + * or no find* either persistAs* method at all and therefore an invalid method call. + * + * @return mixed - TreeNestedRepository if persistAs* is called + */ + public function __call($method, $args) + { + if (substr($method, 0, 9) === 'persistAs') { + if (!isset($args[0])) { + throw new \Gedmo\Exception\InvalidArgumentException('Node to persist must be available as first argument'); + } + $node = $args[0]; + $wrapped = new EntityWrapper($node, $this->_em); + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $position = substr($method, 9); + if (substr($method, -2) === 'Of') { + if (!isset($args[1])) { + throw new \Gedmo\Exception\InvalidArgumentException('If "Of" is specified you must provide parent or sibling as the second argument'); + } + $parentOrSibling = $args[1]; + if (strstr($method,'Sibling')) { + $wrappedParentOrSibling = new EntityWrapper($parentOrSibling, $this->_em); + $newParent = $wrappedParentOrSibling->getPropertyValue($config['parent']); + if (null === $newParent && isset($config['root'])) { + throw new UnexpectedValueException("Cannot persist sibling for a root node, tree operation is not possible"); + } + $node->sibling = $parentOrSibling; + $parentOrSibling = $newParent; + } + $wrapped->setPropertyValue($config['parent'], $parentOrSibling); + $position = substr($position, 0, -2); + } + $wrapped->setPropertyValue($config['left'], 0); // simulate changeset + $oid = spl_object_hash($node); + $this->listener + ->getStrategy($this->_em, $meta->name) + ->setNodePosition($oid, $position) + ; + + $this->_em->persist($node); + + return $this; + } + + return parent::__call($method, $args); + } + + /** + * Get the Tree path query builder by given $node + * + * @param object $node + * + * @throws InvalidArgumentException - if input is not valid + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function getPathQueryBuilder($node) + { + $meta = $this->getClassMetadata(); + if (!$node instanceof $meta->name) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $wrapped = new EntityWrapper($node, $this->_em); + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + $left = $wrapped->getPropertyValue($config['left']); + $right = $wrapped->getPropertyValue($config['right']); + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->lte('node.'.$config['left'], $left)) + ->andWhere($qb->expr()->gte('node.'.$config['right'], $right)) + ->orderBy('node.'.$config['left'], 'ASC') + ; + if (isset($config['root'])) { + $rootId = $wrapped->getPropertyValue($config['root']); + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + + return $qb; + } + + /** + * Get the Tree path query by given $node + * + * @param object $node + * + * @return \Doctrine\ORM\Query + */ + public function getPathQuery($node) + { + return $this->getPathQueryBuilder($node)->getQuery(); + } + + /** + * Get the Tree path of Nodes by given $node + * + * @param object $node + * + * @return array - list of Nodes in path + */ + public function getPath($node) + { + return $this->getPathQuery($node)->getResult(); + } + + /** + * @see getChildrenQueryBuilder + */ + public function childrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ; + if ($node !== null) { + if ($node instanceof $meta->name) { + $wrapped = new EntityWrapper($node, $this->_em); + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + if ($direct) { + $qb->where($qb->expr()->eq('node.'.$config['parent'], ':pid')); + $qb->setParameter('pid', $wrapped->getIdentifier()); + } else { + $left = $wrapped->getPropertyValue($config['left']); + $right = $wrapped->getPropertyValue($config['right']); + if ($left && $right) { + $qb->where($qb->expr()->lt('node.'.$config['right'], $right)); + $qb->andWhere($qb->expr()->gt('node.'.$config['left'], $left)); + } + } + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $wrapped->getPropertyValue($config['root'])); + } + if ($includeNode) { + $idField = $meta->getSingleIdentifierFieldName(); + $qb->where('('.$qb->getDqlPart('where').') OR node.'.$idField.' = :rootNode'); + $qb->setParameter('rootNode', $node); + } + } else { + throw new \InvalidArgumentException("Node is not related to this repository"); + } + } else { + if ($direct) { + $qb->where($qb->expr()->isNull('node.'.$config['parent'])); + } + } + if (!$sortByField) { + $qb->orderBy('node.'.$config['left'], 'ASC'); + } elseif (is_array($sortByField)) { + $fields = ''; + foreach ($sortByField as $field) { + $fields .= 'node.'.$field.','; + } + $fields = rtrim($fields, ','); + $qb->orderBy($fields, $direction); + } else { + if ($meta->hasField($sortByField) && in_array(strtolower($direction), array('asc', 'desc'))) { + $qb->orderBy('node.'.$sortByField, $direction); + } else { + throw new InvalidArgumentException("Invalid sort options specified: field - {$sortByField}, direction - {$direction}"); + } + } + + return $qb; + } + + /** + * @see getChildrenQuery + */ + public function childrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->childrenQueryBuilder($node, $direct, $sortByField, $direction, $includeNode)->getQuery(); + } + + /** + * @see getChildren + */ + public function children($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + $q = $this->childrenQuery($node, $direct, $sortByField, $direction, $includeNode); + + return $q->getResult(); + } + + /** + * {@inheritDoc} + */ + public function getChildrenQueryBuilder($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->childrenQueryBuilder($node, $direct, $sortByField, $direction, $includeNode); + } + + /** + * {@inheritDoc} + */ + public function getChildrenQuery($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->childrenQuery($node, $direct, $sortByField, $direction, $includeNode); + } + + /** + * {@inheritDoc} + */ + public function getChildren($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false) + { + return $this->children($node, $direct, $sortByField, $direction, $includeNode); + } + + /** + * Get tree leafs query builder + * + * @param object $root - root node in case of root tree is required + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * + * @throws InvalidArgumentException - if input is not valid + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function getLeafsQueryBuilder($root = null, $sortByField = null, $direction = 'ASC') + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + + if (isset($config['root']) && is_null($root)) { + if (is_null($root)) { + throw new InvalidArgumentException("If tree has root, getLeafs method requires any node of this tree"); + } + } + + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->eq('node.'.$config['right'], '1 + node.'.$config['left'])) + ; + if (isset($config['root'])) { + if ($root instanceof $meta->name) { + $wrapped = new EntityWrapper($root, $this->_em); + $rootId = $wrapped->getPropertyValue($config['root']); + if (!$rootId) { + throw new InvalidArgumentException("Root node must be managed"); + } + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } else { + throw new InvalidArgumentException("Node is not related to this repository"); + } + } + if (!$sortByField) { + if (isset($config['root'])) { + $qb->addOrderBy('node.'.$config['root'], 'ASC'); + } + $qb->addOrderBy('node.'.$config['left'], 'ASC', true); + } else { + if ($meta->hasField($sortByField) && in_array(strtolower($direction), array('asc', 'desc'))) { + $qb->orderBy('node.'.$sortByField, $direction); + } else { + throw new InvalidArgumentException("Invalid sort options specified: field - {$sortByField}, direction - {$direction}"); + } + } + + return $qb; + } + + /** + * Get tree leafs query + * + * @param object $root - root node in case of root tree is required + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * + * @return \Doctrine\ORM\Query + */ + public function getLeafsQuery($root = null, $sortByField = null, $direction = 'ASC') + { + return $this->getLeafsQueryBuilder($root, $sortByField, $direction)->getQuery(); + } + + /** + * Get list of leaf nodes of the tree + * + * @param object $root - root node in case of root tree is required + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * + * @return array + */ + public function getLeafs($root = null, $sortByField = null, $direction = 'ASC') + { + return $this->getLeafsQuery($root, $sortByField, $direction)->getResult(); + } + + /** + * Get the query builder for next siblings of the given $node + * + * @param object $node + * @param bool $includeSelf - include the node itself + * + * @throws \Gedmo\Exception\InvalidArgumentException - if input is invalid + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function getNextSiblingsQueryBuilder($node, $includeSelf = false) + { + $meta = $this->getClassMetadata(); + if (!$node instanceof $meta->name) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + $wrapped = new EntityWrapper($node, $this->_em); + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $parent = $wrapped->getPropertyValue($config['parent']); + + $left = $wrapped->getPropertyValue($config['left']); + + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where($includeSelf ? + $qb->expr()->gte('node.'.$config['left'], $left) : + $qb->expr()->gt('node.'.$config['left'], $left) + ) + ->orderBy("node.{$config['left']}", 'ASC') + ; + if ($parent) { + $wrappedParent = new EntityWrapper($parent, $this->_em); + $qb->andWhere($qb->expr()->eq('node.'.$config['parent'], ':pid')); + $qb->setParameter('pid', $wrappedParent->getIdentifier()); + } else if (isset($config['root']) && !$parent) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':root')); + $qb->andWhere($qb->expr()->isNull('node.parent')); + $method = $config['rootIdentifierMethod']; + $qb->setParameter('root', $node->$method()); + } else { + $qb->andWhere($qb->expr()->isNull('node.'.$config['parent'])); + } + + return $qb; + } + + /** + * Get the query for next siblings of the given $node + * + * @param object $node + * @param bool $includeSelf - include the node itself + * + * @return \Doctrine\ORM\Query + */ + public function getNextSiblingsQuery($node, $includeSelf = false) + { + return $this->getNextSiblingsQueryBuilder($node, $includeSelf)->getQuery(); + } + + /** + * Find the next siblings of the given $node + * + * @param object $node + * @param bool $includeSelf - include the node itself + * + * @return array + */ + public function getNextSiblings($node, $includeSelf = false) + { + return $this->getNextSiblingsQuery($node, $includeSelf)->getResult(); + } + + /** + * Get query builder for previous siblings of the given $node + * + * @param object $node + * @param bool $includeSelf - include the node itself + * + * @throws \Gedmo\Exception\InvalidArgumentException - if input is invalid + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function getPrevSiblingsQueryBuilder($node, $includeSelf = false) + { + $meta = $this->getClassMetadata(); + if (!$node instanceof $meta->name) { + throw new InvalidArgumentException("Node is not related to this repository"); + } + $wrapped = new EntityWrapper($node, $this->_em); + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $parent = $wrapped->getPropertyValue($config['parent']); + + $left = $wrapped->getPropertyValue($config['left']); + + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where($includeSelf ? + $qb->expr()->lte('node.'.$config['left'], $left) : + $qb->expr()->lt('node.'.$config['left'], $left) + ) + ->orderBy("node.{$config['left']}", 'ASC') + ; + if ($parent) { + $wrappedParent = new EntityWrapper($parent, $this->_em); + $qb->andWhere($qb->expr()->eq('node.'.$config['parent'], ':pid')); + $qb->setParameter('pid', $wrappedParent->getIdentifier()); + } else if (isset($config['root']) && !$parent) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':root')); + $qb->andWhere($qb->expr()->isNull('node.parent')); + $method = $config['rootIdentifierMethod']; + $qb->setParameter('root', $node->$method()); + } else { + $qb->andWhere($qb->expr()->isNull('node.'.$config['parent'])); + } + + return $qb; + } + + /** + * Get query for previous siblings of the given $node + * + * @param object $node + * @param bool $includeSelf - include the node itself + * + * @throws \Gedmo\Exception\InvalidArgumentException - if input is invalid + * + * @return \Doctrine\ORM\Query + */ + public function getPrevSiblingsQuery($node, $includeSelf = false) + { + return $this->getPrevSiblingsQueryBuilder($node, $includeSelf)->getQuery(); + } + + /** + * Find the previous siblings of the given $node + * + * @param object $node + * @param bool $includeSelf - include the node itself + * + * @return array + */ + public function getPrevSiblings($node, $includeSelf = false) + { + return $this->getPrevSiblingsQuery($node, $includeSelf)->getResult(); + } + + /** + * Move the node down in the same level + * + * @param object $node + * @param int|bool $number integer - number of positions to shift + * boolean - if "true" - shift till last position + * + * @throws \RuntimeException - if something fails in transaction + * + * @return boolean - true if shifted + */ + public function moveDown($node, $number = 1) + { + $result = false; + $meta = $this->getClassMetadata(); + if ($node instanceof $meta->name) { + $nextSiblings = $this->getNextSiblings($node); + if ($numSiblings = count($nextSiblings)) { + $result = true; + if ($number === true) { + $number = $numSiblings; + } elseif ($number > $numSiblings) { + $number = $numSiblings; + } + $this->listener + ->getStrategy($this->_em, $meta->name) + ->updateNode($this->_em, $node, $nextSiblings[$number - 1], Nested::NEXT_SIBLING); + } + } else { + throw new InvalidArgumentException("Node is not related to this repository"); + } + + return $result; + } + + /** + * Move the node up in the same level + * + * @param object $node + * @param int|bool $number integer - number of positions to shift + * boolean - true shift till first position + * + * @throws \RuntimeException - if something fails in transaction + * + * @return boolean - true if shifted + */ + public function moveUp($node, $number = 1) + { + $result = false; + $meta = $this->getClassMetadata(); + if ($node instanceof $meta->name) { + $prevSiblings = array_reverse($this->getPrevSiblings($node)); + if ($numSiblings = count($prevSiblings)) { + $result = true; + if ($number === true) { + $number = $numSiblings; + } elseif ($number > $numSiblings) { + $number = $numSiblings; + } + $this->listener + ->getStrategy($this->_em, $meta->name) + ->updateNode($this->_em, $node, $prevSiblings[$number - 1], Nested::PREV_SIBLING); + } + } else { + throw new InvalidArgumentException("Node is not related to this repository"); + } + + return $result; + } + + /** + * UNSAFE: be sure to backup before running this method when necessary + * + * Removes given $node from the tree and reparents its descendants + * + * @param object $node + * + * @throws \RuntimeException - if something fails in transaction + */ + public function removeFromTree($node) + { + $meta = $this->getClassMetadata(); + if ($node instanceof $meta->name) { + $wrapped = new EntityWrapper($node, $this->_em); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $right = $wrapped->getPropertyValue($config['right']); + $left = $wrapped->getPropertyValue($config['left']); + $rootId = isset($config['root']) ? $wrapped->getPropertyValue($config['root']) : null; + + if ($right == $left + 1) { + $this->removeSingle($wrapped); + $this->listener + ->getStrategy($this->_em, $meta->name) + ->shiftRL($this->_em, $config['useObjectClass'], $right, -2, $rootId); + + return; // node was a leaf + } + // process updates in transaction + $this->_em->getConnection()->beginTransaction(); + try { + $parent = $wrapped->getPropertyValue($config['parent']); + $parentId = null; + if ($parent) { + $wrappedParent = new EntityWrapper($parent, $this->_em); + $parentId = $wrappedParent->getIdentifier(); + } + $pk = $meta->getSingleIdentifierFieldName(); + $nodeId = $wrapped->getIdentifier(); + $shift = -1; + + // in case if root node is removed, children become roots + if (isset($config['root']) && !$parent) { + $qb = $this->getQueryBuilder(); + $qb->select('node.'.$pk, 'node.'.$config['left'], 'node.'.$config['right']) + ->from($config['useObjectClass'], 'node'); + + $qb->andWhere($qb->expr()->eq('node.'.$config['parent'], ':pid')); + $qb->setParameter('pid', $nodeId); + $nodes = $qb->getQuery()->getArrayResult(); + + foreach ($nodes as $newRoot) { + $left = $newRoot[$config['left']]; + $right = $newRoot[$config['right']]; + $rootId = $newRoot[$pk]; + $shift = -($left - 1); + + $qb = $this->getQueryBuilder(); + $qb->update($config['useObjectClass'], 'node'); + $qb->set('node.'.$config['root'], ':rid'); + $qb->setParameter('rid', $rootId); + $qb->where($qb->expr()->eq('node.'.$config['root'], ':rpid')); + $qb->setParameter('rpid', $nodeId); + $qb->andWhere($qb->expr()->gte('node.'.$config['left'], $left)); + $qb->andWhere($qb->expr()->lte('node.'.$config['right'], $right)); + $qb->getQuery()->getSingleScalarResult(); + + $qb = $this->getQueryBuilder(); + $qb->update($config['useObjectClass'], 'node'); + $qb->set('node.'.$config['parent'], ':pid'); + $qb->setParameter('pid', $parentId); + $qb->where($qb->expr()->eq('node.'.$config['parent'], ':rpid')); + $qb->setParameter('rpid', $nodeId); + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + $qb->getQuery()->getSingleScalarResult(); + + $this->listener + ->getStrategy($this->_em, $meta->name) + ->shiftRangeRL($this->_em, $config['useObjectClass'], $left, $right, $shift, $rootId, $rootId, - 1); + $this->listener + ->getStrategy($this->_em, $meta->name) + ->shiftRL($this->_em, $config['useObjectClass'], $right, -2, $rootId); + } + } else { + $qb = $this->getQueryBuilder(); + $qb->update($config['useObjectClass'], 'node'); + $qb->set('node.'.$config['parent'], ':pid'); + $qb->setParameter('pid', $parentId); + $qb->where($qb->expr()->eq('node.'.$config['parent'], ':rpid')); + $qb->setParameter('rpid', $nodeId); + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $qb->getQuery()->getSingleScalarResult(); + + $this->listener + ->getStrategy($this->_em, $meta->name) + ->shiftRangeRL($this->_em, $config['useObjectClass'], $left, $right, $shift, $rootId, $rootId, - 1); + + $this->listener + ->getStrategy($this->_em, $meta->name) + ->shiftRL($this->_em, $config['useObjectClass'], $right, -2, $rootId); + } + $this->removeSingle($wrapped); + $this->_em->getConnection()->commit(); + } catch (\Exception $e) { + $this->_em->close(); + $this->_em->getConnection()->rollback(); + throw new \Gedmo\Exception\RuntimeException('Transaction failed', null, $e); + } + } else { + throw new InvalidArgumentException("Node is not related to this repository"); + } + } + + /** + * Reorders $node's sibling nodes and child nodes, + * according to the $sortByField and $direction specified + * + * @param object|null $node - node from which to start reordering the tree; null will reorder everything + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param boolean $verify - true to verify tree first + * + * @return bool|null + */ + public function reorder($node, $sortByField = null, $direction = 'ASC', $verify = true) + { + $meta = $this->getClassMetadata(); + if ($node instanceof $meta->name || $node === null) { + $config = $this->listener->getConfiguration($this->_em, $meta->name); + if ($verify && is_array($this->verify())) { + return false; + } + + $nodes = $this->children($node, true, $sortByField, $direction); + foreach ($nodes as $node) { + $wrapped = new EntityWrapper($node, $this->_em); + $right = $wrapped->getPropertyValue($config['right']); + $left = $wrapped->getPropertyValue($config['left']); + $this->moveDown($node, true); + if ($left != ($right - 1)) { + $this->reorder($node, $sortByField, $direction, false); + } + } + } else { + throw new InvalidArgumentException("Node is not related to this repository"); + } + } + + /** + * Reorders all nodes in the tree according to the $sortByField and $direction specified. + * + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param boolean $verify - true to verify tree first + */ + public function reorderAll($sortByField = null, $direction = 'ASC', $verify = true) + { + $this->reorder(null, $sortByField, $direction, $verify); + } + + /** + * Verifies that current tree is valid. + * If any error is detected it will return an array + * with a list of errors found on tree + * + * @return array|bool - true on success,error list on failure + */ + public function verify() + { + if (!$this->childCount()) { + return true; // tree is empty + } + + $errors = array(); + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + if (isset($config['root'])) { + $trees = $this->getRootNodes(); + foreach ($trees as $tree) { + $this->verifyTree($errors, $tree); + } + } else { + $this->verifyTree($errors); + } + + return $errors ?: true; + } + + /** + * NOTE: flush your entity manager after + * + * Tries to recover the tree + * + * @return void + */ + public function recover() + { + if ($this->verify() === true) { + return; + } + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + $self = $this; + $em = $this->_em; + + $doRecover = function ($root, &$count, &$lvl) use ($meta, $config, $self, $em, &$doRecover) { + $lft = $count++; + foreach ($self->getChildren($root, true) as $child) { + $depth = ($lvl + 1); + $doRecover($child, $count, $depth); + } + $rgt = $count++; + $meta->getReflectionProperty($config['left'])->setValue($root, $lft); + $meta->getReflectionProperty($config['right'])->setValue($root, $rgt); + if (isset($config['level'])) { + $meta->getReflectionProperty($config['level'])->setValue($root, $lvl); + } + $em->persist($root); + }; + + if (isset($config['root'])) { + foreach ($this->getRootNodes() as $root) { + $count = 1; // reset on every root node + $lvl = 0; + $doRecover($root, $count, $lvl); + } + } else { + $count = 1; + $lvl = 0; + foreach ($this->getChildren(null, true) as $root) { + $doRecover($root, $count, $lvl); + } + } + } + + /** + * {@inheritDoc} + */ + public function getNodesHierarchyQueryBuilder($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + + return $this->childrenQueryBuilder( + $node, + $direct, + isset($config['root']) ? array($config['root'], $config['left']) : $config['left'], + 'ASC', + $includeNode + ); + } + + /** + * {@inheritDoc} + */ + public function getNodesHierarchyQuery($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->getNodesHierarchyQueryBuilder($node, $direct, $options, $includeNode)->getQuery(); + } + + /** + * {@inheritdoc} + */ + public function getNodesHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + return $this->getNodesHierarchyQuery($node, $direct, $options, $includeNode)->getArrayResult(); + } + + /** + * {@inheritdoc} + */ + protected function validate() + { + return $this->listener->getStrategy($this->_em, $this->getClassMetadata()->name)->getName() === Strategy::NESTED; + } + + /** + * Collect errors on given tree if + * where are any + * + * @param array $errors + * @param object $root + */ + private function verifyTree(&$errors, $root = null) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + + $identifier = $meta->getSingleIdentifierFieldName(); + if (isset($config['root'])) { + if (isset($config['root'])) { + $rootId = $meta->getReflectionProperty($config['root'])->getValue($root); + if (is_object($rootId)) { + $rootId = $meta->getReflectionProperty($identifier)->getValue($rootId); + } + } else { + $rootId = null; + } + } else { + $rootId = null; + } + + $qb = $this->getQueryBuilder(); + $qb->select($qb->expr()->min('node.'.$config['left'])) + ->from($config['useObjectClass'], 'node') + ; + if (isset($config['root'])) { + $qb->where($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $min = intval($qb->getQuery()->getSingleScalarResult()); + $edge = $this->listener->getStrategy($this->_em, $meta->name)->max($this->_em, $config['useObjectClass'], $rootId); + // check duplicate right and left values + for ($i = $min; $i <= $edge; $i++) { + $qb = $this->getQueryBuilder(); + $qb->select($qb->expr()->count('node.'.$identifier)) + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->orX( + $qb->expr()->eq('node.'.$config['left'], $i), + $qb->expr()->eq('node.'.$config['right'], $i) + )) + ; + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $count = intval($qb->getQuery()->getSingleScalarResult()); + if ($count !== 1) { + if ($count === 0) { + $errors[] = "index [{$i}], missing".($root ? ' on tree root: '.$rootId : ''); + } else { + $errors[] = "index [{$i}], duplicate".($root ? ' on tree root: '.$rootId : ''); + } + } + } + // check for missing parents + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->leftJoin('node.'.$config['parent'], 'parent') + ->where($qb->expr()->isNotNull('node.'.$config['parent'])) + ->andWhere($qb->expr()->isNull('parent.'.$identifier)) + ; + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $nodes = $qb->getQuery()->getArrayResult(); + if (count($nodes)) { + foreach ($nodes as $node) { + $errors[] = "node [{$node[$identifier]}] has missing parent".($root ? ' on tree root: '.$rootId : ''); + } + + return; // loading broken relation can cause infinite loop + } + + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->lt('node.'.$config['right'], 'node.'.$config['left'])) + ; + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $result = $qb->getQuery() + ->setMaxResults(1) + ->getResult(Query::HYDRATE_ARRAY); + $node = count($result) ? array_shift($result) : null; + + if ($node) { + $id = $node[$identifier]; + $errors[] = "node [{$id}], left is greater than right".($root ? ' on tree root: '.$rootId : ''); + } + + $qb = $this->getQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ; + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $nodes = $qb->getQuery()->getResult(Query::HYDRATE_OBJECT); + + foreach ($nodes as $node) { + $right = $meta->getReflectionProperty($config['right'])->getValue($node); + $left = $meta->getReflectionProperty($config['left'])->getValue($node); + $id = $meta->getReflectionProperty($identifier)->getValue($node); + $parent = $meta->getReflectionProperty($config['parent'])->getValue($node); + if (!$right || !$left) { + $errors[] = "node [{$id}] has invalid left or right values"; + } elseif ($right == $left) { + $errors[] = "node [{$id}] has identical left and right values"; + } elseif ($parent) { + if ($parent instanceof Proxy && !$parent->__isInitialized__) { + $this->_em->refresh($parent); + } + $parentRight = $meta->getReflectionProperty($config['right'])->getValue($parent); + $parentLeft = $meta->getReflectionProperty($config['left'])->getValue($parent); + $parentId = $meta->getReflectionProperty($identifier)->getValue($parent); + if ($left < $parentLeft) { + $errors[] = "node [{$id}] left is less than parent`s [{$parentId}] left value"; + } elseif ($right > $parentRight) { + $errors[] = "node [{$id}] right is greater than parent`s [{$parentId}] right value"; + } + } else { + $qb = $this->getQueryBuilder(); + $qb->select($qb->expr()->count('node.'.$identifier)) + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->lt('node.'.$config['left'], $left)) + ->andWhere($qb->expr()->gt('node.'.$config['right'], $right)) + ; + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + if ($count = intval($qb->getQuery()->getSingleScalarResult())) { + $errors[] = "node [{$id}] parent field is blank, but it has a parent"; + } + } + } + } + + /** + * Removes single node without touching children + * + * @internal + * + * @param EntityWrapper $wrapped + */ + private function removeSingle(EntityWrapper $wrapped) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->_em, $meta->name); + + $pk = $meta->getSingleIdentifierFieldName(); + $nodeId = $wrapped->getIdentifier(); + // prevent from deleting whole branch + $qb = $this->getQueryBuilder(); + $qb->update($config['useObjectClass'], 'node') + ->set('node.'.$config['left'], 0) + ->set('node.'.$config['right'], 0); + + $qb->andWhere($qb->expr()->eq('node.'.$pk, ':id')); + $qb->setParameter('id', $nodeId); + $qb->getQuery()->getSingleScalarResult(); + + // remove the node from database + $qb = $this->getQueryBuilder(); + $qb->delete($config['useObjectClass'], 'node'); + $qb->andWhere($qb->expr()->eq('node.'.$pk, ':id')); + $qb->setParameter('id', $nodeId); + $qb->getQuery()->getSingleScalarResult(); + + // remove from identity map + $this->_em->getUnitOfWork()->removeFromIdentityMap($wrapped->getObject()); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Hydrator/ORM/TreeObjectHydrator.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Hydrator/ORM/TreeObjectHydrator.php new file mode 100644 index 0000000..6bdd638 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Hydrator/ORM/TreeObjectHydrator.php @@ -0,0 +1,265 @@ + + * @link http://www.gediminasm.org + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TreeObjectHydrator extends ObjectHydrator +{ + /** + * @var array + */ + private $config; + + /** + * @var string + */ + private $idField; + + /** + * @var string + */ + private $parentField; + + /** + * @var string + */ + private $childrenField; + + /** + * We hook into the `hydrateAllData` to map the children collection of the entity + * + * {@inheritdoc} + */ + protected function hydrateAllData() + { + $data = parent::hydrateAllData(); + + if (count($data) === 0) { + return $data; + } + + $listener = $this->getTreeListener($this->_em); + $entityClass = $this->getEntityClassFromHydratedData($data); + $this->config = $listener->getConfiguration($this->_em, $entityClass); + $this->idField = $this->getIdField($entityClass); + $this->parentField = $this->getParentField(); + $this->childrenField = $this->getChildrenField($entityClass); + + + $childrenHashmap = $this->buildChildrenHashmap($data); + $this->populateChildrenArray($data, $childrenHashmap); + + // Only return root elements or elements who's parents haven't been fetched + // The sub-nodes will be accessible via the `children` property + return $this->getRootNodes($data); + } + + /** + * Creates a hashmap to quickly find the children of a node + * + * ``` + * [parentId => [child1, child2, ...], ...] + * ``` + * + * @param array $nodes + * @return array + */ + protected function buildChildrenHashmap($nodes) + { + $r = array(); + + foreach ($nodes as $node) { + $parentProxy = $this->getPropertyValue($node, $this->config['parent']); + $parentId = null; + + if ($parentProxy !== null) { + $parentId = $this->getPropertyValue($parentProxy, $this->idField); + } + + $r[$parentId][] = $node; + } + + return $r; + } + + /** + * @param array $nodes + * @param array $childrenHashmap + */ + protected function populateChildrenArray($nodes, $childrenHashmap) + { + foreach ($nodes as $node) { + $nodeId = $this->getPropertyValue($node, $this->idField); + $childrenCollection = $this->getPropertyValue($node, $this->childrenField); + + if ($childrenCollection === null) { + $childrenCollection = new ArrayCollection(); + $this->setPropertyValue($node, $this->childrenField, $childrenCollection); + } + + // Mark all children collections as initialized to avoid select queries + if ($childrenCollection instanceof AbstractLazyCollection) { + $childrenCollection->setInitialized(true); + } + + if (!isset($childrenHashmap[$nodeId])) { + continue; + } + + $childrenCollection->clear(); + + foreach ($childrenHashmap[$nodeId] as $child) { + $childrenCollection->add($child); + } + } + } + + /** + * @param array $nodes + * @return array + */ + protected function getRootNodes($nodes) + { + $idHashmap = $this->buildIdHashmap($nodes); + $rootNodes = array(); + + foreach ($nodes as $node) { + $parentProxy = $this->getPropertyValue($node, $this->config['parent']); + $parentId = null; + + if ($parentProxy !== null) { + $parentId = $this->getPropertyValue($parentProxy, $this->idField); + } + + if ($parentId === null || !key_exists($parentId, $idHashmap)) { + $rootNodes[] = $node; + } + } + + return $rootNodes; + } + + /** + * Creates a hashmap of all nodes returned in the query + * + * ``` + * [node1.id => true, node2.id => true, ...] + * ``` + * + * @param array $nodes + * @return array + */ + protected function buildIdHashmap(array $nodes) + { + $ids = array(); + + foreach ($nodes as $node) { + $id = $this->getPropertyValue($node, $this->idField); + $ids[$id] = true; + } + + return $ids; + } + + /** + * @return string + */ + protected function getIdField($entityClass) + { + $meta = $this->getClassMetadata($entityClass); + return $meta->getSingleIdentifierFieldName(); + } + + /** + * @return string + */ + protected function getParentField() + { + if (!isset($this->config['parent'])) { + throw new \Gedmo\Exception\InvalidMappingException('The `parent` property is required for the TreeHydrator to work'); + } + + return $this->config['parent']; + } + + /** + * @return string + */ + protected function getChildrenField($entityClass) + { + $meta = $this->getClassMetadata($entityClass); + + foreach ($meta->getReflectionProperties() as $property) { + + // Skip properties that have no association + if (!$meta->hasAssociation($property->getName())) { + continue; + } + + $associationMapping = $meta->getAssociationMapping($property->getName()); + + // Make sure the association is mapped by the parent property + if ($associationMapping['mappedBy'] !== $this->parentField) { + continue; + } + + return $associationMapping['fieldName']; + } + + throw new \Gedmo\Exception\InvalidMappingException('The children property could not found. It is identified through the `mappedBy` annotation to your parent property.'); + } + + /** + * @param EntityManagerInterface $em + * @return TreeListener + */ + protected function getTreeListener(EntityManagerInterface $em) + { + foreach ($em->getEventManager()->getListeners() as $listeners) { + foreach ($listeners as $listener) { + if ($listener instanceof TreeListener) { + return $listener; + } + } + } + + throw new \Gedmo\Exception\InvalidMappingException('Tree listener was not found on your entity manager, it must be hooked into the event manager'); + } + + /** + * @param array $data + * @return string + */ + protected function getEntityClassFromHydratedData($data) + { + $firstMappedEntity = array_values($data); + $firstMappedEntity = $firstMappedEntity[0]; + return $this->_em->getClassMetadata(get_class($firstMappedEntity))->rootEntityName; + } + + protected function getPropertyValue($object, $property) + { + $meta = $this->_em->getClassMetadata(get_class($object)); + return $meta->getReflectionProperty($property)->getValue($object); + } + + public function setPropertyValue($object, $property, $value) + { + $meta = $this->_em->getClassMetadata(get_class($object)); + $meta->getReflectionProperty($property)->setValue($object, $value); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..0a6b6da --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Annotation.php @@ -0,0 +1,252 @@ + + * @author + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to define the tree type + */ + const TREE = 'Gedmo\\Mapping\\Annotation\\Tree'; + + /** + * Annotation to mark field as one which will store left value + */ + const LEFT = 'Gedmo\\Mapping\\Annotation\\TreeLeft'; + + /** + * Annotation to mark field as one which will store right value + */ + const RIGHT = 'Gedmo\\Mapping\\Annotation\\TreeRight'; + + /** + * Annotation to mark relative parent field + */ + const PARENT = 'Gedmo\\Mapping\\Annotation\\TreeParent'; + + /** + * Annotation to mark node level + */ + const LEVEL = 'Gedmo\\Mapping\\Annotation\\TreeLevel'; + + /** + * Annotation to mark field as tree root + */ + const ROOT = 'Gedmo\\Mapping\\Annotation\\TreeRoot'; + + /** + * Annotation to specify closure tree class + */ + const CLOSURE = 'Gedmo\\Mapping\\Annotation\\TreeClosure'; + + /** + * Annotation to specify path class + */ + const PATH = 'Gedmo\\Mapping\\Annotation\\TreePath'; + + /** + * Annotation to specify path source class + */ + const PATH_SOURCE = 'Gedmo\\Mapping\\Annotation\\TreePathSource'; + + /** + * Annotation to specify path hash class + */ + const PATH_HASH = 'Gedmo\\Mapping\\Annotation\\TreePathHash'; + + /** + * Annotation to mark the field to be used to hold the lock time + */ + const LOCK_TIME = 'Gedmo\\Mapping\\Annotation\\TreeLockTime'; + + /** + * List of tree strategies available + * + * @var array + */ + protected $strategies = array( + 'nested', + 'closure', + 'materializedPath', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $validator = new Validator(); + $class = $this->getMetaReflectionClass($meta); + // class annotations + if ($annot = $this->reader->getClassAnnotation($class, self::TREE)) { + if (!in_array($annot->type, $this->strategies)) { + throw new InvalidMappingException("Tree type: {$annot->type} is not available."); + } + $config['strategy'] = $annot->type; + $config['activate_locking'] = $annot->activateLocking; + $config['locking_timeout'] = (int) $annot->lockingTimeout; + + if ($config['locking_timeout'] < 1) { + throw new InvalidMappingException("Tree Locking Timeout must be at least of 1 second."); + } + } + if ($annot = $this->reader->getClassAnnotation($class, self::CLOSURE)) { + if (!$cl = $this->getRelatedClassName($meta, $annot->class)) { + throw new InvalidMappingException("Tree closure class: {$annot->class} does not exist."); + } + $config['closure'] = $cl; + } + + // property annotations + foreach ($class->getProperties() as $property) { + if ($meta->isMappedSuperclass && !$property->isPrivate() || + $meta->isInheritedField($property->name) || + isset($meta->associationMappings[$property->name]['inherited']) + ) { + continue; + } + // left + if ($this->reader->getPropertyAnnotation($property, self::LEFT)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'left' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['left'] = $field; + } + // right + if ($this->reader->getPropertyAnnotation($property, self::RIGHT)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'right' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['right'] = $field; + } + // ancestor/parent + if ($this->reader->getPropertyAnnotation($property, self::PARENT)) { + $field = $property->getName(); + if (!$meta->isSingleValuedAssociation($field)) { + throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}"); + } + $config['parent'] = $field; + } + // root + if ($this->reader->getPropertyAnnotation($property, self::ROOT)) { + $field = $property->getName(); + if (!$meta->isSingleValuedAssociation($field)) { + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'root' - [{$field}] as mapped property in entity - {$meta->name}"); + } + + if (!$validator->isValidFieldForRoot($meta, $field)) { + throw new InvalidMappingException( + "Tree root field should be either a literal property ('integer' types or 'string') or a many-to-one association through root field - [{$field}] in class - {$meta->name}" + ); + } + } + $annotation = $this->reader->getPropertyAnnotation($property, self::ROOT); + $config['rootIdentifierMethod'] = $annotation->identifierMethod; + $config['root'] = $field; + } + // level + if ($this->reader->getPropertyAnnotation($property, self::LEVEL)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'level' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['level'] = $field; + } + // path + if ($pathAnnotation = $this->reader->getPropertyAnnotation($property, self::PATH)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'path' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidFieldForPath($meta, $field)) { + throw new InvalidMappingException("Tree Path field - [{$field}] type is not valid. It must be string or text in class - {$meta->name}"); + } + if (strlen($pathAnnotation->separator) > 1) { + throw new InvalidMappingException("Tree Path field - [{$field}] Separator {$pathAnnotation->separator} is invalid. It must be only one character long."); + } + $config['path'] = $field; + $config['path_separator'] = $pathAnnotation->separator; + $config['path_append_id'] = $pathAnnotation->appendId; + $config['path_starts_with_separator'] = $pathAnnotation->startsWithSeparator; + $config['path_ends_with_separator'] = $pathAnnotation->endsWithSeparator; + } + // path source + if ($this->reader->getPropertyAnnotation($property, self::PATH_SOURCE)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'path_source' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidFieldForPathSource($meta, $field)) { + throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}"); + } + $config['path_source'] = $field; + } + + // path hash + if ($this->reader->getPropertyAnnotation($property, self::PATH_HASH)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'path_hash' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidFieldForPathHash($meta, $field)) { + throw new InvalidMappingException("Tree PathHash field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}"); + } + $config['path_hash'] = $field; + } + // lock time + + if ($this->reader->getPropertyAnnotation($property, self::LOCK_TIME)) { + $field = $property->getName(); + if (!$meta->hasField($field)) { + throw new InvalidMappingException("Unable to find 'lock_time' - [{$field}] as mapped property in entity - {$meta->name}"); + } + if (!$validator->isValidFieldForLockTime($meta, $field)) { + throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It must be \"date\" in class - {$meta->name}"); + } + $config['lock_time'] = $field; + } + } + + if (isset($config['activate_locking']) && $config['activate_locking'] && !isset($config['lock_time'])) { + throw new InvalidMappingException("You need to map a date field as the tree lock time field to activate locking support."); + } + + if (!$meta->isMappedSuperclass && $config) { + if (isset($config['strategy'])) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}"); + } + $method = 'validate'.ucfirst($config['strategy']).'TreeMetadata'; + $validator->$method($meta, $config); + } else { + throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}"); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Xml.php new file mode 100644 index 0000000..2a55fd8 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Xml.php @@ -0,0 +1,267 @@ + + * @author Gediminas Morkevicius + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * List of tree strategies available + * + * @var array + */ + private $strategies = array( + 'nested', + 'closure', + 'materializedPath', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + $xmlDoctrine = $xml; + $xml = $xml->children(self::GEDMO_NAMESPACE_URI); + $validator = new Validator(); + + if (isset($xml->tree) && $this->_isAttributeSet($xml->tree, 'type')) { + $strategy = $this->_getAttribute($xml->tree, 'type'); + if (!in_array($strategy, $this->strategies)) { + throw new InvalidMappingException("Tree type: $strategy is not available."); + } + $config['strategy'] = $strategy; + $config['activate_locking'] = $this->_getAttribute($xml->tree, 'activate-locking') === 'true' ? true : false; + + if ($lockingTimeout = $this->_getAttribute($xml->tree, 'locking-timeout')) { + $config['locking_timeout'] = (int) $lockingTimeout; + + if ($config['locking_timeout'] < 1) { + throw new InvalidMappingException("Tree Locking Timeout must be at least of 1 second."); + } + } else { + $config['locking_timeout'] = 3; + } + } + if (isset($xml->{'tree-closure'}) && $this->_isAttributeSet($xml->{'tree-closure'}, 'class')) { + $class = $this->_getAttribute($xml->{'tree-closure'}, 'class'); + if (!$cl = $this->getRelatedClassName($meta, $class)) { + throw new InvalidMappingException("Tree closure class: {$class} does not exist."); + } + $config['closure'] = $cl; + } + if (isset($xmlDoctrine->field)) { + foreach ($xmlDoctrine->field as $mapping) { + $mappingDoctrine = $mapping; + $mapping = $mapping->children(self::GEDMO_NAMESPACE_URI); + + $field = $this->_getAttribute($mappingDoctrine, 'name'); + if (isset($mapping->{'tree-left'})) { + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['left'] = $field; + } elseif (isset($mapping->{'tree-right'})) { + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['right'] = $field; + } elseif (isset($mapping->{'tree-root'})) { + if (!$validator->isValidFieldForRoot($meta, $field)) { + throw new InvalidMappingException("Tree root field - [{$field}] type is not valid and must be any of the 'integer' types or 'string' in class - {$meta->name}"); + } + $config['root'] = $field; + } elseif (isset($mapping->{'tree-level'})) { + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['level'] = $field; + } elseif (isset($mapping->{'tree-path'})) { + if (!$validator->isValidFieldForPath($meta, $field)) { + throw new InvalidMappingException("Tree Path field - [{$field}] type is not valid. It must be string or text in class - {$meta->name}"); + } + + $separator = $this->_getAttribute($mapping->{'tree-path'}, 'separator'); + + if (strlen($separator) > 1) { + throw new InvalidMappingException("Tree Path field - [{$field}] Separator {$separator} is invalid. It must be only one character long."); + } + + $appendId = $this->_getAttribute($mapping->{'tree-path'}, 'append_id'); + + if (!$appendId) { + $appendId = true; + } else { + $appendId = strtolower($appendId) == 'false' ? false : true; + } + + $startsWithSeparator = $this->_getAttribute($mapping->{'tree-path'}, 'starts_with_separator'); + + if (!$startsWithSeparator) { + $startsWithSeparator = false; + } else { + $startsWithSeparator = strtolower($startsWithSeparator) == 'false' ? false : true; + } + + $endsWithSeparator = $this->_getAttribute($mapping->{'tree-path'}, 'ends_with_separator'); + + if (!$endsWithSeparator) { + $endsWithSeparator = true; + } else { + $endsWithSeparator = strtolower($endsWithSeparator) == 'false' ? false : true; + } + + $config['path'] = $field; + $config['path_separator'] = $separator; + $config['path_append_id'] = $appendId; + $config['path_starts_with_separator'] = $startsWithSeparator; + $config['path_ends_with_separator'] = $endsWithSeparator; + } elseif (isset($mapping->{'tree-path-source'})) { + if (!$validator->isValidFieldForPathSource($meta, $field)) { + throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}"); + } + $config['path_source'] = $field; + } elseif (isset($mapping->{'tree-lock-time'})) { + if (!$validator->isValidFieldForLockTime($meta, $field)) { + throw new InvalidMappingException("Tree LockTime field - [{$field}] type is not valid. It must be \"date\" in class - {$meta->name}"); + } + $config['lock_time'] = $field; + } + } + } + + if (isset($config['activate_locking']) && $config['activate_locking'] && !isset($config['lock_time'])) { + throw new InvalidMappingException("You need to map a date field as the tree lock time field to activate locking support."); + } + + if ($xmlDoctrine->getName() == 'mapped-superclass') { + if (isset($xmlDoctrine->{'many-to-one'})) { + foreach ($xmlDoctrine->{'many-to-one'} as $manyToOneMapping) { + /** + * @var \SimpleXMLElement $manyToOneMapping + */ + $manyToOneMappingDoctrine = $manyToOneMapping; + $manyToOneMapping = $manyToOneMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($manyToOneMapping->{'tree-parent'})) { + $field = $this->_getAttribute($manyToOneMappingDoctrine, 'field'); + $targetEntity = $meta->associationMappings[$field]['targetEntity']; + if (!$cl = $this->getRelatedClassName($meta, $targetEntity)) { + throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}"); + } + $config['parent'] = $field; + } + if (isset($manyToOneMapping->{'tree-root'})) { + $field = $this->_getAttribute($manyToOneMappingDoctrine, 'field'); + $targetEntity = $meta->associationMappings[$field]['targetEntity']; + if (!$cl = $this->getRelatedClassName($meta, $targetEntity)) { + throw new InvalidMappingException("Unable to find root descendant relation through root field - [{$field}] in class - {$meta->name}"); + } + $config['root'] = $field; + } + } + } elseif (isset($xmlDoctrine->{'reference-one'})) { + foreach ($xmlDoctrine->{'reference-one'} as $referenceOneMapping) { + /** + * @var \SimpleXMLElement $referenceOneMapping + */ + $referenceOneMappingDoctrine = $referenceOneMapping; + $referenceOneMapping = $referenceOneMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($referenceOneMapping->{'tree-parent'})) { + $field = $this->_getAttribute($referenceOneMappingDoctrine, 'field'); + if (!$cl = $this->getRelatedClassName($meta, $this->_getAttribute($referenceOneMappingDoctrine, 'target-document'))) { + throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}"); + } + $config['parent'] = $field; + } + if (isset($referenceOneMapping->{'tree-root'})) { + $field = $this->_getAttribute($referenceOneMappingDoctrine, 'field'); + if (!$cl = $this->getRelatedClassName($meta, $this->_getAttribute($referenceOneMappingDoctrine, 'target-document'))) { + throw new InvalidMappingException("Unable to find root descendant relation through root field - [{$field}] in class - {$meta->name}"); + } + $config['root'] = $field; + } + } + } + } elseif ($xmlDoctrine->getName() == 'entity') { + if (isset($xmlDoctrine->{'many-to-one'})) { + foreach ($xmlDoctrine->{'many-to-one'} as $manyToOneMapping) { + /** + * @var \SimpleXMLElement $manyToOneMapping + */ + $manyToOneMappingDoctrine = $manyToOneMapping; + $manyToOneMapping = $manyToOneMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($manyToOneMapping->{'tree-parent'})) { + $field = $this->_getAttribute($manyToOneMappingDoctrine, 'field'); + $targetEntity = $meta->associationMappings[$field]['targetEntity']; + if (!$cl = $this->getRelatedClassName($meta, $targetEntity)) { + throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}"); + } + $config['parent'] = $field; + } + if (isset($manyToOneMapping->{'tree-root'})) { + $field = $this->_getAttribute($manyToOneMappingDoctrine, 'field'); + $targetEntity = $meta->associationMappings[$field]['targetEntity']; + if (!$cl = $this->getRelatedClassName($meta, $targetEntity)) { + throw new InvalidMappingException("Unable to find root descendant relation through root field - [{$field}] in class - {$meta->name}"); + } + $config['root'] = $field; + } + } + } + } elseif ($xmlDoctrine->getName() == 'document') { + if (isset($xmlDoctrine->{'reference-one'})) { + foreach ($xmlDoctrine->{'reference-one'} as $referenceOneMapping) { + /** + * @var \SimpleXMLElement $referenceOneMapping + */ + $referenceOneMappingDoctrine = $referenceOneMapping; + $referenceOneMapping = $referenceOneMapping->children(self::GEDMO_NAMESPACE_URI); + if (isset($referenceOneMapping->{'tree-parent'})) { + $field = $this->_getAttribute($referenceOneMappingDoctrine, 'field'); + if (!$cl = $this->getRelatedClassName($meta, $this->_getAttribute($referenceOneMappingDoctrine, 'target-document'))) { + throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}"); + } + $config['parent'] = $field; + } + if (isset($referenceOneMapping->{'tree-root'})) { + $field = $this->_getAttribute($referenceOneMappingDoctrine, 'field'); + if (!$cl = $this->getRelatedClassName($meta, $this->_getAttribute($referenceOneMappingDoctrine, 'target-document'))) { + throw new InvalidMappingException("Unable to find root descendant relation through root field - [{$field}] in class - {$meta->name}"); + } + $config['root'] = $field; + } + } + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (isset($config['strategy'])) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}"); + } + $method = 'validate'.ucfirst($config['strategy']).'TreeMetadata'; + $validator->$method($meta, $config); + } else { + throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}"); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..8e7741d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Driver/Yaml.php @@ -0,0 +1,215 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * List of tree strategies available + * + * @var array + */ + private $strategies = array( + 'nested', + 'closure', + 'materializedPath', + ); + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + $validator = new Validator(); + + if (isset($mapping['gedmo'])) { + $classMapping = $mapping['gedmo']; + if (isset($classMapping['tree']['type'])) { + $strategy = $classMapping['tree']['type']; + if (!in_array($strategy, $this->strategies)) { + throw new InvalidMappingException("Tree type: $strategy is not available."); + } + $config['strategy'] = $strategy; + $config['activate_locking'] = isset($classMapping['tree']['activateLocking']) ? + $classMapping['tree']['activateLocking'] : false; + $config['locking_timeout'] = isset($classMapping['tree']['lockingTimeout']) ? + (int) $classMapping['tree']['lockingTimeout'] : 3; + + if ($config['locking_timeout'] < 1) { + throw new InvalidMappingException("Tree Locking Timeout must be at least of 1 second."); + } + } + if (isset($classMapping['tree']['closure'])) { + if (!$class = $this->getRelatedClassName($meta, $classMapping['tree']['closure'])) { + throw new InvalidMappingException("Tree closure class: {$classMapping['tree']['closure']} does not exist."); + } + $config['closure'] = $class; + } + } + + if (isset($mapping['id'])) { + foreach($mapping['id'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('treePathSource', $fieldMapping['gedmo'])) { + if (!$validator->isValidFieldForPathSource($meta, $field)) { + throw new InvalidMappingException( + "Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}" + ); + } + $config['path_source'] = $field; + } + } + } + } + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $fieldMapping) { + if (isset($fieldMapping['gedmo'])) { + if (in_array('treeLeft', $fieldMapping['gedmo'])) { + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree left field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['left'] = $field; + } elseif (in_array('treeRight', $fieldMapping['gedmo'])) { + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree right field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['right'] = $field; + } elseif (in_array('treeLevel', $fieldMapping['gedmo'])) { + if (!$validator->isValidField($meta, $field)) { + throw new InvalidMappingException("Tree level field - [{$field}] type is not valid and must be 'integer' in class - {$meta->name}"); + } + $config['level'] = $field; + } elseif (in_array('treeRoot', $fieldMapping['gedmo'])) { + if (!$validator->isValidFieldForRoot($meta, $field)) { + throw new InvalidMappingException("Tree root field - [{$field}] type is not valid and must be any of the 'integer' types or 'string' in class - {$meta->name}"); + } + $config['root'] = $field; + } elseif (in_array('treePath', $fieldMapping['gedmo']) || isset($fieldMapping['gedmo']['treePath'])) { + if (!$validator->isValidFieldForPath($meta, $field)) { + throw new InvalidMappingException("Tree Path field - [{$field}] type is not valid. It must be string or text in class - {$meta->name}"); + } + + $treePathInfo = isset($fieldMapping['gedmo']['treePath']) ? $fieldMapping['gedmo']['treePath'] : + $fieldMapping['gedmo'][array_search('treePath', $fieldMapping['gedmo'])]; + + if (is_array($treePathInfo) && isset($treePathInfo['separator'])) { + $separator = $treePathInfo['separator']; + } else { + $separator = '|'; + } + + if (strlen($separator) > 1) { + throw new InvalidMappingException("Tree Path field - [{$field}] Separator {$separator} is invalid. It must be only one character long."); + } + + if (is_array($treePathInfo) && isset($treePathInfo['appendId'])) { + $appendId = $treePathInfo['appendId']; + } else { + $appendId = null; + } + + if (is_array($treePathInfo) && isset($treePathInfo['startsWithSeparator'])) { + $startsWithSeparator = $treePathInfo['startsWithSeparator']; + } else { + $startsWithSeparator = false; + } + + if (is_array($treePathInfo) && isset($treePathInfo['endsWithSeparator'])) { + $endsWithSeparator = $treePathInfo['endsWithSeparator']; + } else { + $endsWithSeparator = true; + } + + $config['path'] = $field; + $config['path_separator'] = $separator; + $config['path_append_id'] = $appendId; + $config['path_starts_with_separator'] = $startsWithSeparator; + $config['path_ends_with_separator'] = $endsWithSeparator; + } elseif (in_array('treePathSource', $fieldMapping['gedmo'])) { + if (!$validator->isValidFieldForPathSource($meta, $field)) { + throw new InvalidMappingException("Tree PathSource field - [{$field}] type is not valid. It can be any of the integer variants, double, float or string in class - {$meta->name}"); + } + $config['path_source'] = $field; + } elseif (in_array('treePathHash', $fieldMapping['gedmo'])) { + if (!$validator->isValidFieldForPathSource($meta, $field)) { + throw new InvalidMappingException("Tree PathHash field - [{$field}] type is not valid and must be 'string' in class - {$meta->name}"); + } + $config['path_hash'] = $field; + } elseif (in_array('treeLockTime', $fieldMapping['gedmo'])) { + if (!$validator->isValidFieldForLocktime($meta, $field)) { + throw new InvalidMappingException("Tree LockTime field - [{$field}] type is not valid. It must be \"date\" in class - {$meta->name}"); + } + $config['lock_time'] = $field; + } elseif (in_array('treeParent', $fieldMapping['gedmo'])) { + $config['parent'] = $field; + } + } + } + } + + if (isset($config['activate_locking']) && $config['activate_locking'] && !isset($config['lock_time'])) { + throw new InvalidMappingException("You need to map a date|datetime|timestamp field as the tree lock time field to activate locking support."); + } + + if (isset($mapping['manyToOne'])) { + foreach ($mapping['manyToOne'] as $field => $relationMapping) { + if (isset($relationMapping['gedmo'])) { + if (in_array('treeParent', $relationMapping['gedmo'])) { + if (!$rel = $this->getRelatedClassName($meta, $relationMapping['targetEntity'])) { + throw new InvalidMappingException("Unable to find ancestor/parent child relation through ancestor field - [{$field}] in class - {$meta->name}"); + } + $config['parent'] = $field; + } + if (in_array('treeRoot', $relationMapping['gedmo'])) { + if (!$rel = $this->getRelatedClassName($meta, $relationMapping['targetEntity'])) { + throw new InvalidMappingException("Unable to find root-descendant relation through root field - [{$field}] in class - {$meta->name}"); + } + $config['root'] = $field; + } + } + } + } + + if (!$meta->isMappedSuperclass && $config) { + if (isset($config['strategy'])) { + if (is_array($meta->identifier) && count($meta->identifier) > 1) { + throw new InvalidMappingException("Tree does not support composite identifiers in class - {$meta->name}"); + } + $method = 'validate'.ucfirst($config['strategy']).'TreeMetadata'; + $validator->$method($meta, $config); + } else { + throw new InvalidMappingException("Cannot find Tree type for class: {$meta->name}"); + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ODM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ODM.php new file mode 100644 index 0000000..7ef605d --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ODM.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ODM extends BaseAdapterODM implements TreeAdapter +{ + // Nothing specific yet +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ORM.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ORM.php new file mode 100644 index 0000000..c0ebd63 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/Adapter/ORM.php @@ -0,0 +1,18 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +final class ORM extends BaseAdapterORM implements TreeAdapter +{ + // Nothing specific yet +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/TreeAdapter.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/TreeAdapter.php new file mode 100644 index 0000000..c3e4e98 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Event/TreeAdapter.php @@ -0,0 +1,16 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface TreeAdapter extends AdapterInterface +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Validator.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Validator.php new file mode 100644 index 0000000..23db3ba --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Mapping/Validator.php @@ -0,0 +1,240 @@ + + * @author Gediminas Morkevicius + * @author + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Validator +{ + /** + * List of types which are valid for tree fields + * + * @var array + */ + private $validTypes = array( + 'integer', + 'smallint', + 'bigint', + 'int', + ); + + /** + * List of types which are valid for the path (materialized path strategy) + * + * @var array + */ + private $validPathTypes = array( + 'string', + 'text', + ); + + /** + * List of types which are valid for the path source (materialized path strategy) + * + * @var array + */ + private $validPathSourceTypes = array( + 'id', + 'integer', + 'smallint', + 'bigint', + 'string', + 'int', + 'float', + ); + + /** + * List of types which are valid for the path hash (materialized path strategy) + * + * @var array + */ + private $validPathHashTypes = array( + 'string', + ); + + /** + * List of types which are valid for the path source (materialized path strategy) + * + * @var array + */ + private $validRootTypes = array( + 'integer', + 'smallint', + 'bigint', + 'int', + 'string', + 'guid', + ); + + /** + * Checks if $field type is valid + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + public function isValidField($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validTypes); + } + + /** + * Checks if $field type is valid for Path field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + public function isValidFieldForPath($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validPathTypes); + } + + /** + * Checks if $field type is valid for PathSource field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + public function isValidFieldForPathSource($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validPathSourceTypes); + } + + /** + * Checks if $field type is valid for PathHash field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + public function isValidFieldForPathHash($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validPathHashTypes); + } + + /** + * Checks if $field type is valid for LockTime field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + public function isValidFieldForLockTime($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && ($mapping['type'] === 'date' || $mapping['type'] === 'datetime' || $mapping['type'] === 'timestamp'); + } + + /** + * Checks if $field type is valid for Root field + * + * @param object $meta + * @param string $field + * + * @return boolean + */ + public function isValidFieldForRoot($meta, $field) + { + $mapping = $meta->getFieldMapping($field); + + return $mapping && in_array($mapping['type'], $this->validRootTypes); + } + + /** + * Validates metadata for nested type tree + * + * @param object $meta + * @param array $config + * + * @throws InvalidMappingException + */ + public function validateNestedTreeMetadata($meta, array $config) + { + $missingFields = array(); + if (!isset($config['parent'])) { + $missingFields[] = 'ancestor'; + } + if (!isset($config['left'])) { + $missingFields[] = 'left'; + } + if (!isset($config['right'])) { + $missingFields[] = 'right'; + } + if ($missingFields) { + throw new InvalidMappingException("Missing properties: ".implode(', ', $missingFields)." in class - {$meta->name}"); + } + } + + /** + * Validates metadata for closure type tree + * + * @param object $meta + * @param array $config + * + * @throws InvalidMappingException + */ + public function validateClosureTreeMetadata($meta, array $config) + { + $missingFields = array(); + if (!isset($config['parent'])) { + $missingFields[] = 'ancestor'; + } + if (!isset($config['closure'])) { + $missingFields[] = 'closure class'; + } + if ($missingFields) { + throw new InvalidMappingException("Missing properties: ".implode(', ', $missingFields)." in class - {$meta->name}"); + } + } + + /** + * Validates metadata for materialized path type tree + * + * @param object $meta + * @param array $config + * + * @throws InvalidMappingException + */ + public function validateMaterializedPathTreeMetadata($meta, array $config) + { + $missingFields = array(); + if (!isset($config['parent'])) { + $missingFields[] = 'ancestor'; + } + if (!isset($config['path'])) { + $missingFields[] = 'path'; + } + if (!isset($config['path_source'])) { + $missingFields[] = 'path_source'; + } + if ($missingFields) { + throw new InvalidMappingException("Missing properties: ".implode(', ', $missingFields)." in class - {$meta->name}"); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Node.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Node.php new file mode 100644 index 0000000..7c6cb10 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Node.php @@ -0,0 +1,39 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Node +{ + // use now annotations instead of predefined methods, this interface is not necessary + + /** + * @gedmo:TreeLeft + * to mark the field as "tree left" use property annotation @gedmo:TreeLeft + * it will use this field to store tree left value + */ + + /** + * @gedmo:TreeRight + * to mark the field as "tree right" use property annotation @gedmo:TreeRight + * it will use this field to store tree right value + */ + + /** + * @gedmo:TreeParent + * in every tree there should be link to parent. To identify a relation + * as parent relation to child use @Tree:Ancestor annotation on the related property + */ + + /** + * @gedmo:TreeLevel + * level of node. + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryInterface.php new file mode 100644 index 0000000..60c13da --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryInterface.php @@ -0,0 +1,60 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface RepositoryInterface extends RepositoryUtilsInterface +{ + /** + * Get all root nodes + * + * @param string $sortByField + * @param string $direction + * + * @return array + */ + public function getRootNodes($sortByField = null, $direction = 'asc'); + + /** + * Returns an array of nodes suitable for method buildTree + * + * @param object $node - Root node + * @param bool $direct - Obtain direct children? + * @param array $options - Options + * @param boolean $includeNode - Include node in results? + * + * @return array - Array of nodes + */ + public function getNodesHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false); + + /** + * Get list of children followed by given $node + * + * @param object $node - if null, all tree nodes will be taken + * @param boolean $direct - true to take only direct children + * @param string $sortByField - field name to sort by + * @param string $direction - sort direction : "ASC" or "DESC" + * @param bool $includeNode - Include the root node in results? + * + * @return array - list of given $node children, null on failure + */ + public function getChildren($node = null, $direct = false, $sortByField = null, $direction = 'ASC', $includeNode = false); + + /** + * Counts the children of given TreeNode + * + * @param object $node - if null counts all records in tree + * @param boolean $direct - true to count only direct children + * + * @throws \Gedmo\Exception\InvalidArgumentException - if input is not valid + * + * @return integer + */ + public function childCount($node = null, $direct = false); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtils.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtils.php new file mode 100644 index 0000000..0e89e1c --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtils.php @@ -0,0 +1,183 @@ +om = $om; + $this->meta = $meta; + $this->listener = $listener; + $this->repo = $repo; + } + + public function getClassMetadata() + { + return $this->meta; + } + + /** + * {@inheritDoc} + */ + public function childrenHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false) + { + $meta = $this->getClassMetadata(); + + if ($node !== null) { + if ($node instanceof $meta->name) { + $wrapperClass = $this->om instanceof \Doctrine\ORM\EntityManagerInterface ? + '\Gedmo\Tool\Wrapper\EntityWrapper' : + '\Gedmo\Tool\Wrapper\MongoDocumentWrapper'; + $wrapped = new $wrapperClass($node, $this->om); + if (!$wrapped->hasValidIdentifier()) { + throw new InvalidArgumentException("Node is not managed by UnitOfWork"); + } + } + } else { + $includeNode = true; + } + + // Gets the array of $node results. It must be ordered by depth + $nodes = $this->repo->getNodesHierarchy($node, $direct, $options, $includeNode); + + return $this->repo->buildTree($nodes, $options); + } + + /** + * {@inheritDoc} + */ + public function buildTree(array $nodes, array $options = array()) + { + $meta = $this->getClassMetadata(); + $nestedTree = $this->repo->buildTreeArray($nodes); + + $default = array( + 'decorate' => false, + 'rootOpen' => '
      ', + 'rootClose' => '
    ', + 'childOpen' => '
  • ', + 'childClose' => '
  • ', + 'nodeDecorator' => function ($node) use ($meta) { + // override and change it, guessing which field to use + if ($meta->hasField('title')) { + $field = 'title'; + } elseif ($meta->hasField('name')) { + $field = 'name'; + } else { + throw new InvalidArgumentException("Cannot find any representation field"); + } + + return $node[$field]; + }, + ); + $options = array_merge($default, $options); + // If you don't want any html output it will return the nested array + if (!$options['decorate']) { + return $nestedTree; + } + + if (!count($nestedTree)) { + return ''; + } + + $childrenIndex = $this->childrenIndex; + + $build = function ($tree) use (&$build, &$options, $childrenIndex) { + $output = is_string($options['rootOpen']) ? $options['rootOpen'] : $options['rootOpen']($tree); + foreach ($tree as $node) { + $output .= is_string($options['childOpen']) ? $options['childOpen'] : $options['childOpen']($node); + $output .= $options['nodeDecorator']($node); + if (count($node[$childrenIndex]) > 0) { + $output .= $build($node[$childrenIndex]); + } + $output .= is_string($options['childClose']) ? $options['childClose'] : $options['childClose']($node); + } + + return $output.(is_string($options['rootClose']) ? $options['rootClose'] : $options['rootClose']($tree)); + }; + + return $build($nestedTree); + } + + /** + * {@inheritDoc} + */ + public function buildTreeArray(array $nodes) + { + $meta = $this->getClassMetadata(); + $config = $this->listener->getConfiguration($this->om, $meta->name); + $nestedTree = array(); + $l = 0; + + if (count($nodes) > 0) { + // Node Stack. Used to help building the hierarchy + $stack = array(); + foreach ($nodes as $child) { + $item = $child; + $item[$this->childrenIndex] = array(); + // Number of stack items + $l = count($stack); + // Check if we're dealing with different levels + while ($l > 0 && $stack[$l - 1][$config['level']] >= $item[$config['level']]) { + array_pop($stack); + $l--; + } + // Stack is empty (we are inspecting the root) + if ($l == 0) { + // Assigning the root child + $i = count($nestedTree); + $nestedTree[$i] = $item; + $stack[] = &$nestedTree[$i]; + } else { + // Add child to parent + $i = count($stack[$l - 1][$this->childrenIndex]); + $stack[$l - 1][$this->childrenIndex][$i] = $item; + $stack[] = &$stack[$l - 1][$this->childrenIndex][$i]; + } + } + } + + return $nestedTree; + } + + /** + * {@inheritDoc} + */ + public function setChildrenIndex($childrenIndex) + { + $this->childrenIndex = $childrenIndex; + } + + /** + * {@inheritDoc} + */ + public function getChildrenIndex() + { + return $this->childrenIndex; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtilsInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtilsInterface.php new file mode 100644 index 0000000..67de627 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/RepositoryUtilsInterface.php @@ -0,0 +1,74 @@ +LI tree + * nodeDecorator: Closure (null) - uses $node as argument and returns decorated item as string + * rootOpen: string || Closure ('
      ') - branch start, closure will be given $children as a parameter + * rootClose: string ('
    ') - branch close + * childStart: string || Closure ('
  • ') - start of node, closure will be given $node as a parameter + * childClose: string ('
  • ') - close of node + * childSort: array || keys allowed: field: field to sort on, dir: direction. 'asc' or 'desc' + * @param boolean $includeNode - Include node on results? + * + * @return array|string + */ + public function childrenHierarchy($node = null, $direct = false, array $options = array(), $includeNode = false); + + /** + * Retrieves the nested array or the decorated output. + * + * Uses options to handle decorations + * NOTE: nodes should be fetched and hydrated as array + * + * @throws \Gedmo\Exception\InvalidArgumentException + * + * @param array $nodes - list o nodes to build tree + * @param array $options : + * decorate: boolean (false) - retrieves tree as UL->LI tree + * nodeDecorator: Closure (null) - uses $node as argument and returns decorated item as string + * rootOpen: string || Closure ('
      ') - branch start, closure will be given $children as a parameter + * rootClose: string ('
    ') - branch close + * childStart: string || Closure ('
  • ') - start of node, closure will be given $node as a parameter + * childClose: string ('
  • ') - close of node + * + * @return array|string + */ + public function buildTree(array $nodes, array $options = array()); + + /** + * Process nodes and produce an array with the + * structure of the tree + * + * @param array $nodes - Array of nodes + * + * @return array - Array with tree structure + */ + public function buildTreeArray(array $nodes); + + /** + * Sets the current children index. + * + * @param string $childrenIndex + */ + public function setChildrenIndex($childrenIndex); + + /** + * Gets the current children index. + * + * @return string + */ + public function getChildrenIndex(); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy.php new file mode 100644 index 0000000..bbaf206 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy.php @@ -0,0 +1,151 @@ + + * @author Gediminas Morkevicius + * @author + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +abstract class AbstractMaterializedPath implements Strategy +{ + const ACTION_INSERT = 'insert'; + const ACTION_UPDATE = 'update'; + const ACTION_REMOVE = 'remove'; + + /** + * TreeListener + * + * @var AbstractTreeListener + */ + protected $listener = null; + + /** + * Array of objects which were scheduled for path processes + * + * @var array + */ + protected $scheduledForPathProcess = array(); + + /** + * Array of objects which were scheduled for path process. + * This time, this array contains the objects with their ID + * already set + * + * @var array + */ + protected $scheduledForPathProcessWithIdSet = array(); + + /** + * Roots of trees which needs to be locked + * + * @var array + */ + protected $rootsOfTreesWhichNeedsLocking = array(); + + /** + * Objects which are going to be inserted (set only if tree locking is used) + * + * @var array + */ + protected $pendingObjectsToInsert = array(); + + /** + * Objects which are going to be updated (set only if tree locking is used) + * + * @var array + */ + protected $pendingObjectsToUpdate = array(); + + /** + * Objects which are going to be removed (set only if tree locking is used) + * + * @var array + */ + protected $pendingObjectsToRemove = array(); + + /** + * {@inheritdoc} + */ + public function __construct(TreeListener $listener) + { + $this->listener = $listener; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Strategy::MATERIALIZED_PATH; + } + + /** + * {@inheritdoc} + */ + public function processScheduledInsertion($om, $node, AdapterInterface $ea) + { + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + $fieldMapping = $meta->getFieldMapping($config['path_source']); + + if ($meta->isIdentifier($config['path_source']) || $fieldMapping['type'] === 'string') { + $this->scheduledForPathProcess[spl_object_hash($node)] = $node; + } else { + $this->updateNode($om, $node, $ea); + } + } + + /** + * {@inheritdoc} + */ + public function processScheduledUpdate($om, $node, AdapterInterface $ea) + { + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + $uow = $om->getUnitOfWork(); + $changeSet = $ea->getObjectChangeSet($uow, $node); + + if (isset($changeSet[$config['parent']]) || isset($changeSet[$config['path_source']])) { + if (isset($changeSet[$config['path']])) { + $originalPath = $changeSet[$config['path']][0]; + } else { + $pathProp = $meta->getReflectionProperty($config['path']); + $pathProp->setAccessible(true); + $originalPath = $pathProp->getValue($node); + } + + $this->updateNode($om, $node, $ea); + $this->updateChildren($om, $node, $ea, $originalPath); + } + } + + /** + * {@inheritdoc} + */ + public function processPostPersist($om, $node, AdapterInterface $ea) + { + $oid = spl_object_hash($node); + + if ($this->scheduledForPathProcess && array_key_exists($oid, $this->scheduledForPathProcess)) { + $this->scheduledForPathProcessWithIdSet[$oid] = $node; + + unset($this->scheduledForPathProcess[$oid]); + + if (empty($this->scheduledForPathProcess)) { + foreach ($this->scheduledForPathProcessWithIdSet as $oid => $node) { + $this->updateNode($om, $node, $ea); + + unset($this->scheduledForPathProcessWithIdSet[$oid]); + } + } + } + + $this->processPostEventsActions($om, $ea, $node, self::ACTION_INSERT); + } + + /** + * {@inheritdoc} + */ + public function processPostUpdate($om, $node, AdapterInterface $ea) + { + $this->processPostEventsActions($om, $ea, $node, self::ACTION_UPDATE); + } + + /** + * {@inheritdoc} + */ + public function processPostRemove($om, $node, AdapterInterface $ea) + { + $this->processPostEventsActions($om, $ea, $node, self::ACTION_REMOVE); + } + + /** + * {@inheritdoc} + */ + public function onFlushEnd($om, AdapterInterface $ea) + { + $this->lockTrees($om, $ea); + } + + /** + * {@inheritdoc} + */ + public function processPreRemove($om, $node) + { + $this->processPreLockingActions($om, $node, self::ACTION_REMOVE); + } + + /** + * {@inheritdoc} + */ + public function processPrePersist($om, $node) + { + $this->processPreLockingActions($om, $node, self::ACTION_INSERT); + } + + /** + * {@inheritdoc} + */ + public function processPreUpdate($om, $node) + { + $this->processPreLockingActions($om, $node, self::ACTION_UPDATE); + } + + /** + * {@inheritdoc} + */ + public function processMetadataLoad($om, $meta) + { + } + + /** + * {@inheritdoc} + */ + public function processScheduledDelete($om, $node) + { + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + + $this->removeNode($om, $meta, $config, $node); + } + + /** + * Update the $node + * + * @param ObjectManager $om + * @param object $node - target node + * @param AdapterInterface $ea - event adapter + * + * @return void + */ + public function updateNode(ObjectManager $om, $node, AdapterInterface $ea) + { + $oid = spl_object_hash($node); + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + $uow = $om->getUnitOfWork(); + $parentProp = $meta->getReflectionProperty($config['parent']); + $parentProp->setAccessible(true); + $parent = $parentProp->getValue($node); + $pathProp = $meta->getReflectionProperty($config['path']); + $pathProp->setAccessible(true); + $pathSourceProp = $meta->getReflectionProperty($config['path_source']); + $pathSourceProp->setAccessible(true); + $path = $pathSourceProp->getValue($node); + + // We need to avoid the presence of the path separator in the path source + if (strpos($path, $config['path_separator']) !== false) { + $msg = 'You can\'t use the Path separator ("%s") as a character for your PathSource field value.'; + + throw new RuntimeException(sprintf($msg, $config['path_separator'])); + } + + $fieldMapping = $meta->getFieldMapping($config['path_source']); + + // default behavior: if PathSource field is a string, we append the ID to the path + // path_append_id is true: always append id + // path_append_id is false: never append id + if ($config['path_append_id'] === true || ($fieldMapping['type'] === 'string' && $config['path_append_id'] !== false)) { + if (method_exists($meta, 'getIdentifierValue')) { + $identifier = $meta->getIdentifierValue($node); + } else { + $identifierProp = $meta->getReflectionProperty($meta->getSingleIdentifierFieldName()); + $identifierProp->setAccessible(true); + $identifier = $identifierProp->getValue($node); + } + + $path .= '-'.$identifier; + } + + if ($parent) { + // Ensure parent has been initialized in the case where it's a proxy + $om->initializeObject($parent); + + $changeSet = $uow->isScheduledForUpdate($parent) ? $ea->getObjectChangeSet($uow, $parent) : false; + $pathOrPathSourceHasChanged = $changeSet && (isset($changeSet[$config['path_source']]) || isset($changeSet[$config['path']])); + + if ($pathOrPathSourceHasChanged || !$pathProp->getValue($parent)) { + $this->updateNode($om, $parent, $ea); + } + + $parentPath = $pathProp->getValue($parent); + // if parent path not ends with separator + if ($parentPath[strlen($parentPath) - 1] !== $config['path_separator']) { + // add separator + $path = $pathProp->getValue($parent).$config['path_separator'].$path; + } else { + // don't add separator + $path = $pathProp->getValue($parent).$path; + } + } + + if ($config['path_starts_with_separator'] && (strlen($path) > 0 && $path[0] !== $config['path_separator'])) { + $path = $config['path_separator'].$path; + } + + if ($config['path_ends_with_separator'] && ($path[strlen($path) - 1] !== $config['path_separator'])) { + $path .= $config['path_separator']; + } + + $pathProp->setValue($node, $path); + $changes = array( + $config['path'] => array(null, $path), + ); + + if (isset($config['path_hash'])) { + $pathHash = md5($path); + $pathHashProp = $meta->getReflectionProperty($config['path_hash']); + $pathHashProp->setAccessible(true); + $pathHashProp->setValue($node, $pathHash); + $changes[$config['path_hash']] = array(null, $pathHash); + } + + if (isset($config['root'])) { + $root = null; + + // Define the root value by grabbing the top of the current path + $rootFinderPath = explode($config['path_separator'], $path); + $rootIndex = $config['path_starts_with_separator'] ? 1 : 0; + $root = $rootFinderPath[$rootIndex]; + + // If it is an association, then make it an reference + // to the entity + if ($meta->hasAssociation($config['root'])) { + $rootClass = $meta->getAssociationTargetClass($config['root']); + $root = $om->getReference($rootClass, $root); + } + + $rootProp = $meta->getReflectionProperty($config['root']); + $rootProp->setAccessible(true); + $rootProp->setValue($node, $root); + $changes[$config['root']] = array(null, $root); + } + + if (isset($config['level'])) { + $level = substr_count($path, $config['path_separator']); + $levelProp = $meta->getReflectionProperty($config['level']); + $levelProp->setAccessible(true); + $levelProp->setValue($node, $level); + $changes[$config['level']] = array(null, $level); + } + + if (!$uow instanceof MongoDBUnitOfWork) { + $ea->setOriginalObjectProperty($uow, $oid, $config['path'], $path); + $uow->scheduleExtraUpdate($node, $changes); + } else { + $ea->recomputeSingleObjectChangeSet($uow, $meta, $node); + } + if (isset($config['path_hash'])) { + $ea->setOriginalObjectProperty($uow, $oid, $config['path_hash'], $pathHash); + } + } + + /** + * Update node's children + * + * @param ObjectManager $om + * @param object $node + * @param AdapterInterface $ea + * @param string $originalPath + * + * @return void + */ + public function updateChildren(ObjectManager $om, $node, AdapterInterface $ea, $originalPath) + { + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + $children = $this->getChildren($om, $meta, $config, $originalPath); + + foreach ($children as $child) { + $this->updateNode($om, $child, $ea); + } + } + + /** + * Process pre-locking actions + * + * @param ObjectManager $om + * @param object $node + * @param string $action + * + * @return void + */ + public function processPreLockingActions($om, $node, $action) + { + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + + if ($config['activate_locking']) { + ; + $parentProp = $meta->getReflectionProperty($config['parent']); + $parentProp->setAccessible(true); + $parentNode = $node; + + while (!is_null($parent = $parentProp->getValue($parentNode))) { + $parentNode = $parent; + } + + // In some cases, the parent could be a not initialized proxy. In this case, the + // "lockTime" field may NOT be loaded yet and have null instead of the date. + // We need to be sure that this field has its real value + if ($parentNode !== $node && $parentNode instanceof \Doctrine\ODM\MongoDB\Proxy\Proxy) { + $reflMethod = new \ReflectionMethod(get_class($parentNode), '__load'); + $reflMethod->setAccessible(true); + + $reflMethod->invoke($parentNode); + } + + // If tree is already locked, we throw an exception + $lockTimeProp = $meta->getReflectionProperty($config['lock_time']); + $lockTimeProp->setAccessible(true); + $lockTime = $lockTimeProp->getValue($parentNode); + + if (!is_null($lockTime)) { + $lockTime = $lockTime instanceof \MongoDate ? $lockTime->sec : $lockTime->getTimestamp(); + } + + if (!is_null($lockTime) && ($lockTime >= (time() - $config['locking_timeout']))) { + $msg = 'Tree with root id "%s" is locked.'; + $id = $meta->getIdentifierValue($parentNode); + + throw new TreeLockingException(sprintf($msg, $id)); + } + + $this->rootsOfTreesWhichNeedsLocking[spl_object_hash($parentNode)] = $parentNode; + + $oid = spl_object_hash($node); + + switch ($action) { + case self::ACTION_INSERT: + $this->pendingObjectsToInsert[$oid] = $node; + + break; + case self::ACTION_UPDATE: + $this->pendingObjectsToUpdate[$oid] = $node; + + break; + case self::ACTION_REMOVE: + $this->pendingObjectsToRemove[$oid] = $node; + + break; + default: + throw new \InvalidArgumentException(sprintf('"%s" is not a valid action.', $action)); + } + } + } + + /** + * Process pre-locking actions + * + * @param ObjectManager $om + * @param AdapterInterface $ea + * @param object $node + * @param string $action + * + * @return void + */ + public function processPostEventsActions(ObjectManager $om, AdapterInterface $ea, $node, $action) + { + $meta = $om->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($om, $meta->name); + + if ($config['activate_locking']) { + switch ($action) { + case self::ACTION_INSERT: + unset($this->pendingObjectsToInsert[spl_object_hash($node)]); + + break; + case self::ACTION_UPDATE: + unset($this->pendingObjectsToUpdate[spl_object_hash($node)]); + + break; + case self::ACTION_REMOVE: + unset($this->pendingObjectsToRemove[spl_object_hash($node)]); + + break; + default: + throw new \InvalidArgumentException(sprintf('"%s" is not a valid action.', $action)); + } + + if (empty($this->pendingObjectsToInsert) && empty($this->pendingObjectsToUpdate) && + empty($this->pendingObjectsToRemove)) { + $this->releaseTreeLocks($om, $ea); + } + } + } + + /** + * Locks all needed trees + * + * @param ObjectManager $om + * @param AdapterInterface $ea + * + * @return void + */ + protected function lockTrees(ObjectManager $om, AdapterInterface $ea) + { + // Do nothing by default + } + + /** + * Releases all trees which are locked + * + * @param ObjectManager $om + * @param AdapterInterface $ea + * + * @return void + */ + protected function releaseTreeLocks(ObjectManager $om, AdapterInterface $ea) + { + // Do nothing by default + } + + /** + * Remove node and its children + * + * @param ObjectManager $om + * @param object $meta - Metadata + * @param object $config - config + * @param object $node - node to remove + * + * @return void + */ + abstract public function removeNode($om, $meta, $config, $node); + + /** + * Returns children of the node with its original path + * + * @param ObjectManager $om + * @param object $meta - Metadata + * @param object $config - config + * @param string $originalPath - original path of object + * + * @return array|\Traversable + */ + abstract public function getChildren($om, $meta, $config, $originalPath); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ODM/MongoDB/MaterializedPath.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ODM/MongoDB/MaterializedPath.php new file mode 100644 index 0000000..e5ab019 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ODM/MongoDB/MaterializedPath.php @@ -0,0 +1,97 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class MaterializedPath extends AbstractMaterializedPath +{ + /** + * {@inheritdoc} + */ + public function removeNode($om, $meta, $config, $node) + { + $uow = $om->getUnitOfWork(); + $wrapped = AbstractWrapper::wrap($node, $om); + + // Remove node's children + $results = $om->createQueryBuilder() + ->find($meta->name) + ->field($config['path'])->equals(new \MongoRegex('/^'.preg_quote($wrapped->getPropertyValue($config['path'])).'.?+/')) + ->getQuery() + ->execute(); + + foreach ($results as $node) { + $uow->scheduleForDelete($node); + } + } + + /** + * {@inheritdoc} + */ + public function getChildren($om, $meta, $config, $originalPath) + { + return $om->createQueryBuilder() + ->find($meta->name) + ->field($config['path'])->equals(new \MongoRegex('/^'.preg_quote($originalPath).'.+/')) + ->sort($config['path'], 'asc') // This may save some calls to updateNode + ->getQuery() + ->execute(); + } + + /** + * {@inheritdoc} + */ + protected function lockTrees(ObjectManager $om, AdapterInterface $ea) + { + $uow = $om->getUnitOfWork(); + + foreach ($this->rootsOfTreesWhichNeedsLocking as $oid => $root) { + $meta = $om->getClassMetadata(get_class($root)); + $config = $this->listener->getConfiguration($om, $meta->name); + $lockTimeProp = $meta->getReflectionProperty($config['lock_time']); + $lockTimeProp->setAccessible(true); + $lockTimeValue = new \MongoDate(); + $lockTimeProp->setValue($root, $lockTimeValue); + $changes = array( + $config['lock_time'] => array(null, $lockTimeValue), + ); + + $ea->recomputeSingleObjectChangeSet($uow, $meta, $root); + } + } + + /** + * {@inheritdoc} + */ + protected function releaseTreeLocks(ObjectManager $om, AdapterInterface $ea) + { + $uow = $om->getUnitOfWork(); + + foreach ($this->rootsOfTreesWhichNeedsLocking as $oid => $root) { + $meta = $om->getClassMetadata(get_class($root)); + $config = $this->listener->getConfiguration($om, $meta->name); + $lockTimeProp = $meta->getReflectionProperty($config['lock_time']); + $lockTimeProp->setAccessible(true); + $lockTimeValue = null; + $lockTimeProp->setValue($root, $lockTimeValue); + $changes = array( + $config['lock_time'] => array(null, null), + ); + + $ea->recomputeSingleObjectChangeSet($uow, $meta, $root); + + unset($this->rootsOfTreesWhichNeedsLocking[$oid]); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Closure.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Closure.php new file mode 100644 index 0000000..297462f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Closure.php @@ -0,0 +1,469 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Closure implements Strategy +{ + /** + * TreeListener + * + * @var TreeListener + */ + protected $listener = null; + + /** + * List of pending Nodes, which needs to + * be post processed because of having a parent Node + * which requires some additional calculations + * + * @var array + */ + private $pendingChildNodeInserts = array(); + + /** + * List of nodes which has their parents updated, but using + * new nodes. They have to wait until their parents are inserted + * on DB to make the update + * + * @var array + */ + private $pendingNodeUpdates = array(); + + /** + * List of pending Nodes, which needs their "level" + * field value set + * + * @var array + */ + private $pendingNodesLevelProcess = array(); + + /** + * {@inheritdoc} + */ + public function __construct(TreeListener $listener) + { + $this->listener = $listener; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Strategy::CLOSURE; + } + + /** + * {@inheritdoc} + */ + public function processMetadataLoad($em, $meta) + { + $config = $this->listener->getConfiguration($em, $meta->name); + $closureMetadata = $em->getClassMetadata($config['closure']); + $cmf = $em->getMetadataFactory(); + + if (!$closureMetadata->hasAssociation('ancestor')) { + // create ancestor mapping + $ancestorMapping = array( + 'fieldName' => 'ancestor', + 'id' => false, + 'joinColumns' => array( + array( + 'name' => 'ancestor', + 'referencedColumnName' => 'id', + 'unique' => false, + 'nullable' => false, + 'onDelete' => 'CASCADE', + 'onUpdate' => null, + 'columnDefinition' => null, + ), + ), + 'inversedBy' => null, + 'targetEntity' => $meta->name, + 'cascade' => null, + 'fetch' => ClassMetadataInfo::FETCH_LAZY, + ); + $closureMetadata->mapManyToOne($ancestorMapping); + if (Version::compare('2.3.0-dev') <= 0) { + $closureMetadata->reflFields['ancestor'] = $cmf + ->getReflectionService() + ->getAccessibleProperty($closureMetadata->name, 'ancestor') + ; + } + } + + if (!$closureMetadata->hasAssociation('descendant')) { + // create descendant mapping + $descendantMapping = array( + 'fieldName' => 'descendant', + 'id' => false, + 'joinColumns' => array( + array( + 'name' => 'descendant', + 'referencedColumnName' => 'id', + 'unique' => false, + 'nullable' => false, + 'onDelete' => 'CASCADE', + 'onUpdate' => null, + 'columnDefinition' => null, + ), + ), + 'inversedBy' => null, + 'targetEntity' => $meta->name, + 'cascade' => null, + 'fetch' => ClassMetadataInfo::FETCH_LAZY, + ); + $closureMetadata->mapManyToOne($descendantMapping); + if (Version::compare('2.3.0-dev') <= 0) { + $closureMetadata->reflFields['descendant'] = $cmf + ->getReflectionService() + ->getAccessibleProperty($closureMetadata->name, 'descendant') + ; + } + } + // create unique index on ancestor and descendant + $indexName = substr(strtoupper("IDX_".md5($closureMetadata->name)), 0, 20); + $closureMetadata->table['uniqueConstraints'][$indexName] = array( + 'columns' => array( + $this->getJoinColumnFieldName($em->getClassMetadata($config['closure'])->getAssociationMapping('ancestor')), + $this->getJoinColumnFieldName($em->getClassMetadata($config['closure'])->getAssociationMapping('descendant')), + ), + ); + // this one may not be very useful + $indexName = substr(strtoupper("IDX_".md5($meta->name.'depth')), 0, 20); + $closureMetadata->table['indexes'][$indexName] = array( + 'columns' => array('depth'), + ); + if ($cacheDriver = $cmf->getCacheDriver()) { + $cacheDriver->save($closureMetadata->name."\$CLASSMETADATA", $closureMetadata, null); + } + } + + /** + * {@inheritdoc} + */ + public function onFlushEnd($em, AdapterInterface $ea) + { + } + + /** + * {@inheritdoc} + */ + public function processPrePersist($em, $node) + { + $this->pendingChildNodeInserts[spl_object_hash($em)][spl_object_hash($node)] = $node; + } + + /** + * {@inheritdoc} + */ + public function processPreUpdate($em, $node) + { + } + + /** + * {@inheritdoc} + */ + public function processPreRemove($em, $node) + { + } + + /** + * {@inheritdoc} + */ + public function processScheduledInsertion($em, $node, AdapterInterface $ea) + { + } + + /** + * {@inheritdoc} + */ + public function processScheduledDelete($em, $entity) + { + } + + protected function getJoinColumnFieldName($association) + { + if (count($association['joinColumnFieldNames']) > 1) { + throw new RuntimeException('More association on field '.$association['fieldName']); + } + + return array_shift($association['joinColumnFieldNames']); + } + + /** + * {@inheritdoc} + */ + public function processPostUpdate($em, $entity, AdapterInterface $ea) + { + $meta = $em->getClassMetadata(get_class($entity)); + $config = $this->listener->getConfiguration($em, $meta->name); + + // Process TreeLevel field value + if (!empty($config)) { + $this->setLevelFieldOnPendingNodes($em); + } + } + + /** + * {@inheritdoc} + */ + public function processPostRemove($em, $entity, AdapterInterface $ea) + { + } + + /** + * {@inheritdoc} + */ + public function processPostPersist($em, $entity, AdapterInterface $ea) + { + $uow = $em->getUnitOfWork(); + $emHash = spl_object_hash($em); + + while ($node = array_shift($this->pendingChildNodeInserts[$emHash])) { + $meta = $em->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($em, $meta->name); + + $identifier = $meta->getSingleIdentifierFieldName(); + $nodeId = $meta->getReflectionProperty($identifier)->getValue($node); + $parent = $meta->getReflectionProperty($config['parent'])->getValue($node); + + $closureClass = $config['closure']; + $closureMeta = $em->getClassMetadata($closureClass); + $closureTable = $closureMeta->getTableName(); + + $ancestorColumnName = $this->getJoinColumnFieldName($em->getClassMetadata($config['closure'])->getAssociationMapping('ancestor')); + $descendantColumnName = $this->getJoinColumnFieldName($em->getClassMetadata($config['closure'])->getAssociationMapping('descendant')); + $depthColumnName = $em->getClassMetadata($config['closure'])->getColumnName('depth'); + + $entries = array( + array( + $ancestorColumnName => $nodeId, + $descendantColumnName => $nodeId, + $depthColumnName => 0, + ), + ); + + if ($parent) { + $dql = "SELECT c, a FROM {$closureMeta->name} c"; + $dql .= " JOIN c.ancestor a"; + $dql .= " WHERE c.descendant = :parent"; + $q = $em->createQuery($dql); + $q->setParameters(compact('parent')); + $ancestors = $q->getArrayResult(); + + foreach ($ancestors as $ancestor) { + $entries[] = array( + $ancestorColumnName => $ancestor['ancestor'][$identifier], + $descendantColumnName => $nodeId, + $depthColumnName => $ancestor['depth'] + 1, + ); + } + + if (isset($config['level'])) { + $this->pendingNodesLevelProcess[$nodeId] = $node; + } + } elseif (isset($config['level'])) { + $uow->scheduleExtraUpdate($node, array($config['level'] => array(null, 1))); + $ea->setOriginalObjectProperty($uow, spl_object_hash($node), $config['level'], 1); + $levelProp = $meta->getReflectionProperty($config['level']); + $levelProp->setValue($node, 1); + } + + foreach ($entries as $closure) { + if (!$em->getConnection()->insert($closureTable, $closure)) { + throw new RuntimeException('Failed to insert new Closure record'); + } + } + } + + // Process pending node updates + if (!empty($this->pendingNodeUpdates)) { + foreach ($this->pendingNodeUpdates as $info) { + $this->updateNode($em, $info['node'], $info['oldParent']); + } + + $this->pendingNodeUpdates = array(); + } + + // Process TreeLevel field value + $this->setLevelFieldOnPendingNodes($em); + } + + /** + * Process pending entities to set their "level" value + * + * @param \Doctrine\Common\Persistence\ObjectManager $em + */ + protected function setLevelFieldOnPendingNodes(ObjectManager $em) + { + if (!empty($this->pendingNodesLevelProcess)) { + $first = array_slice($this->pendingNodesLevelProcess, 0, 1); + $first = array_shift($first); + $meta = $em->getClassMetadata(get_class($first)); + unset($first); + $identifier = $meta->getIdentifier(); + $mapping = $meta->getFieldMapping($identifier[0]); + $config = $this->listener->getConfiguration($em, $meta->name); + $closureClass = $config['closure']; + $closureMeta = $em->getClassMetadata($closureClass); + $uow = $em->getUnitOfWork(); + + foreach ($this->pendingNodesLevelProcess as $node) { + $children = $em->getRepository($meta->name)->children($node); + + foreach ($children as $child) { + $this->pendingNodesLevelProcess[AbstractWrapper::wrap($child, $em)->getIdentifier()] = $child; + } + } + + // Avoid type conversion performance penalty + $type = 'integer' === $mapping['type'] ? Connection::PARAM_INT_ARRAY : Connection::PARAM_STR_ARRAY; + + // We calculate levels for all nodes + $sql = 'SELECT c.descendant, MAX(c.depth) + 1 AS levelNum '; + $sql .= 'FROM '.$closureMeta->getTableName().' c '; + $sql .= 'WHERE c.descendant IN (?) '; + $sql .= 'GROUP BY c.descendant'; + + $levelsAssoc = $em->getConnection()->executeQuery($sql, array(array_keys($this->pendingNodesLevelProcess)), array($type))->fetchAll(\PDO::FETCH_NUM); + + //create key pair array with resultset + $levels = array(); + foreach( $levelsAssoc as $level ) + { + $levels[$level[0]] = $level[1]; + } + $levelsAssoc = null; + + // Now we update levels + foreach ($this->pendingNodesLevelProcess as $nodeId => $node) { + // Update new level + $level = $levels[$nodeId]; + $levelProp = $meta->getReflectionProperty($config['level']); + $uow->scheduleExtraUpdate( + $node, + array($config['level'] => array( + $levelProp->getValue($node), $level, + )) + ); + $levelProp->setValue($node, $level); + $uow->setOriginalEntityProperty(spl_object_hash($node), $config['level'], $level); + } + + $this->pendingNodesLevelProcess = array(); + } + } + + /** + * {@inheritdoc} + */ + public function processScheduledUpdate($em, $node, AdapterInterface $ea) + { + $meta = $em->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($em, $meta->name); + $uow = $em->getUnitOfWork(); + $changeSet = $uow->getEntityChangeSet($node); + + if (array_key_exists($config['parent'], $changeSet)) { + // If new parent is new, we need to delay the update of the node + // until it is inserted on DB + $parent = $changeSet[$config['parent']][1] ? AbstractWrapper::wrap($changeSet[$config['parent']][1], $em) : null; + + if ($parent && !$parent->getIdentifier()) { + $this->pendingNodeUpdates[spl_object_hash($node)] = array( + 'node' => $node, + 'oldParent' => $changeSet[$config['parent']][0], + ); + } else { + $this->updateNode($em, $node, $changeSet[$config['parent']][0]); + } + } + } + + /** + * Update node and closures + * + * @param EntityManagerInterface $em + * @param object $node + * @param object $oldParent + */ + public function updateNode(EntityManagerInterface $em, $node, $oldParent) + { + $wrapped = AbstractWrapper::wrap($node, $em); + $meta = $wrapped->getMetadata(); + $config = $this->listener->getConfiguration($em, $meta->name); + $closureMeta = $em->getClassMetadata($config['closure']); + + $nodeId = $wrapped->getIdentifier(); + $parent = $wrapped->getPropertyValue($config['parent']); + $table = $closureMeta->getTableName(); + $conn = $em->getConnection(); + // ensure integrity + if ($parent) { + $dql = "SELECT COUNT(c) FROM {$closureMeta->name} c"; + $dql .= " WHERE c.ancestor = :node"; + $dql .= " AND c.descendant = :parent"; + $q = $em->createQuery($dql); + $q->setParameters(compact('node', 'parent')); + if ($q->getSingleScalarResult()) { + throw new \Gedmo\Exception\UnexpectedValueException("Cannot set child as parent to node: {$nodeId}"); + } + } + + if ($oldParent) { + $subQuery = "SELECT c2.id FROM {$table} c1"; + $subQuery .= " JOIN {$table} c2 ON c1.descendant = c2.descendant"; + $subQuery .= " WHERE c1.ancestor = :nodeId AND c2.depth > c1.depth"; + + $ids = $conn->executeQuery($subQuery, compact('nodeId'))->fetchAll(\PDO::FETCH_COLUMN); + if ($ids) { + // using subquery directly, sqlite acts unfriendly + $query = "DELETE FROM {$table} WHERE id IN (".implode(', ', $ids).")"; + if (!empty($ids) && !$conn->executeQuery($query)) { + throw new RuntimeException('Failed to remove old closures'); + } + } + } + + if ($parent) { + $wrappedParent = AbstractWrapper::wrap($parent, $em); + $parentId = $wrappedParent->getIdentifier(); + $query = "SELECT c1.ancestor, c2.descendant, (c1.depth + c2.depth + 1) AS depth"; + $query .= " FROM {$table} c1, {$table} c2"; + $query .= " WHERE c1.descendant = :parentId"; + $query .= " AND c2.ancestor = :nodeId"; + + $closures = $conn->fetchAll($query, compact('nodeId', 'parentId')); + + foreach ($closures as $closure) { + if (!$conn->insert($table, $closure)) { + throw new RuntimeException('Failed to insert new Closure record'); + } + } + } + + if (isset($config['level'])) { + $this->pendingNodesLevelProcess[$nodeId] = $node; + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/MaterializedPath.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/MaterializedPath.php new file mode 100644 index 0000000..9737058 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/MaterializedPath.php @@ -0,0 +1,66 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class MaterializedPath extends AbstractMaterializedPath +{ + /** + * {@inheritdoc} + */ + public function removeNode($om, $meta, $config, $node) + { + $uow = $om->getUnitOfWork(); + $wrapped = AbstractWrapper::wrap($node, $om); + + $path = addcslashes($wrapped->getPropertyValue($config['path']), '%'); + + // Remove node's children + $qb = $om->createQueryBuilder(); + $qb->select('e') + ->from($config['useObjectClass'], 'e') + ->where($qb->expr()->like('e.'.$config['path'], $qb->expr()->literal($path.'%'))); + + if (isset($config['level'])) { + $lvlField = $config['level']; + $lvl = $wrapped->getPropertyValue($lvlField); + if (!empty($lvl)) { + $qb->andWhere($qb->expr()->gt('e.' . $lvlField, $qb->expr()->literal($lvl))); + } + } + + $results = $qb->getQuery() + ->execute(); + + foreach ($results as $node) { + $uow->scheduleForDelete($node); + } + } + + /** + * {@inheritdoc} + */ + public function getChildren($om, $meta, $config, $path) + { + $path = addcslashes($path, '%'); + $qb = $om->createQueryBuilder($config['useObjectClass']); + $qb->select('e') + ->from($config['useObjectClass'], 'e') + ->where($qb->expr()->like('e.'.$config['path'], $qb->expr()->literal($path.'%'))) + ->andWhere('e.'.$config['path'].' != :path') + ->orderBy('e.'.$config['path'], 'asc'); // This may save some calls to updateNode + $qb->setParameter('path', $path); + + return $qb->getQuery() + ->execute(); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Nested.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Nested.php new file mode 100644 index 0000000..80f689b --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Strategy/ORM/Nested.php @@ -0,0 +1,718 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Nested implements Strategy +{ + /** + * Previous sibling position + */ + const PREV_SIBLING = 'PrevSibling'; + + /** + * Next sibling position + */ + const NEXT_SIBLING = 'NextSibling'; + + /** + * Last child position + */ + const LAST_CHILD = 'LastChild'; + + /** + * First child position + */ + const FIRST_CHILD = 'FirstChild'; + + /** + * TreeListener + * + * @var TreeListener + */ + protected $listener = null; + + /** + * The max number of "right" field of the + * tree in case few root nodes will be persisted + * on one flush for node classes + * + * @var array + */ + private $treeEdges = array(); + + /** + * Stores a list of node position strategies + * for each node by object hash + * + * @var array + */ + private $nodePositions = array(); + + /** + * Stores a list of delayed nodes for correct order of updates + * + * @var array + */ + private $delayedNodes = array(); + + /** + * {@inheritdoc} + */ + public function __construct(TreeListener $listener) + { + $this->listener = $listener; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return Strategy::NESTED; + } + + /** + * Set node position strategy + * + * @param string $oid + * @param string $position + */ + public function setNodePosition($oid, $position) + { + $valid = array( + self::FIRST_CHILD, + self::LAST_CHILD, + self::NEXT_SIBLING, + self::PREV_SIBLING, + ); + if (!in_array($position, $valid, false)) { + throw new \Gedmo\Exception\InvalidArgumentException("Position: {$position} is not valid in nested set tree"); + } + $this->nodePositions[$oid] = $position; + } + + /** + * {@inheritdoc} + */ + public function processScheduledInsertion($em, $node, AdapterInterface $ea) + { + /** @var ClassMetadata $meta */ + $meta = $em->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($em, $meta->name); + + $meta->getReflectionProperty($config['left'])->setValue($node, 0); + $meta->getReflectionProperty($config['right'])->setValue($node, 0); + if (isset($config['level'])) { + $meta->getReflectionProperty($config['level'])->setValue($node, 0); + } + if (isset($config['root']) && !$meta->hasAssociation($config['root']) && !isset($config['rootIdentifierMethod'])) { + $meta->getReflectionProperty($config['root'])->setValue($node, 0); + } else if (isset($config['rootIdentifierMethod']) && is_null($meta->getReflectionProperty($config['root'])->getValue($node))) { + $meta->getReflectionProperty($config['root'])->setValue($node, 0); + } + } + + /** + * {@inheritdoc} + */ + public function processScheduledUpdate($em, $node, AdapterInterface $ea) + { + $meta = $em->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($em, $meta->name); + $uow = $em->getUnitOfWork(); + + $changeSet = $uow->getEntityChangeSet($node); + if (isset($config['root']) && isset($changeSet[$config['root']])) { + throw new \Gedmo\Exception\UnexpectedValueException("Root cannot be changed manually, change parent instead"); + } + + $oid = spl_object_hash($node); + if (isset($changeSet[$config['left']]) && isset($this->nodePositions[$oid])) { + $wrapped = AbstractWrapper::wrap($node, $em); + $parent = $wrapped->getPropertyValue($config['parent']); + // revert simulated changeset + $uow->clearEntityChangeSet($oid); + $wrapped->setPropertyValue($config['left'], $changeSet[$config['left']][0]); + $uow->setOriginalEntityProperty($oid, $config['left'], $changeSet[$config['left']][0]); + // set back all other changes + foreach ($changeSet as $field => $set) { + if ($field !== $config['left']) { + if (is_array($set) && array_key_exists(0, $set) && array_key_exists(1, $set)) { + $uow->setOriginalEntityProperty($oid, $field, $set[0]); + $wrapped->setPropertyValue($field, $set[1]); + } else { + $uow->setOriginalEntityProperty($oid, $field, $set); + $wrapped->setPropertyValue($field, $set); + } + } + } + $uow->recomputeSingleEntityChangeSet($meta, $node); + $this->updateNode($em, $node, $parent); + } elseif (isset($changeSet[$config['parent']])) { + $this->updateNode($em, $node, $changeSet[$config['parent']][1]); + } + } + + /** + * {@inheritdoc} + */ + public function processPostPersist($em, $node, AdapterInterface $ea) + { + $meta = $em->getClassMetadata(get_class($node)); + + $config = $this->listener->getConfiguration($em, $meta->name); + $parent = $meta->getReflectionProperty($config['parent'])->getValue($node); + $this->updateNode($em, $node, $parent, self::LAST_CHILD); + } + + /** + * {@inheritdoc} + */ + public function processScheduledDelete($em, $node) + { + $meta = $em->getClassMetadata(get_class($node)); + $config = $this->listener->getConfiguration($em, $meta->name); + $uow = $em->getUnitOfWork(); + + $wrapped = AbstractWrapper::wrap($node, $em); + $leftValue = $wrapped->getPropertyValue($config['left']); + $rightValue = $wrapped->getPropertyValue($config['right']); + + if (!$leftValue || !$rightValue) { + return; + } + $rootId = isset($config['root']) ? $wrapped->getPropertyValue($config['root']) : null; + $diff = $rightValue - $leftValue + 1; + if ($diff > 2) { + $qb = $em->createQueryBuilder(); + $qb->select('node') + ->from($config['useObjectClass'], 'node') + ->where($qb->expr()->between('node.' . $config['left'], '?1', '?2')) + ->setParameters(array(1 => $leftValue, 2 => $rightValue)); + + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.' . $config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $q = $qb->getQuery(); + // get nodes for deletion + $nodes = $q->getResult(); + foreach ((array)$nodes as $removalNode) { + $uow->scheduleForDelete($removalNode); + } + } + $this->shiftRL($em, $config['useObjectClass'], $rightValue + 1, -$diff, $rootId); + } + + /** + * {@inheritdoc} + */ + public function onFlushEnd($em, AdapterInterface $ea) + { + // reset values + $this->treeEdges = array(); + } + + /** + * {@inheritdoc} + */ + public function processPreRemove($em, $node) + { + } + + /** + * {@inheritdoc} + */ + public function processPrePersist($em, $node) + { + } + + /** + * {@inheritdoc} + */ + public function processPreUpdate($em, $node) + { + } + + /** + * {@inheritdoc} + */ + public function processMetadataLoad($em, $meta) + { + } + + /** + * {@inheritdoc} + */ + public function processPostUpdate($em, $entity, AdapterInterface $ea) + { + } + + /** + * {@inheritdoc} + */ + public function processPostRemove($em, $entity, AdapterInterface $ea) + { + } + + /** + * Update the $node with a diferent $parent + * destination + * + * @param EntityManagerInterface $em + * @param object $node - target node + * @param object $parent - destination node + * @param string $position + * + * @throws \Gedmo\Exception\UnexpectedValueException + */ + public function updateNode(EntityManagerInterface $em, $node, $parent, $position = 'FirstChild') + { + $wrapped = AbstractWrapper::wrap($node, $em); + + /** @var ClassMetadata $meta */ + $meta = $wrapped->getMetadata(); + $config = $this->listener->getConfiguration($em, $meta->name); + + $root = isset($config['root']) ? $wrapped->getPropertyValue($config['root']) : null; + $identifierField = $meta->getSingleIdentifierFieldName(); + $nodeId = $wrapped->getIdentifier(); + + $left = $wrapped->getPropertyValue($config['left']); + $right = $wrapped->getPropertyValue($config['right']); + + $isNewNode = empty($left) && empty($right); + if ($isNewNode) { + $left = 1; + $right = 2; + } + + $oid = spl_object_hash($node); + if (isset($this->nodePositions[$oid])) { + $position = $this->nodePositions[$oid]; + } + $level = 0; + $treeSize = $right - $left + 1; + $newRoot = null; + if ($parent) { // || (!$parent && isset($config['rootIdentifierMethod'])) + $wrappedParent = AbstractWrapper::wrap($parent, $em); + + $parentRoot = isset($config['root']) ? $wrappedParent->getPropertyValue($config['root']) : null; + $parentOid = spl_object_hash($parent); + $parentLeft = $wrappedParent->getPropertyValue($config['left']); + $parentRight = $wrappedParent->getPropertyValue($config['right']); + if (empty($parentLeft) && empty($parentRight)) { + // parent node is a new node, but wasn't processed yet (due to Doctrine commit order calculator redordering) + // We delay processing of node to the moment parent node will be processed + if (!isset($this->delayedNodes[$parentOid])) { + $this->delayedNodes[$parentOid] = array(); + } + $this->delayedNodes[$parentOid][] = array('node' => $node, 'position' => $position); + + return; + } + if (!$isNewNode && $root === $parentRoot && $parentLeft >= $left && $parentRight <= $right) { + throw new UnexpectedValueException("Cannot set child as parent to node: {$nodeId}"); + } + if (isset($config['level'])) { + $level = $wrappedParent->getPropertyValue($config['level']); + } + switch ($position) { + case self::PREV_SIBLING: + if (property_exists($node, 'sibling')) { + $wrappedSibling = AbstractWrapper::wrap($node->sibling, $em); + $start = $wrappedSibling->getPropertyValue($config['left']); + $level++; + } else { + $newParent = $wrappedParent->getPropertyValue($config['parent']); + + if (is_null($newParent) && ((isset($config['root']) && $config['root'] == $config['parent']) || $isNewNode)) { + throw new UnexpectedValueException("Cannot persist sibling for a root node, tree operation is not possible"); + } else if (is_null($newParent) && (isset($config['root']) || $isNewNode)) { + // root is a different column from parent (pointing to another table?), do nothing + } else { + $wrapped->setPropertyValue($config['parent'], $newParent); + } + + $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $node); + $start = $parentLeft; + } + break; + + case self::NEXT_SIBLING: + if (property_exists($node, 'sibling')) { + $wrappedSibling = AbstractWrapper::wrap($node->sibling, $em); + $start = $wrappedSibling->getPropertyValue($config['right']) + 1; + $level++; + } else { + $newParent = $wrappedParent->getPropertyValue($config['parent']); + if (is_null($newParent) && ((isset($config['root']) && $config['root'] == $config['parent']) || $isNewNode)) { + throw new UnexpectedValueException("Cannot persist sibling for a root node, tree operation is not possible"); + } else if (is_null($newParent) && (isset($config['root']) || $isNewNode)) { + // root is a different column from parent (pointing to another table?), do nothing + } else { + $wrapped->setPropertyValue($config['parent'], $newParent); + } + + $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $node); + $start = $parentRight + 1; + } + break; + + case self::LAST_CHILD: + $start = $parentRight; + $level++; + break; + + case self::FIRST_CHILD: + default: + $start = $parentLeft + 1; + $level++; + break; + } + $this->shiftRL($em, $config['useObjectClass'], $start, $treeSize, $parentRoot); + if (!$isNewNode && $root === $parentRoot && $left >= $start) { + $left += $treeSize; + $wrapped->setPropertyValue($config['left'], $left); + } + if (!$isNewNode && $root === $parentRoot && $right >= $start) { + $right += $treeSize; + $wrapped->setPropertyValue($config['right'], $right); + } + $newRoot = $parentRoot; + } elseif (!isset($config['root']) || + ($meta->isSingleValuedAssociation($config['root']) && ($newRoot = $meta->getFieldValue($node, $config['root'])))) { + + if (!isset($this->treeEdges[$meta->name])) { + $this->treeEdges[$meta->name] = $this->max($em, $config['useObjectClass'], $newRoot) + 1; + } + + $level = 0; + $parentLeft = 0; + $parentRight = $this->treeEdges[$meta->name]; + $this->treeEdges[$meta->name] += 2; + + switch ($position) { + case self::PREV_SIBLING: + if (property_exists($node, 'sibling')) { + $wrappedSibling = AbstractWrapper::wrap($node->sibling, $em); + $start = $wrappedSibling->getPropertyValue($config['left']); + } else { + $wrapped->setPropertyValue($config['parent'], null); + $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $node); + $start = $parentLeft + 1; + } + break; + + case self::NEXT_SIBLING: + if (property_exists($node, 'sibling')) { + $wrappedSibling = AbstractWrapper::wrap($node->sibling, $em); + $start = $wrappedSibling->getPropertyValue($config['right']) + 1; + } else { + $wrapped->setPropertyValue($config['parent'], null); + $em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $node); + $start = $parentRight; + } + break; + + case self::LAST_CHILD: + $start = $parentRight; + break; + + case self::FIRST_CHILD: + default: + $start = $parentLeft + 1; + break; + } + + $this->shiftRL($em, $config['useObjectClass'], $start, $treeSize, null); + + if (!$isNewNode && $left >= $start) { + $left += $treeSize; + $wrapped->setPropertyValue($config['left'], $left); + } + if (!$isNewNode && $right >= $start) { + $right += $treeSize; + $wrapped->setPropertyValue($config['right'], $right); + } + } else { + $start = 1; + if (isset($config['rootIdentifierMethod'])) { + $method = $config['rootIdentifierMethod']; + $newRoot = $node->$method(); + $repo = $em->getRepository($config['useObjectClass']); + + $criteria = new Criteria(); + $criteria->andWhere(Criteria::expr()->notIn($wrapped->getMetadata()->identifier[0], [$wrapped->getIdentifier()])); + $criteria->andWhere(Criteria::expr()->eq($config['root'], $node->$method())); + $criteria->andWhere(Criteria::expr()->isNull($config['parent'])); + $criteria->andWhere(Criteria::expr()->eq($config['level'], 0)); + $criteria->orderBy([$config['right'] => Criteria::ASC]); + $roots = $repo->matching($criteria)->toArray(); + $last = array_pop($roots); + + $start = ($last) ? $meta->getFieldValue($last, $config['right']) + 1 : 1; + + } else if ($meta->isSingleValuedAssociation($config['root'])) { + $newRoot = $node; + } else { + $newRoot = $wrapped->getIdentifier(); + } + } + + $diff = $start - $left; + + if (!$isNewNode) { + $levelDiff = isset($config['level']) ? $level - $wrapped->getPropertyValue($config['level']) : null; + $this->shiftRangeRL( + $em, + $config['useObjectClass'], + $left, + $right, + $diff, + $root, + $newRoot, + $levelDiff + ); + $this->shiftRL($em, $config['useObjectClass'], $left, -$treeSize, $root); + } else { + $qb = $em->createQueryBuilder(); + $qb->update($config['useObjectClass'], 'node'); + if (isset($config['root'])) { + $qb->set('node.' . $config['root'], ':rid'); + $qb->setParameter('rid', $newRoot); + $wrapped->setPropertyValue($config['root'], $newRoot); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['root'], $newRoot); + } + if (isset($config['level'])) { + $qb->set('node.' . $config['level'], $level); + $wrapped->setPropertyValue($config['level'], $level); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['level'], $level); + } + if (isset($newParent)) { + $wrappedNewParent = AbstractWrapper::wrap($newParent, $em); + $newParentId = $wrappedNewParent->getIdentifier(); + $qb->set('node.' . $config['parent'], ':pid'); + $qb->setParameter('pid', $newParentId); + $wrapped->setPropertyValue($config['parent'], $newParent); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['parent'], $newParent); + } + $qb->set('node.' . $config['left'], $left + $diff); + $qb->set('node.' . $config['right'], $right + $diff); + // node id cannot be null + $qb->where($qb->expr()->eq('node.' . $identifierField, ':id')); + $qb->setParameter('id', $nodeId); + $qb->getQuery()->getSingleScalarResult(); + $wrapped->setPropertyValue($config['left'], $left + $diff); + $wrapped->setPropertyValue($config['right'], $right + $diff); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['left'], $left + $diff); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['right'], $right + $diff); + } + if (isset($this->delayedNodes[$oid])) { + foreach ($this->delayedNodes[$oid] as $nodeData) { + $this->updateNode($em, $nodeData['node'], $node, $nodeData['position']); + } + } + } + + /** + * Get the edge of tree + * + * @param EntityManagerInterface $em + * @param string $class + * @param integer $rootId + * + * @return integer + */ + public function max(EntityManagerInterface $em, $class, $rootId = 0) + { + $meta = $em->getClassMetadata($class); + $config = $this->listener->getConfiguration($em, $meta->name); + $qb = $em->createQueryBuilder(); + $qb->select($qb->expr()->max('node.' . $config['right'])) + ->from($config['useObjectClass'], 'node'); + + if (isset($config['root']) && $rootId) { + $qb->where($qb->expr()->eq('node.' . $config['root'], ':rid')); + $qb->setParameter('rid', $rootId); + } + $query = $qb->getQuery(); + $right = $query->getSingleScalarResult(); + + return intval($right); + } + + /** + * Shift tree left and right values by delta + * + * @param EntityManager $em + * @param string $class + * @param integer $first + * @param integer $delta + * @param EntityManagerInterface $em + * @param string $class + * @param integer $first + * @param integer $delta + * @param integer|string $root + */ + public function shiftRL(EntityManagerInterface $em, $class, $first, $delta, $root = null) + { + $meta = $em->getClassMetadata($class); + $config = $this->listener->getConfiguration($em, $class); + + $sign = ($delta >= 0) ? ' + ' : ' - '; + $absDelta = abs($delta); + $qb = $em->createQueryBuilder(); + $qb->update($config['useObjectClass'], 'node') + ->set('node.' . $config['left'], "node.{$config['left']} {$sign} {$absDelta}") + ->where($qb->expr()->gte('node.' . $config['left'], $first)); + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.' . $config['root'], ':rid')); + $qb->setParameter('rid', $root); + } + $qb->getQuery()->getSingleScalarResult(); + + $qb = $em->createQueryBuilder(); + $qb->update($config['useObjectClass'], 'node') + ->set('node.' . $config['right'], "node.{$config['right']} {$sign} {$absDelta}") + ->where($qb->expr()->gte('node.' . $config['right'], $first)); + if (isset($config['root'])) { + $qb->andWhere($qb->expr()->eq('node.' . $config['root'], ':rid')); + $qb->setParameter('rid', $root); + } + + $qb->getQuery()->getSingleScalarResult(); + // update in memory nodes increases performance, saves some IO + foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $nodes) { + // for inheritance mapped classes, only root is always in the identity map + if ($className !== $meta->rootEntityName) { + continue; + } + foreach ($nodes as $node) { + if ($node instanceof Proxy && !$node->__isInitialized__) { + continue; + } + + $nodeMeta = $em->getClassMetadata(get_class($node)); + + if (!array_key_exists($config['left'], $nodeMeta->getReflectionProperties())) { + continue; + } + + $oid = spl_object_hash($node); + $left = $meta->getReflectionProperty($config['left'])->getValue($node); + $currentRoot = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null; + if ($currentRoot === $root && $left >= $first) { + $meta->getReflectionProperty($config['left'])->setValue($node, $left + $delta); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['left'], $left + $delta); + } + $right = $meta->getReflectionProperty($config['right'])->getValue($node); + if ($currentRoot === $root && $right >= $first) { + $meta->getReflectionProperty($config['right'])->setValue($node, $right + $delta); + $em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['right'], $right + $delta); + } + } + } + } + + /** + * Shift range of right and left values on tree + * depending on tree level difference also + * + * @param EntityManagerInterface $em + * @param string $class + * @param integer $first + * @param integer $last + * @param integer $delta + * @param integer|string $root + * @param integer|string $destRoot + * @param integer $levelDelta + */ + public function shiftRangeRL(EntityManagerInterface $em, $class, $first, $last, $delta, $root = null, $destRoot = null, $levelDelta = null) + { + $meta = $em->getClassMetadata($class); + $config = $this->listener->getConfiguration($em, $class); + + $sign = ($delta >= 0) ? ' + ' : ' - '; + $absDelta = abs($delta); + $levelSign = ($levelDelta >= 0) ? ' + ' : ' - '; + $absLevelDelta = abs($levelDelta); + + $qb = $em->createQueryBuilder(); + $qb->update($config['useObjectClass'], 'node') + ->set('node.' . $config['left'], "node.{$config['left']} {$sign} {$absDelta}") + ->set('node.' . $config['right'], "node.{$config['right']} {$sign} {$absDelta}") + ->where($qb->expr()->gte('node.' . $config['left'], $first)) + ->andWhere($qb->expr()->lte('node.' . $config['right'], $last)); + if (isset($config['root'])) { + $qb->set('node.' . $config['root'], ':drid'); + $qb->setParameter('drid', $destRoot); + $qb->andWhere($qb->expr()->eq('node.' . $config['root'], ':rid')); + $qb->setParameter('rid', $root); + } + if (isset($config['level'])) { + $qb->set('node.' . $config['level'], "node.{$config['level']} {$levelSign} {$absLevelDelta}"); + } + $qb->getQuery()->getSingleScalarResult(); + // update in memory nodes increases performance, saves some IO + foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $nodes) { + // for inheritance mapped classes, only root is always in the identity map + if ($className !== $meta->rootEntityName) { + continue; + } + foreach ($nodes as $node) { + if ($node instanceof Proxy && !$node->__isInitialized__) { + continue; + } + + $nodeMeta = $em->getClassMetadata(get_class($node)); + + if (!array_key_exists($config['left'], $nodeMeta->getReflectionProperties())) { + continue; + } + + $left = $meta->getReflectionProperty($config['left'])->getValue($node); + $right = $meta->getReflectionProperty($config['right'])->getValue($node); + $currentRoot = isset($config['root']) ? $meta->getReflectionProperty($config['root'])->getValue($node) : null; + if ($currentRoot === $root && $left >= $first && $right <= $last) { + $oid = spl_object_hash($node); + $uow = $em->getUnitOfWork(); + + $meta->getReflectionProperty($config['left'])->setValue($node, $left + $delta); + $uow->setOriginalEntityProperty($oid, $config['left'], $left + $delta); + $meta->getReflectionProperty($config['right'])->setValue($node, $right + $delta); + $uow->setOriginalEntityProperty($oid, $config['right'], $right + $delta); + if (isset($config['root'])) { + $meta->getReflectionProperty($config['root'])->setValue($node, $destRoot); + $uow->setOriginalEntityProperty($oid, $config['root'], $destRoot); + } + if (isset($config['level'])) { + $level = $meta->getReflectionProperty($config['level'])->getValue($node); + $meta->getReflectionProperty($config['level'])->setValue($node, $level + $levelDelta); + $uow->setOriginalEntityProperty($oid, $config['level'], $level + $levelDelta); + } + } + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/MaterializedPath.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/MaterializedPath.php new file mode 100644 index 0000000..87ace1f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/MaterializedPath.php @@ -0,0 +1,124 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait MaterializedPath +{ + /** + * @var string + */ + protected $path; + /** + * @var self + */ + protected $parent; + /** + * @var integer + */ + protected $level; + /** + * @var Collection|self[] + */ + protected $children; + /** + * @var string + */ + protected $hash; + + /** + * @param self $parent + * + * @return self + */ + public function setParent(self $parent = null) + { + $this->parent = $parent; + + return $this; + } + + /** + * @return self + */ + public function getParent() + { + return $this->parent; + } + + /** + * @param string $path + * + * @return self + */ + public function setPath($path) + { + $this->path = $path; + + return $this; + } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * @return integer + */ + public function getLevel() + { + return $this->level; + } + + /** + * @param string $hash + * + * @return self + */ + public function setHash($hash) + { + $this->hash = $hash; + + return $this; + } + + /** + * @return string + */ + public function getHash() + { + return $this->hash; + } + + /** + * @param Collection|self[] $children + * + * @return self + */ + public function setChildren($children) + { + $this->children = $children; + + return $this; + } + + /** + * @return Collection|self[] + */ + public function getChildren() + { + return $this->children = $this->children ?: new ArrayCollection(); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSet.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSet.php new file mode 100644 index 0000000..09950ee --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSet.php @@ -0,0 +1,34 @@ += 5.4 + * + * @author Renaat De Muynck + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait NestedSet +{ + + /** + * @var integer + */ + private $root; + + /** + * @var integer + */ + private $level; + + /** + * @var integer + */ + private $left; + + /** + * @var integer + */ + private $right; + +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntity.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntity.php new file mode 100644 index 0000000..df94cd0 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntity.php @@ -0,0 +1,43 @@ += 5.4 + * + * @author Renaat De Muynck + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait NestedSetEntity +{ + /** + * @var integer + * @Gedmo\TreeRoot + * @ORM\Column(name="root", type="integer", nullable=true) + */ + private $root; + + /** + * @var integer + * @Gedmo\TreeLevel + * @ORM\Column(name="lvl", type="integer") + */ + private $level; + + /** + * @var integer + * @Gedmo\TreeLeft + * @ORM\Column(name="lft", type="integer") + */ + private $left; + + /** + * @var integer + * @Gedmo\TreeRight + * @ORM\Column(name="rgt", type="integer") + */ + private $right; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntityUuid.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntityUuid.php new file mode 100644 index 0000000..6d4bed4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Traits/NestedSetEntityUuid.php @@ -0,0 +1,25 @@ += 5.4 + * + * @author Benjamin Lazarecki + * + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +trait NestedSetEntityUuid +{ + use NestedSetEntity; + + /** + * @var string + * @Gedmo\TreeRoot + * @ORM\Column(name="root", type="string", nullable=true) + */ + private $root; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/TreeListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/TreeListener.php new file mode 100644 index 0000000..9264d8b --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/TreeListener.php @@ -0,0 +1,285 @@ + + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class TreeListener extends MappedEventSubscriber +{ + /** + * Tree processing strategies for object classes + * + * @var array + */ + private $strategies = array(); + + /** + * List of strategy instances + * + * @var array + */ + private $strategyInstances = array(); + + /** + * List of used classes on flush + * + * @var array + */ + private $usedClassesOnFlush = array(); + + /** + * Specifies the list of events to listen + * + * @return array + */ + public function getSubscribedEvents() + { + return array( + 'prePersist', + 'preRemove', + 'preUpdate', + 'onFlush', + 'loadClassMetadata', + 'postPersist', + 'postUpdate', + 'postRemove', + ); + } + + /** + * Get the used strategy for tree processing + * + * @param ObjectManager $om + * @param string $class + * + * @return Strategy + */ + public function getStrategy(ObjectManager $om, $class) + { + if (!isset($this->strategies[$class])) { + $config = $this->getConfiguration($om, $class); + if (!$config) { + throw new \Gedmo\Exception\UnexpectedValueException("Tree object class: {$class} must have tree metadata at this point"); + } + $managerName = 'UnsupportedManager'; + if ($om instanceof \Doctrine\ORM\EntityManagerInterface) { + $managerName = 'ORM'; + } elseif ($om instanceof \Doctrine\ODM\MongoDB\DocumentManager) { + $managerName = 'ODM\\MongoDB'; + } + if (!isset($this->strategyInstances[$config['strategy']])) { + $strategyClass = $this->getNamespace().'\\Strategy\\'.$managerName.'\\'.ucfirst($config['strategy']); + + if (!class_exists($strategyClass)) { + throw new \Gedmo\Exception\InvalidArgumentException($managerName." TreeListener does not support tree type: {$config['strategy']}"); + } + $this->strategyInstances[$config['strategy']] = new $strategyClass($this); + } + $this->strategies[$class] = $config['strategy']; + } + + return $this->strategyInstances[$this->strategies[$class]]; + } + + /** + * Looks for Tree objects being updated + * for further processing + * + * @param EventArgs $args + */ + public function onFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + // check all scheduled updates for TreeNodes + foreach ($ea->getScheduledObjectInsertions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($this->getConfiguration($om, $meta->name)) { + $this->usedClassesOnFlush[$meta->name] = null; + $this->getStrategy($om, $meta->name)->processScheduledInsertion($om, $object, $ea); + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + } + } + + foreach ($ea->getScheduledObjectUpdates($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($this->getConfiguration($om, $meta->name)) { + $this->usedClassesOnFlush[$meta->name] = null; + $this->getStrategy($om, $meta->name)->processScheduledUpdate($om, $object, $ea); + } + } + + foreach ($ea->getScheduledObjectDeletions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + if ($this->getConfiguration($om, $meta->name)) { + $this->usedClassesOnFlush[$meta->name] = null; + $this->getStrategy($om, $meta->name)->processScheduledDelete($om, $object); + } + } + + foreach ($this->getStrategiesUsedForObjects($this->usedClassesOnFlush) as $strategy) { + $strategy->onFlushEnd($om, $ea); + } + } + + /** + * Updates tree on Node removal + * + * @param EventArgs $args + */ + public function preRemove(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($this->getConfiguration($om, $meta->name)) { + $this->getStrategy($om, $meta->name)->processPreRemove($om, $object); + } + } + + /** + * Checks for persisted Nodes + * + * @param EventArgs $args + */ + public function prePersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($this->getConfiguration($om, $meta->name)) { + $this->getStrategy($om, $meta->name)->processPrePersist($om, $object); + } + } + + /** + * Checks for updated Nodes + * + * @param EventArgs $args + */ + public function preUpdate(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($this->getConfiguration($om, $meta->name)) { + $this->getStrategy($om, $meta->name)->processPreUpdate($om, $object); + } + } + + /** + * Checks for pending Nodes to fully synchronize + * the tree + * + * @param EventArgs $args + */ + public function postPersist(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($this->getConfiguration($om, $meta->name)) { + $this->getStrategy($om, $meta->name)->processPostPersist($om, $object, $ea); + } + } + + /** + * Checks for pending Nodes to fully synchronize + * the tree + * + * @param EventArgs $args + */ + public function postUpdate(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($this->getConfiguration($om, $meta->name)) { + $this->getStrategy($om, $meta->name)->processPostUpdate($om, $object, $ea); + } + } + + /** + * Checks for pending Nodes to fully synchronize + * the tree + * + * @param EventArgs $args + */ + public function postRemove(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $object = $ea->getObject(); + $meta = $om->getClassMetadata(get_class($object)); + + if ($this->getConfiguration($om, $meta->name)) { + $this->getStrategy($om, $meta->name)->processPostRemove($om, $object, $ea); + } + } + + /** + * Mapps additional metadata + * + * @param EventArgs $eventArgs + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $om = $ea->getObjectManager(); + $meta = $eventArgs->getClassMetadata(); + $this->loadMetadataForObjectClass($om, $meta); + if (isset(self::$configurations[$this->name][$meta->name]) && self::$configurations[$this->name][$meta->name]) { + $this->getStrategy($om, $meta->name)->processMetadataLoad($om, $meta); + } + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } + + /** + * Get the list of strategy instances used for + * given object classes + * + * @param array $classes + * + * @return Strategy[] + */ + protected function getStrategiesUsedForObjects(array $classes) + { + $strategies = array(); + foreach ($classes as $name => $opt) { + if (isset($this->strategies[$name]) && !isset($strategies[$this->strategies[$name]])) { + $strategies[$this->strategies[$name]] = $this->strategyInstances[$this->strategies[$name]]; + } + } + + return $strategies; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadableBaseEventArgs.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadableBaseEventArgs.php new file mode 100644 index 0000000..2c17584 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadableBaseEventArgs.php @@ -0,0 +1,134 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +abstract class UploadableBaseEventArgs extends EventArgs +{ + /** + * The instance of the Uploadable listener that fired this event + * + * @var \Gedmo\Uploadable\UploadableListener + */ + private $uploadableListener; + + /** + * @var \Doctrine\ORM\EntityManagerInterface + */ + private $em; + + /** + * The Uploadable entity + * + * @var object $entity + */ + private $entity; + + /** + * The configuration of the Uploadable extension for this entity class + * + * @var array $extensionConfiguration + */ + private $extensionConfiguration; + + /** + * @var \Gedmo\Uploadable\FileInfo\FileInfoInterface + */ + private $fileInfo; + + /** + * @var string $action - Is the file being created, updated or removed? + * This value can be: CREATE, UPDATE or DELETE. + */ + private $action; + + /** + * @param UploadableListener $listener + * @param \Doctrine\ORM\EntityManagerInterface $em + * @param array $config + * @param FileInfoInterface $fileInfo + * @param object $entity + * @param string $action + */ + public function __construct(UploadableListener $listener, EntityManagerInterface $em, array $config, FileInfoInterface $fileInfo, $entity, $action) + { + $this->uploadableListener = $listener; + $this->em = $em; + $this->config = $config; + $this->fileInfo = $fileInfo; + $this->entity = $entity; + $this->action = $action; + } + + /** + * Retrieve the associated listener + * + * @return \Gedmo\Uploadable\UploadableListener + */ + public function getListener() + { + return $this->uploadableListener; + } + + /** + * Retrieve associated EntityManager + * + * @return \Doctrine\ORM\EntityManagerInterface + */ + public function getEntityManager() + { + return $this->em; + } + + /** + * Retrieve associated Entity + * + * @return object + */ + public function getEntity() + { + return $this->entity; + } + + /** + * Retrieve associated Uploadable extension configuration + * + * @return array + */ + public function getExtensionConfiguration() + { + return $this->extensionConfiguration; + } + + /** + * Retrieve the FileInfo associated with this entity. + * + * @return \Gedmo\Uploadable\FileInfo\FileInfoInterface + */ + public function getFileInfo() + { + return $this->fileInfo; + } + + /** + * Retrieve the action being performed to the entity: CREATE, UPDATE or DELETE + * + * @return string + */ + public function getAction() + { + return $this->action; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePostFileProcessEventArgs.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePostFileProcessEventArgs.php new file mode 100644 index 0000000..a75d0fd --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePostFileProcessEventArgs.php @@ -0,0 +1,15 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class UploadablePostFileProcessEventArgs extends UploadableBaseEventArgs +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePreFileProcessEventArgs.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePreFileProcessEventArgs.php new file mode 100644 index 0000000..41a667f --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Event/UploadablePreFileProcessEventArgs.php @@ -0,0 +1,15 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class UploadablePreFileProcessEventArgs extends UploadableBaseEventArgs +{ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Events.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Events.php new file mode 100644 index 0000000..8876ddf --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Events.php @@ -0,0 +1,34 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +final class Events +{ + private function __construct() + { + } + /** + * The uploadablePreFileProcess event occurs before a file is processed inside + * the Uploadable listener. This means it happens before the file is validated and moved + * to the configured path. + * + * @var string + */ + const uploadablePreFileProcess = 'uploadablePreFileProcess'; + /** + * The uploadablePostFileProcess event occurs after a file is processed inside + * the Uploadable listener. This means it happens after the file is validated and moved + * to the configured path. + * + * @var string + */ + const uploadablePostFileProcess = 'uploadablePostFileProcess'; +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoArray.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoArray.php new file mode 100644 index 0000000..384f0e8 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoArray.php @@ -0,0 +1,62 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class FileInfoArray implements FileInfoInterface +{ + protected $fileInfo; + + public function __construct(array $fileInfo) + { + $keys = array('error', 'size', 'type', 'tmp_name', 'name'); + + foreach ($keys as $k) { + if (!isset($fileInfo[$k])) { + $msg = 'There are missing keys in the fileInfo. '; + $msg .= 'Keys needed: '.implode(',', $keys); + + throw new \RuntimeException($msg); + } + } + + $this->fileInfo = $fileInfo; + } + + public function getTmpName() + { + return $this->fileInfo['tmp_name']; + } + + public function getName() + { + return $this->fileInfo['name']; + } + + public function getSize() + { + return $this->fileInfo['size']; + } + + public function getType() + { + return $this->fileInfo['type']; + } + + public function getError() + { + return $this->fileInfo['error']; + } + + public function isUploadedFile() + { + return true; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoInterface.php new file mode 100644 index 0000000..f0b7da7 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FileInfo/FileInfoInterface.php @@ -0,0 +1,27 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +interface FileInfoInterface +{ + public function getTmpName(); + public function getName(); + public function getSize(); + public function getType(); + public function getError(); + + /** + * This method must return true if the file is coming from $_FILES, or false instead. + * + * @return bool + */ + public function isUploadedFile(); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumeric.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumeric.php new file mode 100644 index 0000000..88c27c1 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorAlphanumeric.php @@ -0,0 +1,25 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class FilenameGeneratorAlphanumeric implements FilenameGeneratorInterface +{ + /** + * @inheritDoc + */ + public static function generate($filename, $extension, $object = null) + { + return preg_replace('/[^a-z0-9]+/', '-', strtolower($filename)).$extension; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorInterface.php new file mode 100644 index 0000000..baf0e56 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorInterface.php @@ -0,0 +1,25 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +interface FilenameGeneratorInterface +{ + /** + * Generates a new filename + * + * @param string - Filename without extension + * @param string - Extension with dot: .jpg, .gif, etc + * @param $object + * + * @return string + */ + public static function generate($filename, $extension, $object = null); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorSha1.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorSha1.php new file mode 100644 index 0000000..3dcdeb4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/FilenameGenerator/FilenameGeneratorSha1.php @@ -0,0 +1,22 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class FilenameGeneratorSha1 implements FilenameGeneratorInterface +{ + /** + * @inheritDoc + */ + public static function generate($filename, $extension, $object = null) + { + return sha1(uniqid($filename.$extension, true)).$extension; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Annotation.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Annotation.php new file mode 100644 index 0000000..6405deb --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Annotation.php @@ -0,0 +1,102 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Annotation extends AbstractAnnotationDriver +{ + /** + * Annotation to define that this object is loggable + */ + const UPLOADABLE = 'Gedmo\\Mapping\\Annotation\\Uploadable'; + const UPLOADABLE_FILE_MIME_TYPE = 'Gedmo\\Mapping\\Annotation\\UploadableFileMimeType'; + const UPLOADABLE_FILE_NAME = 'Gedmo\\Mapping\\Annotation\\UploadableFileName'; + const UPLOADABLE_FILE_PATH = 'Gedmo\\Mapping\\Annotation\\UploadableFilePath'; + const UPLOADABLE_FILE_SIZE = 'Gedmo\\Mapping\\Annotation\\UploadableFileSize'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $class = $this->getMetaReflectionClass($meta); + + // class annotations + if ($annot = $this->reader->getClassAnnotation($class, self::UPLOADABLE)) { + $config['uploadable'] = true; + $config['allowOverwrite'] = $annot->allowOverwrite; + $config['appendNumber'] = $annot->appendNumber; + $config['path'] = $annot->path; + $config['pathMethod'] = $annot->pathMethod; + $config['fileMimeTypeField'] = false; + $config['fileNameField'] = false; + $config['filePathField'] = false; + $config['fileSizeField'] = false; + $config['callback'] = $annot->callback; + $config['filenameGenerator'] = $annot->filenameGenerator; + $config['maxSize'] = (double) $annot->maxSize; + $config['allowedTypes'] = $annot->allowedTypes; + $config['disallowedTypes'] = $annot->disallowedTypes; + + foreach ($class->getProperties() as $prop) { + if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_MIME_TYPE)) { + $config['fileMimeTypeField'] = $prop->getName(); + } + + if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_NAME)) { + $config['fileNameField'] = $prop->getName(); + } + + if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_PATH)) { + $config['filePathField'] = $prop->getName(); + } + + if ($this->reader->getPropertyAnnotation($prop, self::UPLOADABLE_FILE_SIZE)) { + $config['fileSizeField'] = $prop->getName(); + } + } + + Validator::validateConfiguration($meta, $config); + } + + /* + // Code in case we need to identify entities which are not Uploadables, but have associations + // with other Uploadable entities + + } else { + // We need to check if this class has a relation with Uploadable entities + $associations = $meta->getAssociationMappings(); + + foreach ($associations as $field => $association) { + $refl = new \ReflectionClass($association['targetEntity']); + + if ($annot = $this->reader->getClassAnnotation($refl, self::UPLOADABLE)) { + $config['hasUploadables'] = true; + + if (!isset($config['uploadables'])) { + $config['uploadables'] = array(); + } + + $config['uploadables'][] = array( + 'class' => $association['targetEntity'], + 'property' => $association['fieldName'] + ); + } + } + }*/ + + $this->validateFullMetadata($meta, $config); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Xml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Xml.php new file mode 100644 index 0000000..c52e6f1 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Xml.php @@ -0,0 +1,87 @@ + + * @author Gediminas Morkevicius + * @author Miha Vrhovnik + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Xml extends BaseXml +{ + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + /** + * @var \SimpleXmlElement $xml + */ + $xml = $this->_getMapping($meta->name); + $xmlDoctrine = $xml; + $xml = $xml->children(self::GEDMO_NAMESPACE_URI); + + if ($xmlDoctrine->getName() == 'entity' || $xmlDoctrine->getName() == 'mapped-superclass') { + if (isset($xml->uploadable)) { + $xmlUploadable = $xml->uploadable; + $config['uploadable'] = true; + $config['allowOverwrite'] = $this->_isAttributeSet($xmlUploadable, 'allow-overwrite') ? + (bool) $this->_getAttribute($xmlUploadable, 'allow-overwrite') : false; + $config['appendNumber'] = $this->_isAttributeSet($xmlUploadable, 'append-number') ? + (bool) $this->_getAttribute($xmlUploadable, 'append-number') : false; + $config['path'] = $this->_isAttributeSet($xmlUploadable, 'path') ? + $this->_getAttribute($xml->{'uploadable'}, 'path') : ''; + $config['pathMethod'] = $this->_isAttributeSet($xmlUploadable, 'path-method') ? + $this->_getAttribute($xml->{'uploadable'}, 'path-method') : ''; + $config['callback'] = $this->_isAttributeSet($xmlUploadable, 'callback') ? + $this->_getAttribute($xml->{'uploadable'}, 'callback') : ''; + $config['fileMimeTypeField'] = false; + $config['fileNameField'] = false; + $config['filePathField'] = false; + $config['fileSizeField'] = false; + $config['filenameGenerator'] = $this->_isAttributeSet($xmlUploadable, 'filename-generator') ? + $this->_getAttribute($xml->{'uploadable'}, 'filename-generator') : + Validator::FILENAME_GENERATOR_NONE; + $config['maxSize'] = $this->_isAttributeSet($xmlUploadable, 'max-size') ? + (double) $this->_getAttribute($xml->{'uploadable'}, 'max-size') : + (double) 0; + $config['allowedTypes'] = $this->_isAttributeSet($xmlUploadable, 'allowed-types') ? + $this->_getAttribute($xml->{'uploadable'}, 'allowed-types') : + ''; + $config['disallowedTypes'] = $this->_isAttributeSet($xmlUploadable, 'disallowed-types') ? + $this->_getAttribute($xml->{'uploadable'}, 'disallowed-types') : + ''; + + if (isset($xmlDoctrine->field)) { + foreach ($xmlDoctrine->field as $mapping) { + $mappingDoctrine = $mapping; + $mapping = $mapping->children(self::GEDMO_NAMESPACE_URI); + + $field = $this->_getAttribute($mappingDoctrine, 'name'); + + if (isset($mapping->{'uploadable-file-mime-type'})) { + $config['fileMimeTypeField'] = $field; + } elseif (isset($mapping->{'uploadable-file-size'})) { + $config['fileSizeField'] = $field; + } elseif (isset($mapping->{'uploadable-file-name'})) { + $config['fileNameField'] = $field; + } elseif (isset($mapping->{'uploadable-file-path'})) { + $config['filePathField'] = $field; + } + } + } + + Validator::validateConfiguration($meta, $config); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Yaml.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Yaml.php new file mode 100644 index 0000000..ca186e4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Driver/Yaml.php @@ -0,0 +1,93 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class Yaml extends File implements Driver +{ + /** + * File extension + * @var string + */ + protected $_extension = '.dcm.yml'; + + /** + * {@inheritDoc} + */ + public function readExtendedMetadata($meta, array &$config) + { + $mapping = $this->_getMapping($meta->name); + + if (isset($mapping['gedmo'])) { + $classMapping = $mapping['gedmo']; + + if (isset($classMapping['uploadable'])) { + $uploadable = $classMapping['uploadable']; + + $config['uploadable'] = true; + $config['allowOverwrite'] = isset($uploadable['allowOverwrite']) ? + (bool) $uploadable['allowOverwrite'] : false; + $config['appendNumber'] = isset($uploadable['appendNumber']) ? + (bool) $uploadable['appendNumber'] : false; + $config['path'] = isset($uploadable['path']) ? $uploadable['path'] : ''; + $config['pathMethod'] = isset($uploadable['pathMethod']) ? $uploadable['pathMethod'] : ''; + $config['callback'] = isset($uploadable['callback']) ? $uploadable['callback'] : ''; + $config['fileMimeTypeField'] = false; + $config['fileNameField'] = false; + $config['filePathField'] = false; + $config['fileSizeField'] = false; + $config['filenameGenerator'] = isset($uploadable['filenameGenerator']) ? + $uploadable['filenameGenerator'] : + Validator::FILENAME_GENERATOR_NONE; + $config['maxSize'] = isset($uploadable['maxSize']) ? + (double) $uploadable['maxSize'] : + (double) 0; + $config['allowedTypes'] = isset($uploadable['allowedTypes']) ? + $uploadable['allowedTypes'] : + ''; + $config['disallowedTypes'] = isset($uploadable['disallowedTypes']) ? + $uploadable['disallowedTypes'] : + ''; + + if (isset($mapping['fields'])) { + foreach ($mapping['fields'] as $field => $info) { + if (isset($info['gedmo']) && array_key_exists(0, $info['gedmo'])) { + if ($info['gedmo'][0] === 'uploadableFileMimeType') { + $config['fileMimeTypeField'] = $field; + } elseif ($info['gedmo'][0] === 'uploadableFileSize') { + $config['fileSizeField'] = $field; + } elseif ($info['gedmo'][0] === 'uploadableFileName') { + $config['fileNameField'] = $field; + } elseif ($info['gedmo'][0] === 'uploadableFilePath') { + $config['filePathField'] = $field; + } + } + } + } + + Validator::validateConfiguration($meta, $config); + } + } + } + + /** + * {@inheritDoc} + */ + protected function _loadMappingFile($file) + { + return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file)); + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Validator.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Validator.php new file mode 100644 index 0000000..60bdffa --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Mapping/Validator.php @@ -0,0 +1,242 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ + +class Validator +{ + const UPLOADABLE_FILE_MIME_TYPE = 'UploadableFileMimeType'; + const UPLOADABLE_FILE_NAME = 'UploadableFileName'; + const UPLOADABLE_FILE_PATH = 'UploadableFilePath'; + const UPLOADABLE_FILE_SIZE = 'UploadableFileSize'; + const FILENAME_GENERATOR_SHA1 = 'SHA1'; + const FILENAME_GENERATOR_ALPHANUMERIC = 'ALPHANUMERIC'; + const FILENAME_GENERATOR_NONE = 'NONE'; + + /** + * Determines if we should throw an exception in the case the "allowedTypes" and + * "disallowedTypes" options are BOTH set. Useful for testing purposes + * + * @var bool + */ + public static $enableMimeTypesConfigException = true; + + /** + * List of types which are valid for UploadableFileMimeType field + * + * @var array + */ + public static $validFileMimeTypeTypes = array( + 'string', + ); + + /** + * List of types which are valid for UploadableFileName field + * + * @var array + */ + public static $validFileNameTypes = array( + 'string', + ); + + /** + * List of types which are valid for UploadableFilePath field + * + * @var array + */ + public static $validFilePathTypes = array( + 'string', + ); + + /** + * List of types which are valid for UploadableFileSize field for ORM + * + * @var array + */ + public static $validFileSizeTypes = array( + 'decimal', + ); + + /** + * List of types which are valid for UploadableFileSize field for ODM + * + * @var array + */ + public static $validFileSizeTypesODM = array( + 'float', + ); + + /** + * Whether to validate if the directory of the file exists and is writable, useful to disable it when using + * stream wrappers which don't support is_dir (like Gaufrette) + * + * @var bool + */ + public static $validateWritableDirectory = true; + + public static function validateFileNameField(ClassMetadata $meta, $field) + { + self::validateField($meta, $field, self::UPLOADABLE_FILE_NAME, self::$validFileNameTypes); + } + + public static function validateFileMimeTypeField(ClassMetadata $meta, $field) + { + self::validateField($meta, $field, self::UPLOADABLE_FILE_MIME_TYPE, self::$validFileMimeTypeTypes); + } + + public static function validateFilePathField(ClassMetadata $meta, $field) + { + self::validateField($meta, $field, self::UPLOADABLE_FILE_PATH, self::$validFilePathTypes); + } + + public static function validateFileSizeField(ClassMetadata $meta, $field) + { + if ($meta instanceof \Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo) { + self::validateField($meta, $field, self::UPLOADABLE_FILE_SIZE, self::$validFileSizeTypesODM); + } else { + self::validateField($meta, $field, self::UPLOADABLE_FILE_SIZE, self::$validFileSizeTypes); + } + } + + public static function validateField($meta, $field, $uploadableField, $validFieldTypes) + { + if ($meta->isMappedSuperclass) { + return; + } + + $fieldMapping = $meta->getFieldMapping($field); + + if (!in_array($fieldMapping['type'], $validFieldTypes)) { + $msg = 'Field "%s" to work as an "%s" field must be of one of the following types: "%s".'; + + throw new InvalidMappingException(sprintf($msg, + $field, + $uploadableField, + implode(', ', $validFieldTypes) + )); + } + } + + public static function validatePath($path) + { + if (!is_string($path) || $path === '') { + throw new UploadableInvalidPathException('Path must be a string containing the path to a valid directory.'); + } + + if (!self::$validateWritableDirectory) { + return; + } + + if (!is_dir($path) && !@mkdir($path, 0777, true)) { + throw new UploadableInvalidPathException(sprintf('Unable to create "%s" directory.', + $path + )); + } + + if (!is_writable($path)) { + throw new UploadableCantWriteException(sprintf('Directory "%s" does is not writable.', + $path + )); + } + } + + public static function validateConfiguration(ClassMetadata $meta, array &$config) + { + if (!$config['filePathField'] && !$config['fileNameField']) { + throw new InvalidMappingException(sprintf('Class "%s" must have an UploadableFilePath or UploadableFileName field.', + $meta->name + )); + } + + $refl = $meta->getReflectionClass(); + + if ($config['pathMethod'] !== '' && !$refl->hasMethod($config['pathMethod'])) { + throw new InvalidMappingException(sprintf('Class "%s" doesn\'t have method "%s"!', + $meta->name, + $config['pathMethod'] + )); + } + + if ($config['callback'] !== '' && !$refl->hasMethod($config['callback'])) { + throw new InvalidMappingException(sprintf('Class "%s" doesn\'t have method "%s"!', + $meta->name, + $config['callback'] + )); + } + + $config['maxSize'] = (double) $config['maxSize']; + + if ($config['maxSize'] < 0) { + throw new InvalidMappingException(sprintf('Option "maxSize" must be a number >= 0 for class "%s".', + $meta->name + )); + } + + if (self::$enableMimeTypesConfigException && ($config['allowedTypes'] !== '' && $config['disallowedTypes'] !== '')) { + $msg = 'You\'ve set "allowedTypes" and "disallowedTypes" options. You must set only one in class "%s".'; + + throw new InvalidMappingException(sprintf($msg, + $meta->name + )); + } + + $config['allowedTypes'] = $config['allowedTypes'] ? (strpos($config['allowedTypes'], ',') !== false ? + explode(',', $config['allowedTypes']) : array($config['allowedTypes'])) : false; + $config['disallowedTypes'] = $config['disallowedTypes'] ? (strpos($config['disallowedTypes'], ',') !== false ? + explode(',', $config['disallowedTypes']) : array($config['disallowedTypes'])) : false; + + if ($config['fileNameField']) { + self::validateFileNameField($meta, $config['fileNameField']); + } + + if ($config['filePathField']) { + self::validateFilePathField($meta, $config['filePathField']); + } + + if ($config['fileMimeTypeField']) { + self::validateFileMimeTypeField($meta, $config['fileMimeTypeField']); + } + + if ($config['fileSizeField']) { + self::validateFileSizeField($meta, $config['fileSizeField']); + } + + switch ((string) $config['filenameGenerator']) { + case self::FILENAME_GENERATOR_ALPHANUMERIC: + case self::FILENAME_GENERATOR_SHA1: + case self::FILENAME_GENERATOR_NONE: + break; + default: + $ok = false; + + if (class_exists($config['filenameGenerator'])) { + $refl = new \ReflectionClass($config['filenameGenerator']); + + if ($refl->implementsInterface('Gedmo\Uploadable\FilenameGenerator\FilenameGeneratorInterface')) { + $ok = true; + } + } + + if (!$ok) { + $msg = 'Class "%s" needs a valid value for filenameGenerator. It can be: SHA1, ALPHANUMERIC, NONE or '; + $msg .= 'a class implementing FileGeneratorInterface.'; + + throw new InvalidMappingException(sprintf($msg, + $meta->name + )); + } + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesser.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesser.php new file mode 100644 index 0000000..5bce8c5 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesser.php @@ -0,0 +1,41 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class MimeTypeGuesser implements MimeTypeGuesserInterface +{ + public function guess($filePath) + { + if (!is_file($filePath)) { + throw new UploadableInvalidFileException(sprintf('File "%s" does not exist.', + $filePath + )); + } + + if (!is_readable($filePath)) { + throw new UploadableFileNotReadableException(sprintf('File "%s" is not readable.', + $filePath + )); + } + + if (function_exists('finfo_open')) { + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE)) { + return null; + } + + return $finfo->file($filePath); + } + + return null; + } +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesserInterface.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesserInterface.php new file mode 100644 index 0000000..64a37b6 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypeGuesserInterface.php @@ -0,0 +1,15 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface MimeTypeGuesserInterface +{ + public function guess($filePath); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypesExtensionsMap.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypesExtensionsMap.php new file mode 100644 index 0000000..7a6f2a4 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/MimeType/MimeTypesExtensionsMap.php @@ -0,0 +1,722 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +abstract class MimeTypesExtensionsMap +{ + /** + * Map of mime types and their default extensions. + * + * @var array + */ + public static $map = 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/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/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/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/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/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/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.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.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.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.hzn-3d-crossword' => 'x3d', + '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.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.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.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-authorware-bin' => 'aab', + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bittorrent' => 'torrent', + 'application/x-bzip' => 'bz', + 'application/x-bzip2' => 'bz2', + 'application/x-cdlink' => 'vcd', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => 'deb', + '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-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-futuresplash' => 'spl', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-mobipocket-ebook' => 'prc', + 'application/x-ms-application' => 'application', + '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-pkcs12' => 'p12', + 'application/x-pkcs7-certificates' => 'p7b', + 'application/x-pkcs7-certreqresp' => 'p7r', + 'application/x-rar-compressed' => 'rar', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-stuffit' => 'sit', + 'application/x-stuffitx' => 'sitx', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => 'texinfo', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => 'der', + 'application/x-xfig' => 'fig', + 'application/x-xpinstall' => 'xpi', + '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/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/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-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', + '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/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => 'jpeg', + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + '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.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => 'fh', + 'image/x-icon' => 'ico', + '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-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', + '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/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/x-asm' => 's', + 'text/x-c' => 'c', + 'text/x-fortran' => 'f', + 'text/x-pascal' => 'p', + 'text/x-java-source' => 'java', + 'text/x-setext' => 'etx', + '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-ms-asf' => 'asf', + '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', + 'x-conference/x-cooltalk' => 'ice', + ); +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Uploadable.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Uploadable.php new file mode 100644 index 0000000..7abfb89 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/Uploadable.php @@ -0,0 +1,27 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +interface Uploadable +{ + // this interface is not necessary to implement + + /** + * @gedmo:Uploadable + * to mark the class as Uploadable use class annotation @gedmo:Uploadable + * this object will be able Uploadable + * example: + * + * @gedmo:Uploadable + * class MyEntity + */ +} diff --git a/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/UploadableListener.php b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/UploadableListener.php new file mode 100644 index 0000000..21f55b3 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/lib/Gedmo/Uploadable/UploadableListener.php @@ -0,0 +1,779 @@ + + * @author Gediminas Morkevicius + * @license MIT License (http://www.opensource.org/licenses/mit-license.php) + */ +class UploadableListener extends MappedEventSubscriber +{ + const ACTION_INSERT = 'INSERT'; + const ACTION_UPDATE = 'UPDATE'; + + /** + * Default path to move files in + * + * @var string + */ + private $defaultPath; + + /** + * Mime type guesser + * + * @var \Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface + */ + private $mimeTypeGuesser; + + /** + * Default FileInfoInterface class + * + * @var string + */ + private $defaultFileInfoClass = 'Gedmo\Uploadable\FileInfo\FileInfoArray'; + + /** + * Array of files to remove on postFlush + * + * @var array + */ + private $pendingFileRemovals = array(); + + /** + * Array of FileInfoInterface objects. The index is the hash of the entity owner + * of the FileInfoInterface object. + * + * @var array + */ + private $fileInfoObjects = array(); + + public function __construct(MimeTypeGuesserInterface $mimeTypeGuesser = null) + { + $this->mimeTypeGuesser = $mimeTypeGuesser ? $mimeTypeGuesser : new MimeTypeGuesser(); + } + + /** + * {@inheritdoc} + */ + public function getSubscribedEvents() + { + return array( + 'loadClassMetadata', + 'preFlush', + 'onFlush', + 'postFlush', + ); + } + + /** + * This event is needed in special cases where the entity needs to be updated, but it only has the + * file field modified. Since we can't mark an entity as "dirty" in the "addEntityFileInfo" method, + * doctrine thinks the entity has no changes, which produces that the "onFlush" event gets never called. + * Here we mark the entity as dirty, so the "onFlush" event gets called, and the file is processed. + * + * @param \Doctrine\Common\EventArgs $args + */ + public function preFlush(EventArgs $args) + { + if (empty($this->fileInfoObjects)) { + // Nothing to do + return; + } + + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + $first = reset($this->fileInfoObjects); + $meta = $om->getClassMetadata(get_class($first['entity'])); + $config = $this->getConfiguration($om, $meta->name); + + foreach ($this->fileInfoObjects as $info) { + $entity = $info['entity']; + + // If the entity is in the identity map, it means it will be updated. We need to force the + // "dirty check" here by "modifying" the path. We are actually setting the same value, but + // this will mark the entity as dirty, and the "onFlush" event will be fired, even if there's + // no other change in the entity's fields apart from the file itself. + if ($uow->isInIdentityMap($entity)) { + if ($config['filePathField']) { + $path = $this->getFilePathFieldValue($meta, $config, $entity); + $uow->propertyChanged($entity, $config['filePathField'], $path, $path); + } else { + $fileName = $this->getFileNameFieldValue($meta, $config, $entity); + $uow->propertyChanged($entity, $config['fileNameField'], $fileName, $fileName); + } + $uow->scheduleForUpdate($entity); + } + } + } + + /** + * Handle file-uploading depending on the action + * being done with objects + * + * @param \Doctrine\Common\EventArgs $args + */ + public function onFlush(EventArgs $args) + { + $ea = $this->getEventAdapter($args); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + + // Do we need to upload files? + foreach ($this->fileInfoObjects as $info) { + $entity = $info['entity']; + $scheduledForInsert = $uow->isScheduledForInsert($entity); + $scheduledForUpdate = $uow->isScheduledForUpdate($entity); + $action = ($scheduledForInsert || $scheduledForUpdate) ? + ($scheduledForInsert ? self::ACTION_INSERT : self::ACTION_UPDATE) : + false; + + if ($action) { + $this->processFile($ea, $entity, $action); + } + } + + // Do we need to remove any files? + foreach ($ea->getScheduledObjectDeletions($uow) as $object) { + $meta = $om->getClassMetadata(get_class($object)); + + if ($config = $this->getConfiguration($om, $meta->name)) { + if (isset($config['uploadable']) && $config['uploadable']) { + $this->addFileRemoval($meta, $config, $object); + } + } + } + } + + /** + * Handle removal of files + * + * @param \Doctrine\Common\EventArgs $args + */ + public function postFlush(EventArgs $args) + { + if (!empty($this->pendingFileRemovals)) { + foreach ($this->pendingFileRemovals as $file) { + $this->removeFile($file); + } + + $this->pendingFileRemovals = array(); + } + + $this->fileInfoObjects = array(); + } + + /** + * If it's a Uploadable object, verify if the file was uploaded. + * If that's the case, process it. + * + * @param \Gedmo\Mapping\Event\AdapterInterface $ea + * @param object $object + * @param string $action + * + * @throws \Gedmo\Exception\UploadableNoPathDefinedException + * @throws \Gedmo\Exception\UploadableCouldntGuessMimeTypeException + * @throws \Gedmo\Exception\UploadableMaxSizeException + * @throws \Gedmo\Exception\UploadableInvalidMimeTypeException + */ + public function processFile(AdapterInterface $ea, $object, $action) + { + $oid = spl_object_hash($object); + $om = $ea->getObjectManager(); + $uow = $om->getUnitOfWork(); + $meta = $om->getClassMetadata(get_class($object)); + $config = $this->getConfiguration($om, $meta->name); + + if (!$config || !isset($config['uploadable']) || !$config['uploadable']) { + // Nothing to do + return; + } + + $refl = $meta->getReflectionClass(); + $fileInfo = $this->fileInfoObjects[$oid]['fileInfo']; + $evm = $om->getEventManager(); + + if ($evm->hasListeners(Events::uploadablePreFileProcess)) { + $evm->dispatchEvent(Events::uploadablePreFileProcess, new UploadablePreFileProcessEventArgs( + $this, + $om, + $config, + $fileInfo, + $object, + $action + )); + } + + // Validations + if ($config['maxSize'] > 0 && $fileInfo->getSize() > $config['maxSize']) { + $msg = 'File "%s" exceeds the maximum allowed size of %d bytes. File size: %d bytes'; + + throw new UploadableMaxSizeException(sprintf($msg, + $fileInfo->getName(), + $config['maxSize'], + $fileInfo->getSize() + )); + } + + $mime = $this->mimeTypeGuesser->guess($fileInfo->getTmpName()); + + if (!$mime) { + throw new UploadableCouldntGuessMimeTypeException(sprintf('Couldn\'t guess mime type for file "%s".', + $fileInfo->getName() + )); + } + + if ($config['allowedTypes'] || $config['disallowedTypes']) { + $ok = $config['allowedTypes'] ? false : true; + $mimes = $config['allowedTypes'] ? $config['allowedTypes'] : $config['disallowedTypes']; + + foreach ($mimes as $m) { + if ($mime === $m) { + $ok = $config['allowedTypes'] ? true : false; + + break; + } + } + + if (!$ok) { + throw new UploadableInvalidMimeTypeException(sprintf('Invalid mime type "%s" for file "%s".', + $mime, + $fileInfo->getName() + )); + } + } + + $path = $this->getPath($meta, $config, $object); + + if ($action === self::ACTION_UPDATE) { + // First we add the original file to the pendingFileRemovals array + $this->addFileRemoval($meta, $config, $object); + } + + // We generate the filename based on configuration + $generatorNamespace = 'Gedmo\Uploadable\FilenameGenerator'; + + switch ($config['filenameGenerator']) { + case Validator::FILENAME_GENERATOR_ALPHANUMERIC: + $generatorClass = $generatorNamespace.'\FilenameGeneratorAlphanumeric'; + + break; + case Validator::FILENAME_GENERATOR_SHA1: + $generatorClass = $generatorNamespace.'\FilenameGeneratorSha1'; + + break; + case Validator::FILENAME_GENERATOR_NONE: + $generatorClass = false; + + break; + default: + $generatorClass = $config['filenameGenerator']; + } + + $info = $this->moveFile($fileInfo, $path, $generatorClass, $config['allowOverwrite'], $config['appendNumber'], $object); + + // We override the mime type with the guessed one + $info['fileMimeType'] = $mime; + + if ($config['callback'] !== '') { + $callbackMethod = $refl->getMethod($config['callback']); + $callbackMethod->setAccessible(true); + + $callbackMethod->invokeArgs($object, array($info)); + } + + if ($config['filePathField']) { + $this->updateField($object, $uow, $ea, $meta, $config['filePathField'], $info['filePath']); + } + + if ($config['fileNameField']) { + $this->updateField($object, $uow, $ea, $meta, $config['fileNameField'], $info['fileName']); + } + + if ($config['fileMimeTypeField']) { + $this->updateField($object, $uow, $ea, $meta, $config['fileMimeTypeField'], $info['fileMimeType']); + } + + if ($config['fileSizeField']) { + $this->updateField($object, $uow, $ea, $meta, $config['fileSizeField'], $info['fileSize']); + } + + $ea->recomputeSingleObjectChangeSet($uow, $meta, $object); + + if ($evm->hasListeners(Events::uploadablePostFileProcess)) { + $evm->dispatchEvent(Events::uploadablePostFileProcess, new UploadablePostFileProcessEventArgs( + $this, + $om, + $config, + $fileInfo, + $object, + $action + )); + } + + unset($this->fileInfoObjects[$oid]); + } + + /** + * @param ClassMetadata $meta + * @param array $config + * @param object $object Entity + * + * @return string + * + * @throws UploadableNoPathDefinedException + */ + protected function getPath(ClassMetadata $meta, array $config, $object) + { + $path = $config['path']; + + if ($path === '') { + $defaultPath = $this->getDefaultPath(); + if ($config['pathMethod'] !== '') { + $pathMethod = $meta->getReflectionClass()->getMethod($config['pathMethod']); + $pathMethod->setAccessible(true); + $path = $pathMethod->invoke($object, $defaultPath); + } elseif ($defaultPath !== null) { + $path = $defaultPath; + } else { + $msg = 'You have to define the path to save files either in the listener, or in the class "%s"'; + + throw new UploadableNoPathDefinedException( + sprintf($msg, $meta->name) + ); + } + } + + Validator::validatePath($path); + $path = rtrim($path, '\/'); + + return $path; + } + + /** + * @param ClassMetadata $meta + * @param array $config + * @param object $object Entity + */ + protected function addFileRemoval($meta, $config, $object) + { + if ($config['filePathField']) { + $this->pendingFileRemovals[] = $this->getFilePathFieldValue($meta, $config, $object); + } else { + $path = $this->getPath($meta, $config, $object); + $fileName = $this->getFileNameFieldValue($meta, $config, $object); + $this->pendingFileRemovals[] = $path.DIRECTORY_SEPARATOR.$fileName; + } + } + + /** + * @param string $filePath + */ + protected function cancelFileRemoval($filePath) + { + $k = array_search($filePath, $this->pendingFileRemovals, true); + + if (false !== $k) { + unset($this->pendingFileRemovals[$k]); + } + } + + /** + * Returns value of the entity's property + * + * @param ClassMetadata $meta + * @param string $propertyName + * @param object $object + * + * @return mixed + */ + protected function getPropertyValueFromObject(ClassMetadata $meta, $propertyName, $object) + { + $refl = $meta->getReflectionClass(); + $filePathField = $refl->getProperty($propertyName); + $filePathField->setAccessible(true); + $filePath = $filePathField->getValue($object); + + return $filePath; + } + + /** + * Returns the path of the entity's file + * + * @param ClassMetadata $meta + * @param array $config + * @param object $object + * + * @return string + */ + protected function getFilePathFieldValue(ClassMetadata $meta, array $config, $object) + { + return $this->getPropertyValueFromObject($meta, $config['filePathField'], $object); + } + + /** + * Returns the name of the entity's file + * + * @param ClassMetadata $meta + * @param array $config + * @param object $object + * + * @return string + */ + protected function getFileNameFieldValue(ClassMetadata $meta, array $config, $object) + { + return $this->getPropertyValueFromObject($meta, $config['fileNameField'], $object); + } + + /** + * Simple wrapper for the function "unlink" to ease testing + * + * @param string $filePath + * + * @return bool + */ + public function removeFile($filePath) + { + if (is_file($filePath)) { + return @unlink($filePath); + } + + return false; + } + + /** + * Moves the file to the specified path + * + * @param FileInfoInterface $fileInfo + * @param string $path + * @param bool $filenameGeneratorClass + * @param bool $overwrite + * @param bool $appendNumber + * @param object $object + * + * @return array + * + * @throws \Gedmo\Exception\UploadableUploadException + * @throws \Gedmo\Exception\UploadableNoFileException + * @throws \Gedmo\Exception\UploadableExtensionException + * @throws \Gedmo\Exception\UploadableIniSizeException + * @throws \Gedmo\Exception\UploadableFormSizeException + * @throws \Gedmo\Exception\UploadableFileAlreadyExistsException + * @throws \Gedmo\Exception\UploadablePartialException + * @throws \Gedmo\Exception\UploadableNoTmpDirException + * @throws \Gedmo\Exception\UploadableCantWriteException + */ + public function moveFile(FileInfoInterface $fileInfo, $path, $filenameGeneratorClass = false, $overwrite = false, $appendNumber = false, $object) + { + if ($fileInfo->getError() > 0) { + switch ($fileInfo->getError()) { + case 1: + $msg = 'Size of uploaded file "%s" exceeds limit imposed by directive "upload_max_filesize" in php.ini'; + + throw new UploadableIniSizeException(sprintf($msg, $fileInfo->getName())); + case 2: + $msg = 'Size of uploaded file "%s" exceeds limit imposed by option MAX_FILE_SIZE in your form.'; + + throw new UploadableFormSizeException(sprintf($msg, $fileInfo->getName())); + case 3: + $msg = 'File "%s" was partially uploaded.'; + + throw new UploadablePartialException(sprintf($msg, $fileInfo->getName())); + case 4: + $msg = 'No file was uploaded!'; + + throw new UploadableNoFileException(sprintf($msg, $fileInfo->getName())); + case 6: + $msg = 'Upload failed. Temp dir is missing.'; + + throw new UploadableNoTmpDirException($msg); + case 7: + $msg = 'File "%s" couldn\'t be uploaded because directory is not writable.'; + + throw new UploadableCantWriteException(sprintf($msg, $fileInfo->getName())); + case 8: + $msg = 'A PHP Extension stopped the uploaded for some reason.'; + + throw new UploadableExtensionException(sprintf($msg, $fileInfo->getName())); + default: + throw new UploadableUploadException(sprintf('There was an unknown problem while uploading file "%s"', + $fileInfo->getName() + )); + } + } + + $info = array( + 'fileName' => '', + 'fileExtension' => '', + 'fileWithoutExt' => '', + 'origFileName' => '', + 'filePath' => '', + 'fileMimeType' => $fileInfo->getType(), + 'fileSize' => $fileInfo->getSize(), + ); + + $info['fileName'] = basename($fileInfo->getName()); + $info['filePath'] = $path.'/'.$info['fileName']; + + $hasExtension = strrpos($info['fileName'], '.'); + + if ($hasExtension) { + $info['fileExtension'] = substr($info['filePath'], strrpos($info['filePath'], '.')); + $info['fileWithoutExt'] = substr($info['filePath'], 0, strrpos($info['filePath'], '.')); + } else { + $info['fileWithoutExt'] = $info['fileName']; + } + + // Save the original filename for later use + $info['origFileName'] = $info['fileName']; + + // Now we generate the filename using the configured class + if ($filenameGeneratorClass) { + $filename = $filenameGeneratorClass::generate( + str_replace($path.'/', '', $info['fileWithoutExt']), + $info['fileExtension'], + $object + ); + $info['filePath'] = str_replace( + '/'.$info['fileName'], + '/'.$filename, + $info['filePath'] + ); + $info['fileName'] = $filename; + + if ($pos = strrpos($info['filePath'], '.')) { + // ignores positions like "./file" at 0 see #915 + $info['fileWithoutExt'] = substr($info['filePath'], 0, $pos); + } else { + $info['fileWithoutExt'] = $info['filePath']; + } + } + + if (is_file($info['filePath'])) { + if ($overwrite) { + $this->cancelFileRemoval($info['filePath']); + $this->removeFile($info['filePath']); + } elseif ($appendNumber) { + $counter = 1; + $info['filePath'] = $info['fileWithoutExt'].'-'.$counter.$info['fileExtension']; + + do { + $info['filePath'] = $info['fileWithoutExt'].'-'.(++$counter).$info['fileExtension']; + } while (is_file($info['filePath'])); + } else { + throw new UploadableFileAlreadyExistsException(sprintf('File "%s" already exists!', + $info['filePath'] + )); + } + } + + if (!$this->doMoveFile($fileInfo->getTmpName(), $info['filePath'], $fileInfo->isUploadedFile())) { + throw new UploadableUploadException(sprintf('File "%s" was not uploaded, or there was a problem moving it to the location "%s".', + $fileInfo->getName(), + $path + )); + } + + return $info; + } + + /** + * Simple wrapper method used to move the file. If it's an uploaded file + * it will use the "move_uploaded_file method. If it's not, it will + * simple move it + * + * @param string $source Source file + * @param string $dest Destination file + * @param bool $isUploadedFile Whether this is an uploaded file? + * + * @return bool + */ + public function doMoveFile($source, $dest, $isUploadedFile = true) + { + return $isUploadedFile ? @move_uploaded_file($source, $dest) : @copy($source, $dest); + } + + /** + * Maps additional metadata + * + * @param EventArgs $eventArgs + */ + public function loadClassMetadata(EventArgs $eventArgs) + { + $ea = $this->getEventAdapter($eventArgs); + $this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata()); + } + + /** + * Sets the default path + * + * @param string $path + * + * @return void + */ + public function setDefaultPath($path) + { + $this->defaultPath = $path; + } + + /** + * Returns default path + * + * @return string + */ + public function getDefaultPath() + { + return $this->defaultPath; + } + + /** + * Sets file info default class + * + * @param string $defaultFileInfoClass + * + * @return void + */ + public function setDefaultFileInfoClass($defaultFileInfoClass) + { + $fileInfoInterface = 'Gedmo\\Uploadable\\FileInfo\\FileInfoInterface'; + $refl = is_string($defaultFileInfoClass) && class_exists($defaultFileInfoClass) ? + new \ReflectionClass($defaultFileInfoClass) : + false; + + if (!$refl || !$refl->implementsInterface($fileInfoInterface)) { + $msg = sprintf('Default FileInfo class must be a valid class, and it must implement "%s".', + $fileInfoInterface + ); + + throw new \Gedmo\Exception\InvalidArgumentException($msg); + } + + $this->defaultFileInfoClass = $defaultFileInfoClass; + } + + /** + * Returns file info default class + * + * @return string + */ + public function getDefaultFileInfoClass() + { + return $this->defaultFileInfoClass; + } + + /** + * Adds a FileInfoInterface object for the given entity + * + * @param object $entity + * @param array|FileInfoInterface $fileInfo + * + * @throws \RuntimeException + */ + public function addEntityFileInfo($entity, $fileInfo) + { + $fileInfoClass = $this->getDefaultFileInfoClass(); + $fileInfo = is_array($fileInfo) ? new $fileInfoClass($fileInfo) : $fileInfo; + + if (!$fileInfo instanceof FileInfoInterface) { + $msg = 'You must pass an instance of FileInfoInterface or a valid array for entity of class "%s".'; + + throw new \RuntimeException(sprintf($msg, get_class($entity))); + } + + $this->fileInfoObjects[spl_object_hash($entity)] = array( + 'entity' => $entity, + 'fileInfo' => $fileInfo, + ); + } + + /** + * @param object $entity + * + * @return FileInfoInterface + */ + public function getEntityFileInfo($entity) + { + $oid = spl_object_hash($entity); + + if (!isset($this->fileInfoObjects[$oid])) { + throw new \RuntimeException(sprintf('There\'s no FileInfoInterface object for entity of class "%s".', + get_class($entity) + )); + } + + return $this->fileInfoObjects[$oid]['fileInfo']; + } + + /** + * {@inheritDoc} + */ + protected function getNamespace() + { + return __NAMESPACE__; + } + + /** + * @param \Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface $mimeTypeGuesser + */ + public function setMimeTypeGuesser(MimeTypeGuesserInterface $mimeTypeGuesser) + { + $this->mimeTypeGuesser = $mimeTypeGuesser; + } + + /** + * @return \Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface + */ + public function getMimeTypeGuesser() + { + return $this->mimeTypeGuesser; + } + + /** + * @param object $object + * @param object $uow + * @param AdapterInterface $ea + * @param ClassMetadata $meta + * @param String $field + * @param mixed $value + * @param bool $notifyPropertyChanged + */ + protected function updateField($object, $uow, AdapterInterface $ea, ClassMetadata $meta, $field, $value, $notifyPropertyChanged = true) + { + $property = $meta->getReflectionProperty($field); + $oldValue = $property->getValue($object); + $property->setValue($object, $value); + + if ($notifyPropertyChanged && $object instanceof NotifyPropertyChanged) { + $uow = $ea->getObjectManager()->getUnitOfWork(); + $uow->propertyChanged($object, $field, $oldValue, $value); + } + } +} diff --git a/vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-1.xsd b/vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-1.xsd new file mode 100644 index 0000000..f476678 --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-1.xsd @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-2.xsd b/vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-2.xsd new file mode 100644 index 0000000..4efec0a --- /dev/null +++ b/vendor/gedmo/doctrine-extensions/schemas/orm/doctrine-extensions-mapping-2-2.xsd @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/monolog/monolog/.php_cs b/vendor/monolog/monolog/.php_cs new file mode 100644 index 0000000..366ccd0 --- /dev/null +++ b/vendor/monolog/monolog/.php_cs @@ -0,0 +1,59 @@ + + +For the full copyright and license information, please view the LICENSE +file that was distributed with this source code. +EOF; + +$finder = Symfony\CS\Finder::create() + ->files() + ->name('*.php') + ->exclude('Fixtures') + ->in(__DIR__.'/src') + ->in(__DIR__.'/tests') +; + +return Symfony\CS\Config::create() + ->setUsingCache(true) + //->setUsingLinter(false) + ->setRiskyAllowed(true) + ->setRules(array( + '@PSR2' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_return' => true, + 'header_comment' => array('header' => $header), + 'include' => true, + 'long_array_syntax' => true, + 'method_separation' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_blank_lines_between_uses' => true, + 'no_duplicate_semicolons' => true, + 'no_extra_consecutive_blank_lines' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'no_unused_imports' => true, + 'object_operator_without_whitespace' => true, + 'phpdoc_align' => true, + 'phpdoc_indent' => true, + 'phpdoc_no_access' => true, + 'phpdoc_no_package' => true, + 'phpdoc_order' => true, + 'phpdoc_scalar' => true, + 'phpdoc_trim' => true, + 'phpdoc_type_to_var' => true, + 'psr0' => true, + 'single_blank_line_before_namespace' => true, + 'spaces_cast' => true, + 'standardize_not_equals' => true, + 'ternary_operator_spaces' => true, + 'trailing_comma_in_multiline_array' => true, + 'whitespacy_lines' => true, + )) + ->finder($finder) +; diff --git a/vendor/monolog/monolog/CHANGELOG.md b/vendor/monolog/monolog/CHANGELOG.md new file mode 100644 index 0000000..cd1142d --- /dev/null +++ b/vendor/monolog/monolog/CHANGELOG.md @@ -0,0 +1,342 @@ +### 1.23.0 (2017-06-19) + + * Improved SyslogUdpHandler's support for RFC5424 and added optional `$ident` argument + * Fixed GelfHandler truncation to be per field and not per message + * Fixed compatibility issue with PHP <5.3.6 + * Fixed support for headless Chrome in ChromePHPHandler + * Fixed support for latest Aws SDK in DynamoDbHandler + * Fixed support for SwiftMailer 6.0+ in SwiftMailerHandler + +### 1.22.1 (2017-03-13) + + * Fixed lots of minor issues in the new Slack integrations + * Fixed support for allowInlineLineBreaks in LineFormatter when formatting exception backtraces + +### 1.22.0 (2016-11-26) + + * Added SlackbotHandler and SlackWebhookHandler to set up Slack integration more easily + * Added MercurialProcessor to add mercurial revision and branch names to log records + * Added support for AWS SDK v3 in DynamoDbHandler + * Fixed fatal errors occuring when normalizing generators that have been fully consumed + * Fixed RollbarHandler to include a level (rollbar level), monolog_level (original name), channel and datetime (unix) + * Fixed RollbarHandler not flushing records automatically, calling close() explicitly is not necessary anymore + * Fixed SyslogUdpHandler to avoid sending empty frames + * Fixed a few PHP 7.0 and 7.1 compatibility issues + +### 1.21.0 (2016-07-29) + + * Break: Reverted the addition of $context when the ErrorHandler handles regular php errors from 1.20.0 as it was causing issues + * Added support for more formats in RotatingFileHandler::setFilenameFormat as long as they have Y, m and d in order + * Added ability to format the main line of text the SlackHandler sends by explictly setting a formatter on the handler + * Added information about SoapFault instances in NormalizerFormatter + * Added $handleOnlyReportedErrors option on ErrorHandler::registerErrorHandler (default true) to allow logging of all errors no matter the error_reporting level + +### 1.20.0 (2016-07-02) + + * Added FingersCrossedHandler::activate() to manually trigger the handler regardless of the activation policy + * Added StreamHandler::getUrl to retrieve the stream's URL + * Added ability to override addRow/addTitle in HtmlFormatter + * Added the $context to context information when the ErrorHandler handles a regular php error + * Deprecated RotatingFileHandler::setFilenameFormat to only support 3 formats: Y, Y-m and Y-m-d + * Fixed WhatFailureGroupHandler to work with PHP7 throwables + * Fixed a few minor bugs + +### 1.19.0 (2016-04-12) + + * Break: StreamHandler will not close streams automatically that it does not own. If you pass in a stream (not a path/url), then it will not close it for you. You can retrieve those using getStream() if needed + * Added DeduplicationHandler to remove duplicate records from notifications across multiple requests, useful for email or other notifications on errors + * Added ability to use `%message%` and other LineFormatter replacements in the subject line of emails sent with NativeMailHandler and SwiftMailerHandler + * Fixed HipChatHandler handling of long messages + +### 1.18.2 (2016-04-02) + + * Fixed ElasticaFormatter to use more precise dates + * Fixed GelfMessageFormatter sending too long messages + +### 1.18.1 (2016-03-13) + + * Fixed SlackHandler bug where slack dropped messages randomly + * Fixed RedisHandler issue when using with the PHPRedis extension + * Fixed AmqpHandler content-type being incorrectly set when using with the AMQP extension + * Fixed BrowserConsoleHandler regression + +### 1.18.0 (2016-03-01) + + * Added optional reduction of timestamp precision via `Logger->useMicrosecondTimestamps(false)`, disabling it gets you a bit of performance boost but reduces the precision to the second instead of microsecond + * Added possibility to skip some extra stack frames in IntrospectionProcessor if you have some library wrapping Monolog that is always adding frames + * Added `Logger->withName` to clone a logger (keeping all handlers) with a new name + * Added FluentdFormatter for the Fluentd unix socket protocol + * Added HandlerWrapper base class to ease the creation of handler wrappers, just extend it and override as needed + * Added support for replacing context sub-keys using `%context.*%` in LineFormatter + * Added support for `payload` context value in RollbarHandler + * Added setRelease to RavenHandler to describe the application version, sent with every log + * Added support for `fingerprint` context value in RavenHandler + * Fixed JSON encoding errors that would gobble up the whole log record, we now handle those more gracefully by dropping chars as needed + * Fixed write timeouts in SocketHandler and derivatives, set to 10sec by default, lower it with `setWritingTimeout()` + * Fixed PHP7 compatibility with regard to Exception/Throwable handling in a few places + +### 1.17.2 (2015-10-14) + + * Fixed ErrorHandler compatibility with non-Monolog PSR-3 loggers + * Fixed SlackHandler handling to use slack functionalities better + * Fixed SwiftMailerHandler bug when sending multiple emails they all had the same id + * Fixed 5.3 compatibility regression + +### 1.17.1 (2015-08-31) + + * Fixed RollbarHandler triggering PHP notices + +### 1.17.0 (2015-08-30) + + * Added support for `checksum` and `release` context/extra values in RavenHandler + * Added better support for exceptions in RollbarHandler + * Added UidProcessor::getUid + * Added support for showing the resource type in NormalizedFormatter + * Fixed IntrospectionProcessor triggering PHP notices + +### 1.16.0 (2015-08-09) + + * Added IFTTTHandler to notify ifttt.com triggers + * Added Logger::setHandlers() to allow setting/replacing all handlers + * Added $capSize in RedisHandler to cap the log size + * Fixed StreamHandler creation of directory to only trigger when the first log write happens + * Fixed bug in the handling of curl failures + * Fixed duplicate logging of fatal errors when both error and fatal error handlers are registered in monolog's ErrorHandler + * Fixed missing fatal errors records with handlers that need to be closed to flush log records + * Fixed TagProcessor::addTags support for associative arrays + +### 1.15.0 (2015-07-12) + + * Added addTags and setTags methods to change a TagProcessor + * Added automatic creation of directories if they are missing for a StreamHandler to open a log file + * Added retry functionality to Loggly, Cube and Mandrill handlers so they retry up to 5 times in case of network failure + * Fixed process exit code being incorrectly reset to 0 if ErrorHandler::registerExceptionHandler was used + * Fixed HTML/JS escaping in BrowserConsoleHandler + * Fixed JSON encoding errors being silently suppressed (PHP 5.5+ only) + +### 1.14.0 (2015-06-19) + + * Added PHPConsoleHandler to send record to Chrome's PHP Console extension and library + * Added support for objects implementing __toString in the NormalizerFormatter + * Added support for HipChat's v2 API in HipChatHandler + * Added Logger::setTimezone() to initialize the timezone monolog should use in case date.timezone isn't correct for your app + * Added an option to send formatted message instead of the raw record on PushoverHandler via ->useFormattedMessage(true) + * Fixed curl errors being silently suppressed + +### 1.13.1 (2015-03-09) + + * Fixed regression in HipChat requiring a new token to be created + +### 1.13.0 (2015-03-05) + + * Added Registry::hasLogger to check for the presence of a logger instance + * Added context.user support to RavenHandler + * Added HipChat API v2 support in the HipChatHandler + * Added NativeMailerHandler::addParameter to pass params to the mail() process + * Added context data to SlackHandler when $includeContextAndExtra is true + * Added ability to customize the Swift_Message per-email in SwiftMailerHandler + * Fixed SwiftMailerHandler to lazily create message instances if a callback is provided + * Fixed serialization of INF and NaN values in Normalizer and LineFormatter + +### 1.12.0 (2014-12-29) + + * Break: HandlerInterface::isHandling now receives a partial record containing only a level key. This was always the intent and does not break any Monolog handler but is strictly speaking a BC break and you should check if you relied on any other field in your own handlers. + * Added PsrHandler to forward records to another PSR-3 logger + * Added SamplingHandler to wrap around a handler and include only every Nth record + * Added MongoDBFormatter to support better storage with MongoDBHandler (it must be enabled manually for now) + * Added exception codes in the output of most formatters + * Added LineFormatter::includeStacktraces to enable exception stack traces in logs (uses more than one line) + * Added $useShortAttachment to SlackHandler to minify attachment size and $includeExtra to append extra data + * Added $host to HipChatHandler for users of private instances + * Added $transactionName to NewRelicHandler and support for a transaction_name context value + * Fixed MandrillHandler to avoid outputing API call responses + * Fixed some non-standard behaviors in SyslogUdpHandler + +### 1.11.0 (2014-09-30) + + * Break: The NewRelicHandler extra and context data are now prefixed with extra_ and context_ to avoid clashes. Watch out if you have scripts reading those from the API and rely on names + * Added WhatFailureGroupHandler to suppress any exception coming from the wrapped handlers and avoid chain failures if a logging service fails + * Added MandrillHandler to send emails via the Mandrillapp.com API + * Added SlackHandler to log records to a Slack.com account + * Added FleepHookHandler to log records to a Fleep.io account + * Added LogglyHandler::addTag to allow adding tags to an existing handler + * Added $ignoreEmptyContextAndExtra to LineFormatter to avoid empty [] at the end + * Added $useLocking to StreamHandler and RotatingFileHandler to enable flock() while writing + * Added support for PhpAmqpLib in the AmqpHandler + * Added FingersCrossedHandler::clear and BufferHandler::clear to reset them between batches in long running jobs + * Added support for adding extra fields from $_SERVER in the WebProcessor + * Fixed support for non-string values in PrsLogMessageProcessor + * Fixed SwiftMailer messages being sent with the wrong date in long running scripts + * Fixed minor PHP 5.6 compatibility issues + * Fixed BufferHandler::close being called twice + +### 1.10.0 (2014-06-04) + + * Added Logger::getHandlers() and Logger::getProcessors() methods + * Added $passthruLevel argument to FingersCrossedHandler to let it always pass some records through even if the trigger level is not reached + * Added support for extra data in NewRelicHandler + * Added $expandNewlines flag to the ErrorLogHandler to create multiple log entries when a message has multiple lines + +### 1.9.1 (2014-04-24) + + * Fixed regression in RotatingFileHandler file permissions + * Fixed initialization of the BufferHandler to make sure it gets flushed after receiving records + * Fixed ChromePHPHandler and FirePHPHandler's activation strategies to be more conservative + +### 1.9.0 (2014-04-20) + + * Added LogEntriesHandler to send logs to a LogEntries account + * Added $filePermissions to tweak file mode on StreamHandler and RotatingFileHandler + * Added $useFormatting flag to MemoryProcessor to make it send raw data in bytes + * Added support for table formatting in FirePHPHandler via the table context key + * Added a TagProcessor to add tags to records, and support for tags in RavenHandler + * Added $appendNewline flag to the JsonFormatter to enable using it when logging to files + * Added sound support to the PushoverHandler + * Fixed multi-threading support in StreamHandler + * Fixed empty headers issue when ChromePHPHandler received no records + * Fixed default format of the ErrorLogHandler + +### 1.8.0 (2014-03-23) + + * Break: the LineFormatter now strips newlines by default because this was a bug, set $allowInlineLineBreaks to true if you need them + * Added BrowserConsoleHandler to send logs to any browser's console via console.log() injection in the output + * Added FilterHandler to filter records and only allow those of a given list of levels through to the wrapped handler + * Added FlowdockHandler to send logs to a Flowdock account + * Added RollbarHandler to send logs to a Rollbar account + * Added HtmlFormatter to send prettier log emails with colors for each log level + * Added GitProcessor to add the current branch/commit to extra record data + * Added a Monolog\Registry class to allow easier global access to pre-configured loggers + * Added support for the new official graylog2/gelf-php lib for GelfHandler, upgrade if you can by replacing the mlehner/gelf-php requirement + * Added support for HHVM + * Added support for Loggly batch uploads + * Added support for tweaking the content type and encoding in NativeMailerHandler + * Added $skipClassesPartials to tweak the ignored classes in the IntrospectionProcessor + * Fixed batch request support in GelfHandler + +### 1.7.0 (2013-11-14) + + * Added ElasticSearchHandler to send logs to an Elastic Search server + * Added DynamoDbHandler and ScalarFormatter to send logs to Amazon's Dynamo DB + * Added SyslogUdpHandler to send logs to a remote syslogd server + * Added LogglyHandler to send logs to a Loggly account + * Added $level to IntrospectionProcessor so it only adds backtraces when needed + * Added $version to LogstashFormatter to allow using the new v1 Logstash format + * Added $appName to NewRelicHandler + * Added configuration of Pushover notification retries/expiry + * Added $maxColumnWidth to NativeMailerHandler to change the 70 chars default + * Added chainability to most setters for all handlers + * Fixed RavenHandler batch processing so it takes the message from the record with highest priority + * Fixed HipChatHandler batch processing so it sends all messages at once + * Fixed issues with eAccelerator + * Fixed and improved many small things + +### 1.6.0 (2013-07-29) + + * Added HipChatHandler to send logs to a HipChat chat room + * Added ErrorLogHandler to send logs to PHP's error_log function + * Added NewRelicHandler to send logs to NewRelic's service + * Added Monolog\ErrorHandler helper class to register a Logger as exception/error/fatal handler + * Added ChannelLevelActivationStrategy for the FingersCrossedHandler to customize levels by channel + * Added stack traces output when normalizing exceptions (json output & co) + * Added Monolog\Logger::API constant (currently 1) + * Added support for ChromePHP's v4.0 extension + * Added support for message priorities in PushoverHandler, see $highPriorityLevel and $emergencyLevel + * Added support for sending messages to multiple users at once with the PushoverHandler + * Fixed RavenHandler's support for batch sending of messages (when behind a Buffer or FingersCrossedHandler) + * Fixed normalization of Traversables with very large data sets, only the first 1000 items are shown now + * Fixed issue in RotatingFileHandler when an open_basedir restriction is active + * Fixed minor issues in RavenHandler and bumped the API to Raven 0.5.0 + * Fixed SyslogHandler issue when many were used concurrently with different facilities + +### 1.5.0 (2013-04-23) + + * Added ProcessIdProcessor to inject the PID in log records + * Added UidProcessor to inject a unique identifier to all log records of one request/run + * Added support for previous exceptions in the LineFormatter exception serialization + * Added Monolog\Logger::getLevels() to get all available levels + * Fixed ChromePHPHandler so it avoids sending headers larger than Chrome can handle + +### 1.4.1 (2013-04-01) + + * Fixed exception formatting in the LineFormatter to be more minimalistic + * Fixed RavenHandler's handling of context/extra data, requires Raven client >0.1.0 + * Fixed log rotation in RotatingFileHandler to work with long running scripts spanning multiple days + * Fixed WebProcessor array access so it checks for data presence + * Fixed Buffer, Group and FingersCrossed handlers to make use of their processors + +### 1.4.0 (2013-02-13) + + * Added RedisHandler to log to Redis via the Predis library or the phpredis extension + * Added ZendMonitorHandler to log to the Zend Server monitor + * Added the possibility to pass arrays of handlers and processors directly in the Logger constructor + * Added `$useSSL` option to the PushoverHandler which is enabled by default + * Fixed ChromePHPHandler and FirePHPHandler issue when multiple instances are used simultaneously + * Fixed header injection capability in the NativeMailHandler + +### 1.3.1 (2013-01-11) + + * Fixed LogstashFormatter to be usable with stream handlers + * Fixed GelfMessageFormatter levels on Windows + +### 1.3.0 (2013-01-08) + + * Added PSR-3 compliance, the `Monolog\Logger` class is now an instance of `Psr\Log\LoggerInterface` + * Added PsrLogMessageProcessor that you can selectively enable for full PSR-3 compliance + * Added LogstashFormatter (combine with SocketHandler or StreamHandler to send logs to Logstash) + * Added PushoverHandler to send mobile notifications + * Added CouchDBHandler and DoctrineCouchDBHandler + * Added RavenHandler to send data to Sentry servers + * Added support for the new MongoClient class in MongoDBHandler + * Added microsecond precision to log records' timestamps + * Added `$flushOnOverflow` param to BufferHandler to flush by batches instead of losing + the oldest entries + * Fixed normalization of objects with cyclic references + +### 1.2.1 (2012-08-29) + + * Added new $logopts arg to SyslogHandler to provide custom openlog options + * Fixed fatal error in SyslogHandler + +### 1.2.0 (2012-08-18) + + * Added AmqpHandler (for use with AMQP servers) + * Added CubeHandler + * Added NativeMailerHandler::addHeader() to send custom headers in mails + * Added the possibility to specify more than one recipient in NativeMailerHandler + * Added the possibility to specify float timeouts in SocketHandler + * Added NOTICE and EMERGENCY levels to conform with RFC 5424 + * Fixed the log records to use the php default timezone instead of UTC + * Fixed BufferHandler not being flushed properly on PHP fatal errors + * Fixed normalization of exotic resource types + * Fixed the default format of the SyslogHandler to avoid duplicating datetimes in syslog + +### 1.1.0 (2012-04-23) + + * Added Monolog\Logger::isHandling() to check if a handler will + handle the given log level + * Added ChromePHPHandler + * Added MongoDBHandler + * Added GelfHandler (for use with Graylog2 servers) + * Added SocketHandler (for use with syslog-ng for example) + * Added NormalizerFormatter + * Added the possibility to change the activation strategy of the FingersCrossedHandler + * Added possibility to show microseconds in logs + * Added `server` and `referer` to WebProcessor output + +### 1.0.2 (2011-10-24) + + * Fixed bug in IE with large response headers and FirePHPHandler + +### 1.0.1 (2011-08-25) + + * Added MemoryPeakUsageProcessor and MemoryUsageProcessor + * Added Monolog\Logger::getName() to get a logger's channel name + +### 1.0.0 (2011-07-06) + + * Added IntrospectionProcessor to get info from where the logger was called + * Fixed WebProcessor in CLI + +### 1.0.0-RC1 (2011-07-01) + + * Initial release diff --git a/vendor/monolog/monolog/LICENSE b/vendor/monolog/monolog/LICENSE new file mode 100644 index 0000000..1647321 --- /dev/null +++ b/vendor/monolog/monolog/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2016 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 +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/monolog/monolog/README.md b/vendor/monolog/monolog/README.md new file mode 100644 index 0000000..7d8ade5 --- /dev/null +++ b/vendor/monolog/monolog/README.md @@ -0,0 +1,95 @@ +# Monolog - Logging for PHP [![Build Status](https://img.shields.io/travis/Seldaek/monolog.svg)](https://travis-ci.org/Seldaek/monolog) + +[![Total Downloads](https://img.shields.io/packagist/dt/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) +[![Latest Stable Version](https://img.shields.io/packagist/v/monolog/monolog.svg)](https://packagist.org/packages/monolog/monolog) +[![Reference Status](https://www.versioneye.com/php/monolog:monolog/reference_badge.svg)](https://www.versioneye.com/php/monolog:monolog/references) + + +Monolog sends your logs to files, sockets, inboxes, databases and various +web services. See the complete list of handlers below. Special handlers +allow you to build advanced logging strategies. + +This library implements the [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +interface that you can type-hint against in your own libraries to keep +a maximum of interoperability. You can also use it in your applications to +make sure you can always use another compatible logger at a later time. +As of 1.11.0 Monolog public APIs will also accept PSR-3 log levels. +Internally Monolog still uses its own level scheme since it predates PSR-3. + +## Installation + +Install the latest version with + +```bash +$ composer require monolog/monolog +``` + +## Basic Usage + +```php +pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); + +// add records to the log +$log->addWarning('Foo'); +$log->addError('Bar'); +``` + +## Documentation + +- [Usage Instructions](doc/01-usage.md) +- [Handlers, Formatters and Processors](doc/02-handlers-formatters-processors.md) +- [Utility classes](doc/03-utilities.md) +- [Extending Monolog](doc/04-extending.md) + +## Third Party Packages + +Third party handlers, formatters and processors are +[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You +can also add your own there if you publish one. + +## About + +### Requirements + +- Monolog works with PHP 5.3 or above, and is also tested to work with HHVM. + +### Submitting bugs and feature requests + +Bugs and feature request are tracked on [GitHub](https://github.com/Seldaek/monolog/issues) + +### Framework Integrations + +- Frameworks and libraries using [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) + can be used very easily with Monolog since it implements the interface. +- [Symfony2](http://symfony.com) comes out of the box with Monolog. +- [Silex](http://silex.sensiolabs.org/) comes out of the box with Monolog. +- [Laravel 4 & 5](http://laravel.com/) come out of the box with Monolog. +- [Lumen](http://lumen.laravel.com/) comes out of the box with Monolog. +- [PPI](http://www.ppi.io/) comes out of the box with Monolog. +- [CakePHP](http://cakephp.org/) is usable with Monolog via the [cakephp-monolog](https://github.com/jadb/cakephp-monolog) plugin. +- [Slim](http://www.slimframework.com/) is usable with Monolog via the [Slim-Monolog](https://github.com/Flynsarmy/Slim-Monolog) log writer. +- [XOOPS 2.6](http://xoops.org/) comes out of the box with Monolog. +- [Aura.Web_Project](https://github.com/auraphp/Aura.Web_Project) comes out of the box with Monolog. +- [Nette Framework](http://nette.org/en/) can be used with Monolog via [Kdyby/Monolog](https://github.com/Kdyby/Monolog) extension. +- [Proton Micro Framework](https://github.com/alexbilbie/Proton) comes out of the box with Monolog. + +### Author + +Jordi Boggiano - -
    +See also the list of [contributors](https://github.com/Seldaek/monolog/contributors) which participated in this project. + +### License + +Monolog is licensed under the MIT License - see the `LICENSE` file for details + +### Acknowledgements + +This library is heavily inspired by Python's [Logbook](http://packages.python.org/Logbook/) +library, although most concepts have been adjusted to fit to the PHP world. diff --git a/vendor/monolog/monolog/composer.json b/vendor/monolog/monolog/composer.json new file mode 100644 index 0000000..3b0c880 --- /dev/null +++ b/vendor/monolog/monolog/composer.json @@ -0,0 +1,66 @@ +{ + "name": "monolog/monolog", + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "keywords": ["log", "logging", "psr-3"], + "homepage": "http://github.com/Seldaek/monolog", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5", + "graylog2/gelf-php": "~1.0", + "sentry/sentry": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "doctrine/couchdb": "~1.0@dev", + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "php-amqplib/php-amqplib": "~2.4", + "swiftmailer/swiftmailer": "^5.3|^6.0", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit-mock-objects": "2.3.0", + "jakub-onderka/php-parallel-lint": "0.9" + }, + "_": "phpunit/phpunit-mock-objects required in 2.3.0 due to https://github.com/sebastianbergmann/phpunit-mock-objects/issues/223 - needs hhvm 3.8+ on travis", + "suggest": { + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "sentry/sentry": "Allow sending log messages to a Sentry server", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "php-console/php-console": "Allow sending log messages to Google Chrome" + }, + "autoload": { + "psr-4": {"Monolog\\": "src/Monolog"} + }, + "autoload-dev": { + "psr-4": {"Monolog\\": "tests/Monolog"} + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "scripts": { + "test": [ + "parallel-lint . --exclude vendor", + "phpunit" + ] + } +} diff --git a/vendor/monolog/monolog/doc/01-usage.md b/vendor/monolog/monolog/doc/01-usage.md new file mode 100644 index 0000000..8e2551f --- /dev/null +++ b/vendor/monolog/monolog/doc/01-usage.md @@ -0,0 +1,231 @@ +# Using Monolog + +- [Installation](#installation) +- [Core Concepts](#core-concepts) +- [Log Levels](#log-levels) +- [Configuring a logger](#configuring-a-logger) +- [Adding extra data in the records](#adding-extra-data-in-the-records) +- [Leveraging channels](#leveraging-channels) +- [Customizing the log format](#customizing-the-log-format) + +## Installation + +Monolog is available on Packagist ([monolog/monolog](http://packagist.org/packages/monolog/monolog)) +and as such installable via [Composer](http://getcomposer.org/). + +```bash +composer require monolog/monolog +``` + +If you do not use Composer, you can grab the code from GitHub, and use any +PSR-0 compatible autoloader (e.g. the [Symfony2 ClassLoader component](https://github.com/symfony/ClassLoader)) +to load Monolog classes. + +## Core Concepts + +Every `Logger` instance has a channel (name) and a stack of handlers. Whenever +you add a record to the logger, it traverses the handler stack. Each handler +decides whether it fully handled the record, and if so, the propagation of the +record ends there. + +This allows for flexible logging setups, for example having a `StreamHandler` at +the bottom of the stack that will log anything to disk, and on top of that add +a `MailHandler` that will send emails only when an error message is logged. +Handlers also have a `$bubble` property which defines whether they block the +record or not if they handled it. In this example, setting the `MailHandler`'s +`$bubble` argument to false means that records handled by the `MailHandler` will +not propagate to the `StreamHandler` anymore. + +You can create many `Logger`s, each defining a channel (e.g.: db, request, +router, ..) and each of them combining various handlers, which can be shared +or not. The channel is reflected in the logs and allows you to easily see or +filter records. + +Each Handler also has a Formatter, a default one with settings that make sense +will be created if you don't set one. The formatters normalize and format +incoming records so that they can be used by the handlers to output useful +information. + +Custom severity levels are not available. Only the eight +[RFC 5424](http://tools.ietf.org/html/rfc5424) levels (debug, info, notice, +warning, error, critical, alert, emergency) are present for basic filtering +purposes, but for sorting and other use cases that would require +flexibility, you should add Processors to the Logger that can add extra +information (tags, user ip, ..) to the records before they are handled. + +## Log Levels + +Monolog supports the logging levels described by [RFC 5424](http://tools.ietf.org/html/rfc5424). + +- **DEBUG** (100): Detailed debug information. + +- **INFO** (200): Interesting events. Examples: User logs in, SQL logs. + +- **NOTICE** (250): Normal but significant events. + +- **WARNING** (300): Exceptional occurrences that are not errors. Examples: + Use of deprecated APIs, poor use of an API, undesirable things that are not + necessarily wrong. + +- **ERROR** (400): Runtime errors that do not require immediate action but + should typically be logged and monitored. + +- **CRITICAL** (500): Critical conditions. Example: Application component + unavailable, unexpected exception. + +- **ALERT** (550): Action must be taken immediately. Example: Entire website + down, database unavailable, etc. This should trigger the SMS alerts and wake + you up. + +- **EMERGENCY** (600): Emergency: system is unusable. + +## Configuring a logger + +Here is a basic setup to log to a file and to firephp on the DEBUG level: + +```php +pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG)); +$logger->pushHandler(new FirePHPHandler()); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); +``` + +Let's explain it. The first step is to create the logger instance which will +be used in your code. The argument is a channel name, which is useful when +you use several loggers (see below for more details about it). + +The logger itself does not know how to handle a record. It delegates it to +some handlers. The code above registers two handlers in the stack to allow +handling records in two different ways. + +Note that the FirePHPHandler is called first as it is added on top of the +stack. This allows you to temporarily add a logger with bubbling disabled if +you want to override other configured loggers. + +> If you use Monolog standalone and are looking for an easy way to +> configure many handlers, the [theorchard/monolog-cascade](https://github.com/theorchard/monolog-cascade) +> can help you build complex logging configs via PHP arrays, yaml or json configs. + +## Adding extra data in the records + +Monolog provides two different ways to add extra informations along the simple +textual message. + +### Using the logging context + +The first way is the context, allowing to pass an array of data along the +record: + +```php +addInfo('Adding a new user', array('username' => 'Seldaek')); +``` + +Simple handlers (like the StreamHandler for instance) will simply format +the array to a string but richer handlers can take advantage of the context +(FirePHP is able to display arrays in pretty way for instance). + +### Using processors + +The second way is to add extra data for all records by using a processor. +Processors can be any callable. They will get the record as parameter and +must return it after having eventually changed the `extra` part of it. Let's +write a processor adding some dummy data in the record: + +```php +pushProcessor(function ($record) { + $record['extra']['dummy'] = 'Hello world!'; + + return $record; +}); +``` + +Monolog provides some built-in processors that can be used in your project. +Look at the [dedicated chapter](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#processors) for the list. + +> Tip: processors can also be registered on a specific handler instead of + the logger to apply only for this handler. + +## Leveraging channels + +Channels are a great way to identify to which part of the application a record +is related. This is useful in big applications (and is leveraged by +MonologBundle in Symfony2). + +Picture two loggers sharing a handler that writes to a single log file. +Channels would allow you to identify the logger that issued every record. +You can easily grep through the log files filtering this or that channel. + +```php +pushHandler($stream); +$logger->pushHandler($firephp); + +// Create a logger for the security-related stuff with a different channel +$securityLogger = new Logger('security'); +$securityLogger->pushHandler($stream); +$securityLogger->pushHandler($firephp); + +// Or clone the first one to only change the channel +$securityLogger = $logger->withName('security'); +``` + +## Customizing the log format + +In Monolog it's easy to customize the format of the logs written into files, +sockets, mails, databases and other handlers. Most of the handlers use the + +```php +$record['formatted'] +``` + +value to be automatically put into the log device. This value depends on the +formatter settings. You can choose between predefined formatter classes or +write your own (e.g. a multiline text file for human-readable output). + +To configure a predefined formatter class, just set it as the handler's field: + +```php +// the default date format is "Y-m-d H:i:s" +$dateFormat = "Y n j, g:i a"; +// the default output format is "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n" +$output = "%datetime% > %level_name% > %message% %context% %extra%\n"; +// finally, create a formatter +$formatter = new LineFormatter($output, $dateFormat); + +// Create a handler +$stream = new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG); +$stream->setFormatter($formatter); +// bind it to a logger object +$securityLogger = new Logger('security'); +$securityLogger->pushHandler($stream); +``` + +You may also reuse the same formatter between multiple handlers and share those +handlers between multiple loggers. + +[Handlers, Formatters and Processors](02-handlers-formatters-processors.md) → diff --git a/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md b/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md new file mode 100644 index 0000000..bea968a --- /dev/null +++ b/vendor/monolog/monolog/doc/02-handlers-formatters-processors.md @@ -0,0 +1,157 @@ +# Handlers, Formatters and Processors + +- [Handlers](#handlers) + - [Log to files and syslog](#log-to-files-and-syslog) + - [Send alerts and emails](#send-alerts-and-emails) + - [Log specific servers and networked logging](#log-specific-servers-and-networked-logging) + - [Logging in development](#logging-in-development) + - [Log to databases](#log-to-databases) + - [Wrappers / Special Handlers](#wrappers--special-handlers) +- [Formatters](#formatters) +- [Processors](#processors) +- [Third Party Packages](#third-party-packages) + +## Handlers + +### Log to files and syslog + +- _StreamHandler_: Logs records into any PHP stream, use this for log files. +- _RotatingFileHandler_: Logs records to a file and creates one logfile per day. + It will also delete files older than `$maxFiles`. You should use + [logrotate](http://linuxcommand.org/man_pages/logrotate8.html) for high profile + setups though, this is just meant as a quick and dirty solution. +- _SyslogHandler_: Logs records to the syslog. +- _ErrorLogHandler_: Logs records to PHP's + [`error_log()`](http://docs.php.net/manual/en/function.error-log.php) function. + +### Send alerts and emails + +- _NativeMailerHandler_: Sends emails using PHP's + [`mail()`](http://php.net/manual/en/function.mail.php) function. +- _SwiftMailerHandler_: Sends emails using a [`Swift_Mailer`](http://swiftmailer.org/) instance. +- _PushoverHandler_: Sends mobile notifications via the [Pushover](https://www.pushover.net/) API. +- _HipChatHandler_: Logs records to a [HipChat](http://hipchat.com) chat room using its API. +- _FlowdockHandler_: Logs records to a [Flowdock](https://www.flowdock.com/) account. +- _SlackHandler_: Logs records to a [Slack](https://www.slack.com/) account using the Slack API. +- _SlackbotHandler_: Logs records to a [Slack](https://www.slack.com/) account using the Slackbot incoming hook. +- _SlackWebhookHandler_: Logs records to a [Slack](https://www.slack.com/) account using Slack Webhooks. +- _MandrillHandler_: Sends emails via the Mandrill API using a [`Swift_Message`](http://swiftmailer.org/) instance. +- _FleepHookHandler_: Logs records to a [Fleep](https://fleep.io/) conversation using Webhooks. +- _IFTTTHandler_: Notifies an [IFTTT](https://ifttt.com/maker) trigger with the log channel, level name and message. + +### Log specific servers and networked logging + +- _SocketHandler_: Logs records to [sockets](http://php.net/fsockopen), use this + for UNIX and TCP sockets. See an [example](sockets.md). +- _AmqpHandler_: Logs records to an [amqp](http://www.amqp.org/) compatible + server. Requires the [php-amqp](http://pecl.php.net/package/amqp) extension (1.0+). +- _GelfHandler_: Logs records to a [Graylog2](http://www.graylog2.org) server. +- _CubeHandler_: Logs records to a [Cube](http://square.github.com/cube/) server. +- _RavenHandler_: Logs records to a [Sentry](http://getsentry.com/) server using + [raven](https://packagist.org/packages/raven/raven). +- _ZendMonitorHandler_: Logs records to the Zend Monitor present in Zend Server. +- _NewRelicHandler_: Logs records to a [NewRelic](http://newrelic.com/) application. +- _LogglyHandler_: Logs records to a [Loggly](http://www.loggly.com/) account. +- _RollbarHandler_: Logs records to a [Rollbar](https://rollbar.com/) account. +- _SyslogUdpHandler_: Logs records to a remote [Syslogd](http://www.rsyslog.com/) server. +- _LogEntriesHandler_: Logs records to a [LogEntries](http://logentries.com/) account. + +### Logging in development + +- _FirePHPHandler_: Handler for [FirePHP](http://www.firephp.org/), providing + inline `console` messages within [FireBug](http://getfirebug.com/). +- _ChromePHPHandler_: Handler for [ChromePHP](http://www.chromephp.com/), providing + inline `console` messages within Chrome. +- _BrowserConsoleHandler_: Handler to send logs to browser's Javascript `console` with + no browser extension required. Most browsers supporting `console` API are supported. +- _PHPConsoleHandler_: Handler for [PHP Console](https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef), providing + inline `console` and notification popup messages within Chrome. + +### Log to databases + +- _RedisHandler_: Logs records to a [redis](http://redis.io) server. +- _MongoDBHandler_: Handler to write records in MongoDB via a + [Mongo](http://pecl.php.net/package/mongo) extension connection. +- _CouchDBHandler_: Logs records to a CouchDB server. +- _DoctrineCouchDBHandler_: Logs records to a CouchDB server via the Doctrine CouchDB ODM. +- _ElasticSearchHandler_: Logs records to an Elastic Search server. +- _DynamoDbHandler_: Logs records to a DynamoDB table with the [AWS SDK](https://github.com/aws/aws-sdk-php). + +### Wrappers / Special Handlers + +- _FingersCrossedHandler_: A very interesting wrapper. It takes a logger as + parameter and will accumulate log records of all levels until a record + exceeds the defined severity level. At which point it delivers all records, + including those of lower severity, to the handler it wraps. This means that + until an error actually happens you will not see anything in your logs, but + when it happens you will have the full information, including debug and info + records. This provides you with all the information you need, but only when + you need it. +- _DeduplicationHandler_: Useful if you are sending notifications or emails + when critical errors occur. It takes a logger as parameter and will + accumulate log records of all levels until the end of the request (or + `flush()` is called). At that point it delivers all records to the handler + it wraps, but only if the records are unique over a given time period + (60seconds by default). If the records are duplicates they are simply + discarded. The main use of this is in case of critical failure like if your + database is unreachable for example all your requests will fail and that + can result in a lot of notifications being sent. Adding this handler reduces + the amount of notifications to a manageable level. +- _WhatFailureGroupHandler_: This handler extends the _GroupHandler_ ignoring + exceptions raised by each child handler. This allows you to ignore issues + where a remote tcp connection may have died but you do not want your entire + application to crash and may wish to continue to log to other handlers. +- _BufferHandler_: This handler will buffer all the log records it receives + until `close()` is called at which point it will call `handleBatch()` on the + handler it wraps with all the log messages at once. This is very useful to + send an email with all records at once for example instead of having one mail + for every log record. +- _GroupHandler_: This handler groups other handlers. Every record received is + sent to all the handlers it is configured with. +- _FilterHandler_: This handler only lets records of the given levels through + to the wrapped handler. +- _SamplingHandler_: Wraps around another handler and lets you sample records + if you only want to store some of them. +- _NullHandler_: Any record it can handle will be thrown away. This can be used + to put on top of an existing handler stack to disable it temporarily. +- _PsrHandler_: Can be used to forward log records to an existing PSR-3 logger +- _TestHandler_: Used for testing, it records everything that is sent to it and + has accessors to read out the information. +- _HandlerWrapper_: A simple handler wrapper you can inherit from to create + your own wrappers easily. + +## Formatters + +- _LineFormatter_: Formats a log record into a one-line string. +- _HtmlFormatter_: Used to format log records into a human readable html table, mainly suitable for emails. +- _NormalizerFormatter_: Normalizes objects/resources down to strings so a record can easily be serialized/encoded. +- _ScalarFormatter_: Used to format log records into an associative array of scalar values. +- _JsonFormatter_: Encodes a log record into json. +- _WildfireFormatter_: Used to format log records into the Wildfire/FirePHP protocol, only useful for the FirePHPHandler. +- _ChromePHPFormatter_: Used to format log records into the ChromePHP format, only useful for the ChromePHPHandler. +- _GelfMessageFormatter_: Used to format log records into Gelf message instances, only useful for the GelfHandler. +- _LogstashFormatter_: Used to format log records into [logstash](http://logstash.net/) event json, useful for any handler listed under inputs [here](http://logstash.net/docs/latest). +- _ElasticaFormatter_: Used to format log records into an Elastica\Document object, only useful for the ElasticSearchHandler. +- _LogglyFormatter_: Used to format log records into Loggly messages, only useful for the LogglyHandler. +- _FlowdockFormatter_: Used to format log records into Flowdock messages, only useful for the FlowdockHandler. +- _MongoDBFormatter_: Converts \DateTime instances to \MongoDate and objects recursively to arrays, only useful with the MongoDBHandler. + +## Processors + +- _PsrLogMessageProcessor_: Processes a log record's message according to PSR-3 rules, replacing `{foo}` with the value from `$context['foo']`. +- _IntrospectionProcessor_: Adds the line/file/class/method from which the log call originated. +- _WebProcessor_: Adds the current request URI, request method and client IP to a log record. +- _MemoryUsageProcessor_: Adds the current memory usage to a log record. +- _MemoryPeakUsageProcessor_: Adds the peak memory usage to a log record. +- _ProcessIdProcessor_: Adds the process id to a log record. +- _UidProcessor_: Adds a unique identifier to a log record. +- _GitProcessor_: Adds the current git branch and commit to a log record. +- _TagProcessor_: Adds an array of predefined tags to a log record. + +## Third Party Packages + +Third party handlers, formatters and processors are +[listed in the wiki](https://github.com/Seldaek/monolog/wiki/Third-Party-Packages). You +can also add your own there if you publish one. + +← [Usage](01-usage.md) | [Utility classes](03-utilities.md) → diff --git a/vendor/monolog/monolog/doc/03-utilities.md b/vendor/monolog/monolog/doc/03-utilities.md new file mode 100644 index 0000000..c62aa41 --- /dev/null +++ b/vendor/monolog/monolog/doc/03-utilities.md @@ -0,0 +1,13 @@ +# Utilities + +- _Registry_: The `Monolog\Registry` class lets you configure global loggers that you + can then statically access from anywhere. It is not really a best practice but can + help in some older codebases or for ease of use. +- _ErrorHandler_: The `Monolog\ErrorHandler` class allows you to easily register + a Logger instance as an exception handler, error handler or fatal error handler. +- _ErrorLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain log + level is reached. +- _ChannelLevelActivationStrategy_: Activates a FingersCrossedHandler when a certain + log level is reached, depending on which channel received the log record. + +← [Handlers, Formatters and Processors](02-handlers-formatters-processors.md) | [Extending Monolog](04-extending.md) → diff --git a/vendor/monolog/monolog/doc/04-extending.md b/vendor/monolog/monolog/doc/04-extending.md new file mode 100644 index 0000000..ebd9104 --- /dev/null +++ b/vendor/monolog/monolog/doc/04-extending.md @@ -0,0 +1,76 @@ +# Extending Monolog + +Monolog is fully extensible, allowing you to adapt your logger to your needs. + +## Writing your own handler + +Monolog provides many built-in handlers. But if the one you need does not +exist, you can write it and use it in your logger. The only requirement is +to implement `Monolog\Handler\HandlerInterface`. + +Let's write a PDOHandler to log records to a database. We will extend the +abstract class provided by Monolog to keep things DRY. + +```php +pdo = $pdo; + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + if (!$this->initialized) { + $this->initialize(); + } + + $this->statement->execute(array( + 'channel' => $record['channel'], + 'level' => $record['level'], + 'message' => $record['formatted'], + 'time' => $record['datetime']->format('U'), + )); + } + + private function initialize() + { + $this->pdo->exec( + 'CREATE TABLE IF NOT EXISTS monolog ' + .'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)' + ); + $this->statement = $this->pdo->prepare( + 'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)' + ); + + $this->initialized = true; + } +} +``` + +You can now use this handler in your logger: + +```php +pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite'))); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); +``` + +The `Monolog\Handler\AbstractProcessingHandler` class provides most of the +logic needed for the handler, including the use of processors and the formatting +of the record (which is why we use ``$record['formatted']`` instead of ``$record['message']``). + +← [Utility classes](03-utilities.md) diff --git a/vendor/monolog/monolog/doc/sockets.md b/vendor/monolog/monolog/doc/sockets.md new file mode 100644 index 0000000..ea9cf0e --- /dev/null +++ b/vendor/monolog/monolog/doc/sockets.md @@ -0,0 +1,39 @@ +Sockets Handler +=============== + +This handler allows you to write your logs to sockets using [fsockopen](http://php.net/fsockopen) +or [pfsockopen](http://php.net/pfsockopen). + +Persistent sockets are mainly useful in web environments where you gain some performance not closing/opening +the connections between requests. + +You can use a `unix://` prefix to access unix sockets and `udp://` to open UDP sockets instead of the default TCP. + +Basic Example +------------- + +```php +setPersistent(true); + +// Now add the handler +$logger->pushHandler($handler, Logger::DEBUG); + +// You can now use your logger +$logger->addInfo('My logger is now ready'); + +``` + +In this example, using syslog-ng, you should see the log on the log server: + + cweb1 [2012-02-26 00:12:03] my_logger.INFO: My logger is now ready [] [] + diff --git a/vendor/monolog/monolog/phpunit.xml.dist b/vendor/monolog/monolog/phpunit.xml.dist new file mode 100644 index 0000000..20d82b6 --- /dev/null +++ b/vendor/monolog/monolog/phpunit.xml.dist @@ -0,0 +1,19 @@ + + + + + + tests/Monolog/ + + + + + + src/Monolog/ + + + + + + + diff --git a/vendor/monolog/monolog/src/Monolog/ErrorHandler.php b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php new file mode 100644 index 0000000..7bfcd83 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/ErrorHandler.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Monolog\Handler\AbstractHandler; + +/** + * Monolog error handler + * + * A facility to enable logging of runtime errors, exceptions and fatal errors. + * + * Quick setup: ErrorHandler::register($logger); + * + * @author Jordi Boggiano + */ +class ErrorHandler +{ + private $logger; + + private $previousExceptionHandler; + private $uncaughtExceptionLevel; + + private $previousErrorHandler; + private $errorLevelMap; + private $handleOnlyReportedErrors; + + private $hasFatalErrorHandler; + private $fatalLevel; + private $reservedMemory; + private static $fatalErrors = array(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR); + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Registers a new ErrorHandler for a given Logger + * + * By default it will handle errors, exceptions and fatal errors + * + * @param LoggerInterface $logger + * @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling + * @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling + * @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling + * @return ErrorHandler + */ + public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null) + { + //Forces the autoloader to run for LogLevel. Fixes an autoload issue at compile-time on PHP5.3. See https://github.com/Seldaek/monolog/pull/929 + class_exists('\\Psr\\Log\\LogLevel', true); + + $handler = new static($logger); + if ($errorLevelMap !== false) { + $handler->registerErrorHandler($errorLevelMap); + } + if ($exceptionLevel !== false) { + $handler->registerExceptionHandler($exceptionLevel); + } + if ($fatalLevel !== false) { + $handler->registerFatalHandler($fatalLevel); + } + + return $handler; + } + + public function registerExceptionHandler($level = null, $callPrevious = true) + { + $prev = set_exception_handler(array($this, 'handleException')); + $this->uncaughtExceptionLevel = $level; + if ($callPrevious && $prev) { + $this->previousExceptionHandler = $prev; + } + } + + public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1, $handleOnlyReportedErrors = true) + { + $prev = set_error_handler(array($this, 'handleError'), $errorTypes); + $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap); + if ($callPrevious) { + $this->previousErrorHandler = $prev ?: true; + } + + $this->handleOnlyReportedErrors = $handleOnlyReportedErrors; + } + + public function registerFatalHandler($level = null, $reservedMemorySize = 20) + { + register_shutdown_function(array($this, 'handleFatalError')); + + $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); + $this->fatalLevel = $level; + $this->hasFatalErrorHandler = true; + } + + protected function defaultErrorLevelMap() + { + return array( + E_ERROR => LogLevel::CRITICAL, + E_WARNING => LogLevel::WARNING, + E_PARSE => LogLevel::ALERT, + E_NOTICE => LogLevel::NOTICE, + E_CORE_ERROR => LogLevel::CRITICAL, + E_CORE_WARNING => LogLevel::WARNING, + E_COMPILE_ERROR => LogLevel::ALERT, + E_COMPILE_WARNING => LogLevel::WARNING, + E_USER_ERROR => LogLevel::ERROR, + E_USER_WARNING => LogLevel::WARNING, + E_USER_NOTICE => LogLevel::NOTICE, + E_STRICT => LogLevel::NOTICE, + E_RECOVERABLE_ERROR => LogLevel::ERROR, + E_DEPRECATED => LogLevel::NOTICE, + E_USER_DEPRECATED => LogLevel::NOTICE, + ); + } + + /** + * @private + */ + public function handleException($e) + { + $this->logger->log( + $this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, + sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), + array('exception' => $e) + ); + + if ($this->previousExceptionHandler) { + call_user_func($this->previousExceptionHandler, $e); + } + + exit(255); + } + + /** + * @private + */ + public function handleError($code, $message, $file = '', $line = 0, $context = array()) + { + if ($this->handleOnlyReportedErrors && !(error_reporting() & $code)) { + return; + } + + // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries + if (!$this->hasFatalErrorHandler || !in_array($code, self::$fatalErrors, true)) { + $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; + $this->logger->log($level, self::codeToString($code).': '.$message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); + } + + if ($this->previousErrorHandler === true) { + return false; + } elseif ($this->previousErrorHandler) { + return call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context); + } + } + + /** + * @private + */ + public function handleFatalError() + { + $this->reservedMemory = null; + + $lastError = error_get_last(); + if ($lastError && in_array($lastError['type'], self::$fatalErrors, true)) { + $this->logger->log( + $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, + 'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'], + array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line']) + ); + + if ($this->logger instanceof Logger) { + foreach ($this->logger->getHandlers() as $handler) { + if ($handler instanceof AbstractHandler) { + $handler->close(); + } + } + } + } + } + + private static function codeToString($code) + { + switch ($code) { + case E_ERROR: + return 'E_ERROR'; + case E_WARNING: + return 'E_WARNING'; + case E_PARSE: + return 'E_PARSE'; + case E_NOTICE: + return 'E_NOTICE'; + case E_CORE_ERROR: + return 'E_CORE_ERROR'; + case E_CORE_WARNING: + return 'E_CORE_WARNING'; + case E_COMPILE_ERROR: + return 'E_COMPILE_ERROR'; + case E_COMPILE_WARNING: + return 'E_COMPILE_WARNING'; + case E_USER_ERROR: + return 'E_USER_ERROR'; + case E_USER_WARNING: + return 'E_USER_WARNING'; + case E_USER_NOTICE: + return 'E_USER_NOTICE'; + case E_STRICT: + return 'E_STRICT'; + case E_RECOVERABLE_ERROR: + return 'E_RECOVERABLE_ERROR'; + case E_DEPRECATED: + return 'E_DEPRECATED'; + case E_USER_DEPRECATED: + return 'E_USER_DEPRECATED'; + } + + return 'Unknown PHP error'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php new file mode 100644 index 0000000..9beda1e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats a log message according to the ChromePHP array format + * + * @author Christophe Coevoet + */ +class ChromePHPFormatter implements FormatterInterface +{ + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'log', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warn', + Logger::ERROR => 'error', + Logger::CRITICAL => 'error', + Logger::ALERT => 'error', + Logger::EMERGENCY => 'error', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $backtrace = 'unknown'; + if (isset($record['extra']['file'], $record['extra']['line'])) { + $backtrace = $record['extra']['file'].' : '.$record['extra']['line']; + unset($record['extra']['file'], $record['extra']['line']); + } + + $message = array('message' => $record['message']); + if ($record['context']) { + $message['context'] = $record['context']; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + } + if (count($message) === 1) { + $message = reset($message); + } + + return array( + $record['channel'], + $message, + $backtrace, + $this->logLevels[$record['level']], + ); + } + + public function formatBatch(array $records) + { + $formatted = array(); + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php new file mode 100644 index 0000000..4c556cf --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.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 Monolog\Formatter; + +use Elastica\Document; + +/** + * Format a log message into an Elastica Document + * + * @author Jelle Vink + */ +class ElasticaFormatter extends NormalizerFormatter +{ + /** + * @var string Elastic search index name + */ + protected $index; + + /** + * @var string Elastic search document type + */ + protected $type; + + /** + * @param string $index Elastic Search index name + * @param string $type Elastic Search document type + */ + public function __construct($index, $type) + { + // elasticsearch requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->index = $index; + $this->type = $type; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + return $this->getDocument($record); + } + + /** + * Getter index + * @return string + */ + public function getIndex() + { + return $this->index; + } + + /** + * Getter type + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Convert a log message into an Elastica Document + * + * @param array $record Log message + * @return Document + */ + protected function getDocument($record) + { + $document = new Document(); + $document->setData($record); + $document->setType($this->type); + $document->setIndex($this->index); + + return $document; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php new file mode 100644 index 0000000..5094af3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * formats the record to be used in the FlowdockHandler + * + * @author Dominik Liebler + */ +class FlowdockFormatter implements FormatterInterface +{ + /** + * @var string + */ + private $source; + + /** + * @var string + */ + private $sourceEmail; + + /** + * @param string $source + * @param string $sourceEmail + */ + public function __construct($source, $sourceEmail) + { + $this->source = $source; + $this->sourceEmail = $sourceEmail; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $tags = array( + '#logs', + '#' . strtolower($record['level_name']), + '#' . $record['channel'], + ); + + foreach ($record['extra'] as $value) { + $tags[] = '#' . $value; + } + + $subject = sprintf( + 'in %s: %s - %s', + $this->source, + $record['level_name'], + $this->getShortMessage($record['message']) + ); + + $record['flowdock'] = array( + 'source' => $this->source, + 'from_address' => $this->sourceEmail, + 'subject' => $subject, + 'content' => $record['message'], + 'tags' => $tags, + 'project' => $this->source, + ); + + return $record; + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + $formatted = array(); + + foreach ($records as $record) { + $formatted[] = $this->format($record); + } + + return $formatted; + } + + /** + * @param string $message + * + * @return string + */ + public function getShortMessage($message) + { + static $hasMbString; + + if (null === $hasMbString) { + $hasMbString = function_exists('mb_strlen'); + } + + $maxLength = 45; + + if ($hasMbString) { + if (mb_strlen($message, 'UTF-8') > $maxLength) { + $message = mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...'; + } + } else { + if (strlen($message) > $maxLength) { + $message = substr($message, 0, $maxLength - 4) . ' ...'; + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php new file mode 100644 index 0000000..02632bb --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FluentdFormatter.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 Monolog\Formatter; + +/** + * Class FluentdFormatter + * + * Serializes a log message to Fluentd unix socket protocol + * + * Fluentd config: + * + * + * type unix + * path /var/run/td-agent/td-agent.sock + * + * + * Monolog setup: + * + * $logger = new Monolog\Logger('fluent.tag'); + * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock'); + * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter()); + * $logger->pushHandler($fluentHandler); + * + * @author Andrius Putna + */ +class FluentdFormatter implements FormatterInterface +{ + /** + * @var bool $levelTag should message level be a part of the fluentd tag + */ + protected $levelTag = false; + + public function __construct($levelTag = false) + { + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter'); + } + + $this->levelTag = (bool) $levelTag; + } + + public function isUsingLevelsInTag() + { + return $this->levelTag; + } + + public function format(array $record) + { + $tag = $record['channel']; + if ($this->levelTag) { + $tag .= '.' . strtolower($record['level_name']); + } + + $message = array( + 'message' => $record['message'], + 'extra' => $record['extra'], + ); + + if (!$this->levelTag) { + $message['level'] = $record['level']; + $message['level_name'] = $record['level_name']; + } + + return json_encode(array($tag, $record['datetime']->getTimestamp(), $message)); + } + + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.php new file mode 100644 index 0000000..b5de751 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/FormatterInterface.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 Monolog\Formatter; + +/** + * Interface for formatters + * + * @author Jordi Boggiano + */ +interface FormatterInterface +{ + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + */ + public function format(array $record); + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + */ + public function formatBatch(array $records); +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php new file mode 100644 index 0000000..2c1b0e8 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php @@ -0,0 +1,138 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Gelf\Message; + +/** + * Serializes a log message to GELF + * @see http://www.graylog2.org/about/gelf + * + * @author Matt Lehner + */ +class GelfMessageFormatter extends NormalizerFormatter +{ + const DEFAULT_MAX_LENGTH = 32766; + + /** + * @var string the name of the system for the Gelf log message + */ + protected $systemName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * @var int max length per field + */ + protected $maxLength; + + /** + * Translates Monolog log levels to Graylog2 log priorities. + */ + private $logLevels = array( + Logger::DEBUG => 7, + Logger::INFO => 6, + Logger::NOTICE => 5, + Logger::WARNING => 4, + Logger::ERROR => 3, + Logger::CRITICAL => 2, + Logger::ALERT => 1, + Logger::EMERGENCY => 0, + ); + + public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $maxLength = null) + { + parent::__construct('U.u'); + + $this->systemName = $systemName ?: gethostname(); + + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + $this->maxLength = is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + if (!isset($record['datetime'], $record['message'], $record['level'])) { + throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, '.var_export($record, true).' given'); + } + + $message = new Message(); + $message + ->setTimestamp($record['datetime']) + ->setShortMessage((string) $record['message']) + ->setHost($this->systemName) + ->setLevel($this->logLevels[$record['level']]); + + // message length + system name length + 200 for padding / metadata + $len = 200 + strlen((string) $record['message']) + strlen($this->systemName); + + if ($len > $this->maxLength) { + $message->setShortMessage(substr($record['message'], 0, $this->maxLength)); + } + + if (isset($record['channel'])) { + $message->setFacility($record['channel']); + } + if (isset($record['extra']['line'])) { + $message->setLine($record['extra']['line']); + unset($record['extra']['line']); + } + if (isset($record['extra']['file'])) { + $message->setFile($record['extra']['file']); + unset($record['extra']['file']); + } + + foreach ($record['extra'] as $key => $val) { + $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); + $len = strlen($this->extraPrefix . $key . $val); + if ($len > $this->maxLength) { + $message->setAdditional($this->extraPrefix . $key, substr($val, 0, $this->maxLength)); + break; + } + $message->setAdditional($this->extraPrefix . $key, $val); + } + + foreach ($record['context'] as $key => $val) { + $val = is_scalar($val) || null === $val ? $val : $this->toJson($val); + $len = strlen($this->contextPrefix . $key . $val); + if ($len > $this->maxLength) { + $message->setAdditional($this->contextPrefix . $key, substr($val, 0, $this->maxLength)); + break; + } + $message->setAdditional($this->contextPrefix . $key, $val); + } + + if (null === $message->getFile() && isset($record['context']['exception']['file'])) { + if (preg_match("/^(.+):([0-9]+)$/", $record['context']['exception']['file'], $matches)) { + $message->setFile($matches[1]); + $message->setLine($matches[2]); + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php new file mode 100644 index 0000000..3eec95f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/HtmlFormatter.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 Monolog\Formatter; + +use Monolog\Logger; + +/** + * Formats incoming records into an HTML table + * + * This is especially useful for html email logging + * + * @author Tiago Brito + */ +class HtmlFormatter extends NormalizerFormatter +{ + /** + * Translates Monolog log levels to html color priorities. + */ + protected $logLevels = array( + Logger::DEBUG => '#cccccc', + Logger::INFO => '#468847', + Logger::NOTICE => '#3a87ad', + Logger::WARNING => '#c09853', + Logger::ERROR => '#f0ad4e', + Logger::CRITICAL => '#FF7708', + Logger::ALERT => '#C12A19', + Logger::EMERGENCY => '#000000', + ); + + /** + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($dateFormat = null) + { + parent::__construct($dateFormat); + } + + /** + * Creates an HTML table row + * + * @param string $th Row header content + * @param string $td Row standard cell content + * @param bool $escapeTd false if td content must not be html escaped + * @return string + */ + protected function addRow($th, $td = ' ', $escapeTd = true) + { + $th = htmlspecialchars($th, ENT_NOQUOTES, 'UTF-8'); + if ($escapeTd) { + $td = '
    '.htmlspecialchars($td, ENT_NOQUOTES, 'UTF-8').'
    '; + } + + return "\n$th:\n".$td."\n"; + } + + /** + * Create a HTML h1 tag + * + * @param string $title Text to be in the h1 + * @param int $level Error level + * @return string + */ + protected function addTitle($title, $level) + { + $title = htmlspecialchars($title, ENT_NOQUOTES, 'UTF-8'); + + return '

    '.$title.'

    '; + } + + /** + * Formats a log record. + * + * @param array $record A record to format + * @return mixed The formatted record + */ + public function format(array $record) + { + $output = $this->addTitle($record['level_name'], $record['level']); + $output .= ''; + + $output .= $this->addRow('Message', (string) $record['message']); + $output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat)); + $output .= $this->addRow('Channel', $record['channel']); + if ($record['context']) { + $embeddedTable = '
    '; + foreach ($record['context'] as $key => $value) { + $embeddedTable .= $this->addRow($key, $this->convertToString($value)); + } + $embeddedTable .= '
    '; + $output .= $this->addRow('Context', $embeddedTable, false); + } + if ($record['extra']) { + $embeddedTable = ''; + foreach ($record['extra'] as $key => $value) { + $embeddedTable .= $this->addRow($key, $this->convertToString($value)); + } + $embeddedTable .= '
    '; + $output .= $this->addRow('Extra', $embeddedTable, false); + } + + return $output.''; + } + + /** + * Formats a set of log records. + * + * @param array $records A set of records to format + * @return mixed The formatted set of records + */ + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + protected function convertToString($data) + { + if (null === $data || is_scalar($data)) { + return (string) $data; + } + + $data = $this->normalize($data); + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return str_replace('\\/', '/', json_encode($data)); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php new file mode 100644 index 0000000..0782f14 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/JsonFormatter.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Exception; +use Throwable; + +/** + * Encodes whatever record data is passed to it as json + * + * This can be useful to log to databases or remote APIs + * + * @author Jordi Boggiano + */ +class JsonFormatter extends NormalizerFormatter +{ + const BATCH_MODE_JSON = 1; + const BATCH_MODE_NEWLINES = 2; + + protected $batchMode; + protected $appendNewline; + + /** + * @var bool + */ + protected $includeStacktraces = false; + + /** + * @param int $batchMode + * @param bool $appendNewline + */ + public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = true) + { + $this->batchMode = $batchMode; + $this->appendNewline = $appendNewline; + } + + /** + * The batch mode option configures the formatting style for + * multiple records. By default, multiple records will be + * formatted as a JSON-encoded array. However, for + * compatibility with some API endpoints, alternative styles + * are available. + * + * @return int + */ + public function getBatchMode() + { + return $this->batchMode; + } + + /** + * True if newlines are appended to every formatted record + * + * @return bool + */ + public function isAppendingNewlines() + { + return $this->appendNewline; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return $this->toJson($this->normalize($record), true) . ($this->appendNewline ? "\n" : ''); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + switch ($this->batchMode) { + case static::BATCH_MODE_NEWLINES: + return $this->formatBatchNewlines($records); + + case static::BATCH_MODE_JSON: + default: + return $this->formatBatchJson($records); + } + } + + /** + * @param bool $include + */ + public function includeStacktraces($include = true) + { + $this->includeStacktraces = $include; + } + + /** + * Return a JSON-encoded array of records. + * + * @param array $records + * @return string + */ + protected function formatBatchJson(array $records) + { + return $this->toJson($this->normalize($records), true); + } + + /** + * Use new lines to separate records instead of a + * JSON-encoded array. + * + * @param array $records + * @return string + */ + protected function formatBatchNewlines(array $records) + { + $instance = $this; + + $oldNewline = $this->appendNewline; + $this->appendNewline = false; + array_walk($records, function (&$value, $key) use ($instance) { + $value = $instance->format($value); + }); + $this->appendNewline = $oldNewline; + + return implode("\n", $records); + } + + /** + * Normalizes given $data. + * + * @param mixed $data + * + * @return mixed + */ + protected function normalize($data) + { + if (is_array($data) || $data instanceof \Traversable) { + $normalized = array(); + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ >= 1000) { + $normalized['...'] = 'Over 1000 items, aborting normalization'; + break; + } + $normalized[$key] = $this->normalize($value); + } + + return $normalized; + } + + if ($data instanceof Exception || $data instanceof Throwable) { + return $this->normalizeException($data); + } + + return $data; + } + + /** + * Normalizes given exception with or without its own stack trace based on + * `includeStacktraces` property. + * + * @param Exception|Throwable $e + * + * @return array + */ + protected function normalizeException($e) + { + // TODO 2.0 only check for Throwable + if (!$e instanceof Exception && !$e instanceof Throwable) { + throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); + } + + $data = array( + 'class' => get_class($e), + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile().':'.$e->getLine(), + ); + + if ($this->includeStacktraces) { + $trace = $e->getTrace(); + foreach ($trace as $frame) { + if (isset($frame['file'])) { + $data['trace'][] = $frame['file'].':'.$frame['line']; + } elseif (isset($frame['function']) && $frame['function'] === '{closure}') { + // We should again normalize the frames, because it might contain invalid items + $data['trace'][] = $frame['function']; + } else { + // We should again normalize the frames, because it might contain invalid items + $data['trace'][] = $this->normalize($frame); + } + } + } + + if ($previous = $e->getPrevious()) { + $data['previous'] = $this->normalizeException($previous); + } + + return $data; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php new file mode 100644 index 0000000..d3e209e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.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 Monolog\Formatter; + +/** + * Formats incoming records into a one-line string + * + * This is especially useful for logging to files + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +class LineFormatter extends NormalizerFormatter +{ + const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; + + protected $format; + protected $allowInlineLineBreaks; + protected $ignoreEmptyContextAndExtra; + protected $includeStacktraces; + + /** + * @param string $format The format of the message + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries + * @param bool $ignoreEmptyContextAndExtra + */ + public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = false, $ignoreEmptyContextAndExtra = false) + { + $this->format = $format ?: static::SIMPLE_FORMAT; + $this->allowInlineLineBreaks = $allowInlineLineBreaks; + $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; + parent::__construct($dateFormat); + } + + public function includeStacktraces($include = true) + { + $this->includeStacktraces = $include; + if ($this->includeStacktraces) { + $this->allowInlineLineBreaks = true; + } + } + + public function allowInlineLineBreaks($allow = true) + { + $this->allowInlineLineBreaks = $allow; + } + + public function ignoreEmptyContextAndExtra($ignore = true) + { + $this->ignoreEmptyContextAndExtra = $ignore; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $vars = parent::format($record); + + $output = $this->format; + + foreach ($vars['extra'] as $var => $val) { + if (false !== strpos($output, '%extra.'.$var.'%')) { + $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output); + unset($vars['extra'][$var]); + } + } + + + foreach ($vars['context'] as $var => $val) { + if (false !== strpos($output, '%context.'.$var.'%')) { + $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output); + unset($vars['context'][$var]); + } + } + + if ($this->ignoreEmptyContextAndExtra) { + if (empty($vars['context'])) { + unset($vars['context']); + $output = str_replace('%context%', '', $output); + } + + if (empty($vars['extra'])) { + unset($vars['extra']); + $output = str_replace('%extra%', '', $output); + } + } + + foreach ($vars as $var => $val) { + if (false !== strpos($output, '%'.$var.'%')) { + $output = str_replace('%'.$var.'%', $this->stringify($val), $output); + } + } + + // remove leftover %extra.xxx% and %context.xxx% if any + if (false !== strpos($output, '%')) { + $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output); + } + + return $output; + } + + public function formatBatch(array $records) + { + $message = ''; + foreach ($records as $record) { + $message .= $this->format($record); + } + + return $message; + } + + public function stringify($value) + { + return $this->replaceNewlines($this->convertToString($value)); + } + + protected function normalizeException($e) + { + // TODO 2.0 only check for Throwable + if (!$e instanceof \Exception && !$e instanceof \Throwable) { + throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); + } + + $previousText = ''; + if ($previous = $e->getPrevious()) { + do { + $previousText .= ', '.get_class($previous).'(code: '.$previous->getCode().'): '.$previous->getMessage().' at '.$previous->getFile().':'.$previous->getLine(); + } while ($previous = $previous->getPrevious()); + } + + $str = '[object] ('.get_class($e).'(code: '.$e->getCode().'): '.$e->getMessage().' at '.$e->getFile().':'.$e->getLine().$previousText.')'; + if ($this->includeStacktraces) { + $str .= "\n[stacktrace]\n".$e->getTraceAsString()."\n"; + } + + return $str; + } + + protected function convertToString($data) + { + if (null === $data || is_bool($data)) { + return var_export($data, true); + } + + if (is_scalar($data)) { + return (string) $data; + } + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return $this->toJson($data, true); + } + + return str_replace('\\/', '/', @json_encode($data)); + } + + protected function replaceNewlines($str) + { + if ($this->allowInlineLineBreaks) { + if (0 === strpos($str, '{')) { + return str_replace(array('\r', '\n'), array("\r", "\n"), $str); + } + + return $str; + } + + return str_replace(array("\r\n", "\r", "\n"), ' ', $str); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php new file mode 100644 index 0000000..401859b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogglyFormatter.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 Monolog\Formatter; + +/** + * Encodes message information into JSON in a format compatible with Loggly. + * + * @author Adam Pancutt + */ +class LogglyFormatter extends JsonFormatter +{ + /** + * Overrides the default batch mode to new lines for compatibility with the + * Loggly bulk API. + * + * @param int $batchMode + */ + public function __construct($batchMode = self::BATCH_MODE_NEWLINES, $appendNewline = false) + { + parent::__construct($batchMode, $appendNewline); + } + + /** + * Appends the 'timestamp' parameter for indexing by Loggly. + * + * @see https://www.loggly.com/docs/automated-parsing/#json + * @see \Monolog\Formatter\JsonFormatter::format() + */ + public function format(array $record) + { + if (isset($record["datetime"]) && ($record["datetime"] instanceof \DateTime)) { + $record["timestamp"] = $record["datetime"]->format("Y-m-d\TH:i:s.uO"); + // TODO 2.0 unset the 'datetime' parameter, retained for BC + } + + return parent::format($record); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php new file mode 100644 index 0000000..8f83bec --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * Serializes a log message to Logstash Event Format + * + * @see http://logstash.net/ + * @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb + * + * @author Tim Mower + */ +class LogstashFormatter extends NormalizerFormatter +{ + const V0 = 0; + const V1 = 1; + + /** + * @var string the name of the system for the Logstash log message, used to fill the @source field + */ + protected $systemName; + + /** + * @var string an application name for the Logstash log message, used to fill the @type field + */ + protected $applicationName; + + /** + * @var string a prefix for 'extra' fields from the Monolog record (optional) + */ + protected $extraPrefix; + + /** + * @var string a prefix for 'context' fields from the Monolog record (optional) + */ + protected $contextPrefix; + + /** + * @var int logstash format version to use + */ + protected $version; + + /** + * @param string $applicationName the application that sends the data, used as the "type" field of logstash + * @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine + * @param string $extraPrefix prefix for extra keys inside logstash "fields" + * @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_ + * @param int $version the logstash format version to use, defaults to 0 + */ + public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $version = self::V0) + { + // logstash requires a ISO 8601 format date with optional millisecond precision. + parent::__construct('Y-m-d\TH:i:s.uP'); + + $this->systemName = $systemName ?: gethostname(); + $this->applicationName = $applicationName; + $this->extraPrefix = $extraPrefix; + $this->contextPrefix = $contextPrefix; + $this->version = $version; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = parent::format($record); + + if ($this->version === self::V1) { + $message = $this->formatV1($record); + } else { + $message = $this->formatV0($record); + } + + return $this->toJson($message) . "\n"; + } + + protected function formatV0(array $record) + { + if (empty($record['datetime'])) { + $record['datetime'] = gmdate('c'); + } + $message = array( + '@timestamp' => $record['datetime'], + '@source' => $this->systemName, + '@fields' => array(), + ); + if (isset($record['message'])) { + $message['@message'] = $record['message']; + } + if (isset($record['channel'])) { + $message['@tags'] = array($record['channel']); + $message['@fields']['channel'] = $record['channel']; + } + if (isset($record['level'])) { + $message['@fields']['level'] = $record['level']; + } + if ($this->applicationName) { + $message['@type'] = $this->applicationName; + } + if (isset($record['extra']['server'])) { + $message['@source_host'] = $record['extra']['server']; + } + if (isset($record['extra']['url'])) { + $message['@source_path'] = $record['extra']['url']; + } + if (!empty($record['extra'])) { + foreach ($record['extra'] as $key => $val) { + $message['@fields'][$this->extraPrefix . $key] = $val; + } + } + if (!empty($record['context'])) { + foreach ($record['context'] as $key => $val) { + $message['@fields'][$this->contextPrefix . $key] = $val; + } + } + + return $message; + } + + protected function formatV1(array $record) + { + if (empty($record['datetime'])) { + $record['datetime'] = gmdate('c'); + } + $message = array( + '@timestamp' => $record['datetime'], + '@version' => 1, + 'host' => $this->systemName, + ); + if (isset($record['message'])) { + $message['message'] = $record['message']; + } + if (isset($record['channel'])) { + $message['type'] = $record['channel']; + $message['channel'] = $record['channel']; + } + if (isset($record['level_name'])) { + $message['level'] = $record['level_name']; + } + if ($this->applicationName) { + $message['type'] = $this->applicationName; + } + if (!empty($record['extra'])) { + foreach ($record['extra'] as $key => $val) { + $message[$this->extraPrefix . $key] = $val; + } + } + if (!empty($record['context'])) { + foreach ($record['context'] as $key => $val) { + $message[$this->contextPrefix . $key] = $val; + } + } + + return $message; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php new file mode 100644 index 0000000..eb067bb --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.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 Monolog\Formatter; + +/** + * Formats a record for use with the MongoDBHandler. + * + * @author Florian Plattner + */ +class MongoDBFormatter implements FormatterInterface +{ + private $exceptionTraceAsString; + private $maxNestingLevel; + + /** + * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2 + * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings + */ + public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true) + { + $this->maxNestingLevel = max($maxNestingLevel, 0); + $this->exceptionTraceAsString = (bool) $exceptionTraceAsString; + } + + /** + * {@inheritDoc} + */ + public function format(array $record) + { + return $this->formatArray($record); + } + + /** + * {@inheritDoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + protected function formatArray(array $record, $nestingLevel = 0) + { + if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) { + foreach ($record as $name => $value) { + if ($value instanceof \DateTime) { + $record[$name] = $this->formatDate($value, $nestingLevel + 1); + } elseif ($value instanceof \Exception) { + $record[$name] = $this->formatException($value, $nestingLevel + 1); + } elseif (is_array($value)) { + $record[$name] = $this->formatArray($value, $nestingLevel + 1); + } elseif (is_object($value)) { + $record[$name] = $this->formatObject($value, $nestingLevel + 1); + } + } + } else { + $record = '[...]'; + } + + return $record; + } + + protected function formatObject($value, $nestingLevel) + { + $objectVars = get_object_vars($value); + $objectVars['class'] = get_class($value); + + return $this->formatArray($objectVars, $nestingLevel); + } + + protected function formatException(\Exception $exception, $nestingLevel) + { + $formattedException = array( + 'class' => get_class($exception), + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + ); + + if ($this->exceptionTraceAsString === true) { + $formattedException['trace'] = $exception->getTraceAsString(); + } else { + $formattedException['trace'] = $exception->getTrace(); + } + + return $this->formatArray($formattedException, $nestingLevel); + } + + protected function formatDate(\DateTime $value, $nestingLevel) + { + return new \MongoDate($value->getTimestamp()); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php new file mode 100644 index 0000000..d441488 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php @@ -0,0 +1,297 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Exception; + +/** + * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets + * + * @author Jordi Boggiano + */ +class NormalizerFormatter implements FormatterInterface +{ + const SIMPLE_DATE = "Y-m-d H:i:s"; + + protected $dateFormat; + + /** + * @param string $dateFormat The format of the timestamp: one supported by DateTime::format + */ + public function __construct($dateFormat = null) + { + $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); + } + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + return $this->normalize($record); + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + protected function normalize($data) + { + if (null === $data || is_scalar($data)) { + if (is_float($data)) { + if (is_infinite($data)) { + return ($data > 0 ? '' : '-') . 'INF'; + } + if (is_nan($data)) { + return 'NaN'; + } + } + + return $data; + } + + if (is_array($data)) { + $normalized = array(); + + $count = 1; + foreach ($data as $key => $value) { + if ($count++ >= 1000) { + $normalized['...'] = 'Over 1000 items ('.count($data).' total), aborting normalization'; + break; + } + $normalized[$key] = $this->normalize($value); + } + + return $normalized; + } + + if ($data instanceof \DateTime) { + return $data->format($this->dateFormat); + } + + if (is_object($data)) { + // TODO 2.0 only check for Throwable + if ($data instanceof Exception || (PHP_VERSION_ID > 70000 && $data instanceof \Throwable)) { + return $this->normalizeException($data); + } + + // non-serializable objects that implement __toString stringified + if (method_exists($data, '__toString') && !$data instanceof \JsonSerializable) { + $value = $data->__toString(); + } else { + // the rest is json-serialized in some way + $value = $this->toJson($data, true); + } + + return sprintf("[object] (%s: %s)", get_class($data), $value); + } + + if (is_resource($data)) { + return sprintf('[resource] (%s)', get_resource_type($data)); + } + + return '[unknown('.gettype($data).')]'; + } + + protected function normalizeException($e) + { + // TODO 2.0 only check for Throwable + if (!$e instanceof Exception && !$e instanceof \Throwable) { + throw new \InvalidArgumentException('Exception/Throwable expected, got '.gettype($e).' / '.get_class($e)); + } + + $data = array( + 'class' => get_class($e), + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile().':'.$e->getLine(), + ); + + if ($e instanceof \SoapFault) { + if (isset($e->faultcode)) { + $data['faultcode'] = $e->faultcode; + } + + if (isset($e->faultactor)) { + $data['faultactor'] = $e->faultactor; + } + + if (isset($e->detail)) { + $data['detail'] = $e->detail; + } + } + + $trace = $e->getTrace(); + foreach ($trace as $frame) { + if (isset($frame['file'])) { + $data['trace'][] = $frame['file'].':'.$frame['line']; + } elseif (isset($frame['function']) && $frame['function'] === '{closure}') { + // We should again normalize the frames, because it might contain invalid items + $data['trace'][] = $frame['function']; + } else { + // We should again normalize the frames, because it might contain invalid items + $data['trace'][] = $this->toJson($this->normalize($frame), true); + } + } + + if ($previous = $e->getPrevious()) { + $data['previous'] = $this->normalizeException($previous); + } + + return $data; + } + + /** + * Return the JSON representation of a value + * + * @param mixed $data + * @param bool $ignoreErrors + * @throws \RuntimeException if encoding fails and errors are not ignored + * @return string + */ + protected function toJson($data, $ignoreErrors = false) + { + // suppress json_encode errors since it's twitchy with some inputs + if ($ignoreErrors) { + return @$this->jsonEncode($data); + } + + $json = $this->jsonEncode($data); + + if ($json === false) { + $json = $this->handleJsonError(json_last_error(), $data); + } + + return $json; + } + + /** + * @param mixed $data + * @return string JSON encoded data or null on failure + */ + private function jsonEncode($data) + { + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return json_encode($data); + } + + /** + * Handle a json_encode failure. + * + * If the failure is due to invalid string encoding, try to clean the + * input and encode again. If the second encoding attempt fails, the + * inital error is not encoding related or the input can't be cleaned then + * raise a descriptive exception. + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException if failure can't be corrected + * @return string JSON encoded data after error correction + */ + private function handleJsonError($code, $data) + { + if ($code !== JSON_ERROR_UTF8) { + $this->throwEncodeError($code, $data); + } + + if (is_string($data)) { + $this->detectAndCleanUtf8($data); + } elseif (is_array($data)) { + array_walk_recursive($data, array($this, 'detectAndCleanUtf8')); + } else { + $this->throwEncodeError($code, $data); + } + + $json = $this->jsonEncode($data); + + if ($json === false) { + $this->throwEncodeError(json_last_error(), $data); + } + + return $json; + } + + /** + * Throws an exception according to a given code with a customized message + * + * @param int $code return code of json_last_error function + * @param mixed $data data that was meant to be encoded + * @throws \RuntimeException + */ + private function throwEncodeError($code, $data) + { + switch ($code) { + case JSON_ERROR_DEPTH: + $msg = 'Maximum stack depth exceeded'; + break; + case JSON_ERROR_STATE_MISMATCH: + $msg = 'Underflow or the modes mismatch'; + break; + case JSON_ERROR_CTRL_CHAR: + $msg = 'Unexpected control character found'; + break; + case JSON_ERROR_UTF8: + $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; + break; + default: + $msg = 'Unknown error'; + } + + throw new \RuntimeException('JSON encoding failed: '.$msg.'. Encoding: '.var_export($data, true)); + } + + /** + * Detect invalid UTF-8 string characters and convert to valid UTF-8. + * + * Valid UTF-8 input will be left unmodified, but strings containing + * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed + * original encoding of ISO-8859-15. This conversion may result in + * incorrect output if the actual encoding was not ISO-8859-15, but it + * will be clean UTF-8 output and will not rely on expensive and fragile + * detection algorithms. + * + * Function converts the input in place in the passed variable so that it + * can be used as a callback for array_walk_recursive. + * + * @param mixed &$data Input to check and convert if needed + * @private + */ + public function detectAndCleanUtf8(&$data) + { + if (is_string($data) && !preg_match('//u', $data)) { + $data = preg_replace_callback( + '/[\x80-\xFF]+/', + function ($m) { return utf8_encode($m[0]); }, + $data + ); + $data = str_replace( + array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'), + array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'), + $data + ); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php new file mode 100644 index 0000000..5d345d5 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/ScalarFormatter.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 Monolog\Formatter; + +/** + * Formats data into an associative array of scalar values. + * Objects and arrays will be JSON encoded. + * + * @author Andrew Lawson + */ +class ScalarFormatter extends NormalizerFormatter +{ + /** + * {@inheritdoc} + */ + public function format(array $record) + { + foreach ($record as $key => $value) { + $record[$key] = $this->normalizeValue($value); + } + + return $record; + } + + /** + * @param mixed $value + * @return mixed + */ + protected function normalizeValue($value) + { + $normalized = $this->normalize($value); + + if (is_array($normalized) || is_object($normalized)) { + return $this->toJson($normalized, true); + } + + return $normalized; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php new file mode 100644 index 0000000..654710a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Formatter/WildfireFormatter.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 Monolog\Formatter; + +use Monolog\Logger; + +/** + * Serializes a log message according to Wildfire's header requirements + * + * @author Eric Clemmons (@ericclemmons) + * @author Christophe Coevoet + * @author Kirill chEbba Chebunin + */ +class WildfireFormatter extends NormalizerFormatter +{ + const TABLE = 'table'; + + /** + * Translates Monolog log levels to Wildfire levels. + */ + private $logLevels = array( + Logger::DEBUG => 'LOG', + Logger::INFO => 'INFO', + Logger::NOTICE => 'INFO', + Logger::WARNING => 'WARN', + Logger::ERROR => 'ERROR', + Logger::CRITICAL => 'ERROR', + Logger::ALERT => 'ERROR', + Logger::EMERGENCY => 'ERROR', + ); + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + // Retrieve the line and file if set and remove them from the formatted extra + $file = $line = ''; + if (isset($record['extra']['file'])) { + $file = $record['extra']['file']; + unset($record['extra']['file']); + } + if (isset($record['extra']['line'])) { + $line = $record['extra']['line']; + unset($record['extra']['line']); + } + + $record = $this->normalize($record); + $message = array('message' => $record['message']); + $handleError = false; + if ($record['context']) { + $message['context'] = $record['context']; + $handleError = true; + } + if ($record['extra']) { + $message['extra'] = $record['extra']; + $handleError = true; + } + if (count($message) === 1) { + $message = reset($message); + } + + if (isset($record['context'][self::TABLE])) { + $type = 'TABLE'; + $label = $record['channel'] .': '. $record['message']; + $message = $record['context'][self::TABLE]; + } else { + $type = $this->logLevels[$record['level']]; + $label = $record['channel']; + } + + // Create JSON object describing the appearance of the message in the console + $json = $this->toJson(array( + array( + 'Type' => $type, + 'File' => $file, + 'Line' => $line, + 'Label' => $label, + ), + $message, + ), $handleError); + + // The message itself is a serialization of the above JSON object + it's length + return sprintf( + '%s|%s|', + strlen($json), + $json + ); + } + + public function formatBatch(array $records) + { + throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); + } + + protected function normalize($data) + { + if (is_object($data) && !$data instanceof \DateTime) { + return $data; + } + + return parent::normalize($data); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.php new file mode 100644 index 0000000..758a425 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\LineFormatter; + +/** + * Base Handler class providing the Handler structure + * + * @author Jordi Boggiano + */ +abstract class AbstractHandler implements HandlerInterface +{ + protected $level = Logger::DEBUG; + protected $bubble = true; + + /** + * @var FormatterInterface + */ + protected $formatter; + protected $processors = array(); + + /** + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + $this->setLevel($level); + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $record['level'] >= $this->level; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($records as $record) { + $this->handle($record); + } + } + + /** + * Closes the handler. + * + * This will be called automatically when the object is destroyed + */ + public function close() + { + } + + /** + * {@inheritdoc} + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + if (!$this->formatter) { + $this->formatter = $this->getDefaultFormatter(); + } + + return $this->formatter; + } + + /** + * Sets minimum logging level at which this handler will be triggered. + * + * @param int|string $level Level or level name + * @return self + */ + public function setLevel($level) + { + $this->level = Logger::toMonologLevel($level); + + return $this; + } + + /** + * Gets minimum logging level at which this handler will be triggered. + * + * @return int + */ + public function getLevel() + { + return $this->level; + } + + /** + * Sets the bubbling behavior. + * + * @param Boolean $bubble true means that this handler allows bubbling. + * false means that bubbling is not permitted. + * @return self + */ + public function setBubble($bubble) + { + $this->bubble = $bubble; + + return $this; + } + + /** + * Gets the bubbling behavior. + * + * @return Boolean true means that this handler allows bubbling. + * false means that bubbling is not permitted. + */ + public function getBubble() + { + return $this->bubble; + } + + public function __destruct() + { + try { + $this->close(); + } catch (\Exception $e) { + // do nothing + } catch (\Throwable $e) { + // do nothing + } + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php new file mode 100644 index 0000000..6f18f72 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.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 Monolog\Handler; + +/** + * Base Handler class providing the Handler structure + * + * Classes extending it should (in most cases) only implement write($record) + * + * @author Jordi Boggiano + * @author Christophe Coevoet + */ +abstract class AbstractProcessingHandler extends AbstractHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + $record = $this->processRecord($record); + + $record['formatted'] = $this->getFormatter()->format($record); + + $this->write($record); + + return false === $this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * @return void + */ + abstract protected function write(array $record); + + /** + * Processes a record. + * + * @param array $record + * @return array + */ + protected function processRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php new file mode 100644 index 0000000..e2b2832 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * Common syslog functionality + */ +abstract class AbstractSyslogHandler extends AbstractProcessingHandler +{ + protected $facility; + + /** + * Translates Monolog log levels to syslog log priorities. + */ + protected $logLevels = array( + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG, + ); + + /** + * List of valid log facility names. + */ + protected $facilities = array( + 'auth' => LOG_AUTH, + 'authpriv' => LOG_AUTHPRIV, + 'cron' => LOG_CRON, + 'daemon' => LOG_DAEMON, + 'kern' => LOG_KERN, + 'lpr' => LOG_LPR, + 'mail' => LOG_MAIL, + 'news' => LOG_NEWS, + 'syslog' => LOG_SYSLOG, + 'user' => LOG_USER, + 'uucp' => LOG_UUCP, + ); + + /** + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($facility = LOG_USER, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->facilities['local0'] = LOG_LOCAL0; + $this->facilities['local1'] = LOG_LOCAL1; + $this->facilities['local2'] = LOG_LOCAL2; + $this->facilities['local3'] = LOG_LOCAL3; + $this->facilities['local4'] = LOG_LOCAL4; + $this->facilities['local5'] = LOG_LOCAL5; + $this->facilities['local6'] = LOG_LOCAL6; + $this->facilities['local7'] = LOG_LOCAL7; + } else { + $this->facilities['local0'] = 128; // LOG_LOCAL0 + $this->facilities['local1'] = 136; // LOG_LOCAL1 + $this->facilities['local2'] = 144; // LOG_LOCAL2 + $this->facilities['local3'] = 152; // LOG_LOCAL3 + $this->facilities['local4'] = 160; // LOG_LOCAL4 + $this->facilities['local5'] = 168; // LOG_LOCAL5 + $this->facilities['local6'] = 176; // LOG_LOCAL6 + $this->facilities['local7'] = 184; // LOG_LOCAL7 + } + + // convert textual description of facility to syslog constant + if (array_key_exists(strtolower($facility), $this->facilities)) { + $facility = $this->facilities[strtolower($facility)]; + } elseif (!in_array($facility, array_values($this->facilities), true)) { + throw new \UnexpectedValueException('Unknown facility value "'.$facility.'" given'); + } + + $this->facility = $facility; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.php new file mode 100644 index 0000000..e5a46bc --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/AmqpHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\JsonFormatter; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Channel\AMQPChannel; +use AMQPExchange; + +class AmqpHandler extends AbstractProcessingHandler +{ + /** + * @var AMQPExchange|AMQPChannel $exchange + */ + protected $exchange; + + /** + * @var string + */ + protected $exchangeName; + + /** + * @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use + * @param string $exchangeName + * @param int $level + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = true) + { + if ($exchange instanceof AMQPExchange) { + $exchange->setName($exchangeName); + } elseif ($exchange instanceof AMQPChannel) { + $this->exchangeName = $exchangeName; + } else { + throw new \InvalidArgumentException('PhpAmqpLib\Channel\AMQPChannel or AMQPExchange instance required'); + } + $this->exchange = $exchange; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $data = $record["formatted"]; + $routingKey = $this->getRoutingKey($record); + + if ($this->exchange instanceof AMQPExchange) { + $this->exchange->publish( + $data, + $routingKey, + 0, + array( + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ) + ); + } else { + $this->exchange->basic_publish( + $this->createAmqpMessage($data), + $this->exchangeName, + $routingKey + ); + } + } + + /** + * {@inheritDoc} + */ + public function handleBatch(array $records) + { + if ($this->exchange instanceof AMQPExchange) { + parent::handleBatch($records); + + return; + } + + foreach ($records as $record) { + if (!$this->isHandling($record)) { + continue; + } + + $record = $this->processRecord($record); + $data = $this->getFormatter()->format($record); + + $this->exchange->batch_basic_publish( + $this->createAmqpMessage($data), + $this->exchangeName, + $this->getRoutingKey($record) + ); + } + + $this->exchange->publish_batch(); + } + + /** + * Gets the routing key for the AMQP exchange + * + * @param array $record + * @return string + */ + protected function getRoutingKey(array $record) + { + $routingKey = sprintf( + '%s.%s', + // TODO 2.0 remove substr call + substr($record['level_name'], 0, 4), + $record['channel'] + ); + + return strtolower($routingKey); + } + + /** + * @param string $data + * @return AMQPMessage + */ + private function createAmqpMessage($data) + { + return new AMQPMessage( + (string) $data, + array( + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ) + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php new file mode 100644 index 0000000..b3a21bd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; + +/** + * Handler sending logs to browser's javascript console with no browser extension required + * + * @author Olivier Poitrey + */ +class BrowserConsoleHandler extends AbstractProcessingHandler +{ + protected static $initialized = false; + protected static $records = array(); + + /** + * {@inheritDoc} + * + * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. + * + * Example of formatted string: + * + * You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + // Accumulate records + self::$records[] = $record; + + // Register shutdown handler if not already done + if (!self::$initialized) { + self::$initialized = true; + $this->registerShutdownFunction(); + } + } + + /** + * Convert records to javascript console commands and send it to the browser. + * This method is automatically called on PHP shutdown if output is HTML or Javascript. + */ + public static function send() + { + $format = self::getResponseFormat(); + if ($format === 'unknown') { + return; + } + + if (count(self::$records)) { + if ($format === 'html') { + self::writeOutput(''); + } elseif ($format === 'js') { + self::writeOutput(self::generateScript()); + } + self::reset(); + } + } + + /** + * Forget all logged records + */ + public static function reset() + { + self::$records = array(); + } + + /** + * Wrapper for register_shutdown_function to allow overriding + */ + protected function registerShutdownFunction() + { + if (PHP_SAPI !== 'cli') { + register_shutdown_function(array('Monolog\Handler\BrowserConsoleHandler', 'send')); + } + } + + /** + * Wrapper for echo to allow overriding + * + * @param string $str + */ + protected static function writeOutput($str) + { + echo $str; + } + + /** + * Checks the format of the response + * + * If Content-Type is set to application/javascript or text/javascript -> js + * If Content-Type is set to text/html, or is unset -> html + * If Content-Type is anything else -> unknown + * + * @return string One of 'js', 'html' or 'unknown' + */ + protected static function getResponseFormat() + { + // Check content type + foreach (headers_list() as $header) { + if (stripos($header, 'content-type:') === 0) { + // This handler only works with HTML and javascript outputs + // text/javascript is obsolete in favour of application/javascript, but still used + if (stripos($header, 'application/javascript') !== false || stripos($header, 'text/javascript') !== false) { + return 'js'; + } + if (stripos($header, 'text/html') === false) { + return 'unknown'; + } + break; + } + } + + return 'html'; + } + + private static function generateScript() + { + $script = array(); + foreach (self::$records as $record) { + $context = self::dump('Context', $record['context']); + $extra = self::dump('Extra', $record['extra']); + + if (empty($context) && empty($extra)) { + $script[] = self::call_array('log', self::handleStyles($record['formatted'])); + } else { + $script = array_merge($script, + array(self::call_array('groupCollapsed', self::handleStyles($record['formatted']))), + $context, + $extra, + array(self::call('groupEnd')) + ); + } + } + + return "(function (c) {if (c && c.groupCollapsed) {\n" . implode("\n", $script) . "\n}})(console);"; + } + + private static function handleStyles($formatted) + { + $args = array(self::quote('font-weight: normal')); + $format = '%c' . $formatted; + preg_match_all('/\[\[(.*?)\]\]\{([^}]*)\}/s', $format, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + + foreach (array_reverse($matches) as $match) { + $args[] = self::quote(self::handleCustomStyles($match[2][0], $match[1][0])); + $args[] = '"font-weight: normal"'; + + $pos = $match[0][1]; + $format = substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . substr($format, $pos + strlen($match[0][0])); + } + + array_unshift($args, self::quote($format)); + + return $args; + } + + private static function handleCustomStyles($style, $string) + { + static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'); + static $labels = array(); + + return preg_replace_callback('/macro\s*:(.*?)(?:;|$)/', function ($m) use ($string, &$colors, &$labels) { + if (trim($m[1]) === 'autolabel') { + // Format the string as a label with consistent auto assigned background color + if (!isset($labels[$string])) { + $labels[$string] = $colors[count($labels) % count($colors)]; + } + $color = $labels[$string]; + + return "background-color: $color; color: white; border-radius: 3px; padding: 0 2px 0 2px"; + } + + return $m[1]; + }, $style); + } + + private static function dump($title, array $dict) + { + $script = array(); + $dict = array_filter($dict); + if (empty($dict)) { + return $script; + } + $script[] = self::call('log', self::quote('%c%s'), self::quote('font-weight: bold'), self::quote($title)); + foreach ($dict as $key => $value) { + $value = json_encode($value); + if (empty($value)) { + $value = self::quote(''); + } + $script[] = self::call('log', self::quote('%s: %o'), self::quote($key), $value); + } + + return $script; + } + + private static function quote($arg) + { + return '"' . addcslashes($arg, "\"\n\\") . '"'; + } + + private static function call() + { + $args = func_get_args(); + $method = array_shift($args); + + return self::call_array($method, $args); + } + + private static function call_array($method, array $args) + { + return 'c.' . $method . '(' . implode(', ', $args) . ');'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php new file mode 100644 index 0000000..72f8953 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/BufferHandler.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Buffers all records until closing the handler and then pass them as batch. + * + * This is useful for a MailHandler to send only one mail per request instead of + * sending one per log message. + * + * @author Christophe Coevoet + */ +class BufferHandler extends AbstractHandler +{ + protected $handler; + protected $bufferSize = 0; + protected $bufferLimit; + protected $flushOnOverflow; + protected $buffer = array(); + protected $initialized = false; + + /** + * @param HandlerInterface $handler Handler. + * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded + */ + public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = true, $flushOnOverflow = false) + { + parent::__construct($level, $bubble); + $this->handler = $handler; + $this->bufferLimit = (int) $bufferLimit; + $this->flushOnOverflow = $flushOnOverflow; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + $this->initialized = true; + } + + if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { + if ($this->flushOnOverflow) { + $this->flush(); + } else { + array_shift($this->buffer); + $this->bufferSize--; + } + } + + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $this->buffer[] = $record; + $this->bufferSize++; + + return false === $this->bubble; + } + + public function flush() + { + if ($this->bufferSize === 0) { + return; + } + + $this->handler->handleBatch($this->buffer); + $this->clear(); + } + + public function __destruct() + { + // suppress the parent behavior since we already have register_shutdown_function() + // to call close(), and the reference contained there will prevent this from being + // GC'd until the end of the request + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->flush(); + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + */ + public function clear() + { + $this->bufferSize = 0; + $this->buffer = array(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php new file mode 100644 index 0000000..785cb0c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ChromePHPFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) + * + * This also works out of the box with Firefox 43+ + * + * @author Christophe Coevoet + */ +class ChromePHPHandler extends AbstractProcessingHandler +{ + /** + * Version of the extension + */ + const VERSION = '4.0'; + + /** + * Header name + */ + const HEADER_NAME = 'X-ChromeLogger-Data'; + + /** + * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) + */ + const USER_AGENT_REGEX = '{\b(?:Chrome/\d+(?:\.\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\d|\d{3,})(?:\.\d)*)\b}'; + + protected static $initialized = false; + + /** + * Tracks whether we sent too much data + * + * Chrome limits the headers to 256KB, so when we sent 240KB we stop sending + * + * @var Boolean + */ + protected static $overflowed = false; + + protected static $json = array( + 'version' => self::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array(), + ); + + protected static $sendHeaders = true; + + /** + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + if (!function_exists('json_encode')) { + throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); + } + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $messages = $this->getFormatter()->formatBatch($messages); + self::$json['rows'] = array_merge(self::$json['rows'], $messages); + $this->send(); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new ChromePHPFormatter(); + } + + /** + * Creates & sends header for a record + * + * @see sendHeader() + * @see send() + * @param array $record + */ + protected function write(array $record) + { + self::$json['rows'][] = $record['formatted']; + + $this->send(); + } + + /** + * Sends the log header + * + * @see sendHeader() + */ + protected function send() + { + if (self::$overflowed || !self::$sendHeaders) { + return; + } + + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; + } + + $json = @json_encode(self::$json); + $data = base64_encode(utf8_encode($json)); + if (strlen($data) > 240 * 1024) { + self::$overflowed = true; + + $record = array( + 'message' => 'Incomplete logs, chrome header size limit reached', + 'context' => array(), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'monolog', + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + self::$json['rows'][count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); + $json = @json_encode(self::$json); + $data = base64_encode(utf8_encode($json)); + } + + if (trim($data) !== '') { + $this->sendHeader(self::HEADER_NAME, $data); + } + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + if (empty($_SERVER['HTTP_USER_AGENT'])) { + return false; + } + + return preg_match(self::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']); + } + + /** + * BC getter for the sendHeaders property that has been made static + */ + public function __get($property) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + return static::$sendHeaders; + } + + /** + * BC setter for the sendHeaders property that has been made static + */ + public function __set($property, $value) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + static::$sendHeaders = $value; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.php new file mode 100644 index 0000000..cc98697 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CouchDBHandler.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 Monolog\Handler; + +use Monolog\Formatter\JsonFormatter; +use Monolog\Logger; + +/** + * CouchDB handler + * + * @author Markus Bachmann + */ +class CouchDBHandler extends AbstractProcessingHandler +{ + private $options; + + public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = true) + { + $this->options = array_merge(array( + 'host' => 'localhost', + 'port' => 5984, + 'dbname' => 'logger', + 'username' => null, + 'password' => null, + ), $options); + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $basicAuth = null; + if ($this->options['username']) { + $basicAuth = sprintf('%s:%s@', $this->options['username'], $this->options['password']); + } + + $url = 'http://'.$basicAuth.$this->options['host'].':'.$this->options['port'].'/'.$this->options['dbname']; + $context = stream_context_create(array( + 'http' => array( + 'method' => 'POST', + 'content' => $record['formatted'], + 'ignore_errors' => true, + 'max_redirects' => 0, + 'header' => 'Content-type: application/json', + ), + )); + + if (false === @file_get_contents($url, null, $context)) { + throw new \RuntimeException(sprintf('Could not connect to %s', $url)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.php new file mode 100644 index 0000000..96b3ca0 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/CubeHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Logs to Cube. + * + * @link http://square.github.com/cube/ + * @author Wan Chen + */ +class CubeHandler extends AbstractProcessingHandler +{ + private $udpConnection; + private $httpConnection; + private $scheme; + private $host; + private $port; + private $acceptedSchemes = array('http', 'udp'); + + /** + * Create a Cube handler + * + * @throws \UnexpectedValueException when given url is not a valid url. + * A valid url must consist of three parts : protocol://host:port + * Only valid protocols used by Cube are http and udp + */ + public function __construct($url, $level = Logger::DEBUG, $bubble = true) + { + $urlInfo = parse_url($url); + + if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { + throw new \UnexpectedValueException('URL "'.$url.'" is not valid'); + } + + if (!in_array($urlInfo['scheme'], $this->acceptedSchemes)) { + throw new \UnexpectedValueException( + 'Invalid protocol (' . $urlInfo['scheme'] . ').' + . ' Valid options are ' . implode(', ', $this->acceptedSchemes)); + } + + $this->scheme = $urlInfo['scheme']; + $this->host = $urlInfo['host']; + $this->port = $urlInfo['port']; + + parent::__construct($level, $bubble); + } + + /** + * Establish a connection to an UDP socket + * + * @throws \LogicException when unable to connect to the socket + * @throws MissingExtensionException when there is no socket extension + */ + protected function connectUdp() + { + if (!extension_loaded('sockets')) { + throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); + } + + $this->udpConnection = socket_create(AF_INET, SOCK_DGRAM, 0); + if (!$this->udpConnection) { + throw new \LogicException('Unable to create a socket'); + } + + if (!socket_connect($this->udpConnection, $this->host, $this->port)) { + throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); + } + } + + /** + * Establish a connection to a http server + * @throws \LogicException when no curl extension + */ + protected function connectHttp() + { + if (!extension_loaded('curl')) { + throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler'); + } + + $this->httpConnection = curl_init('http://'.$this->host.':'.$this->port.'/1.0/event/put'); + + if (!$this->httpConnection) { + throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); + } + + curl_setopt($this->httpConnection, CURLOPT_CUSTOMREQUEST, "POST"); + curl_setopt($this->httpConnection, CURLOPT_RETURNTRANSFER, true); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $date = $record['datetime']; + + $data = array('time' => $date->format('Y-m-d\TH:i:s.uO')); + unset($record['datetime']); + + if (isset($record['context']['type'])) { + $data['type'] = $record['context']['type']; + unset($record['context']['type']); + } else { + $data['type'] = $record['channel']; + } + + $data['data'] = $record['context']; + $data['data']['level'] = $record['level']; + + if ($this->scheme === 'http') { + $this->writeHttp(json_encode($data)); + } else { + $this->writeUdp(json_encode($data)); + } + } + + private function writeUdp($data) + { + if (!$this->udpConnection) { + $this->connectUdp(); + } + + socket_send($this->udpConnection, $data, strlen($data), 0); + } + + private function writeHttp($data) + { + if (!$this->httpConnection) { + $this->connectHttp(); + } + + curl_setopt($this->httpConnection, CURLOPT_POSTFIELDS, '['.$data.']'); + curl_setopt($this->httpConnection, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/json', + 'Content-Length: ' . strlen('['.$data.']'), + )); + + Curl\Util::execute($this->httpConnection, 5, false); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php b/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.php new file mode 100644 index 0000000..48d30b3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/Curl/Util.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 Monolog\Handler\Curl; + +class Util +{ + private static $retriableErrorCodes = array( + CURLE_COULDNT_RESOLVE_HOST, + CURLE_COULDNT_CONNECT, + CURLE_HTTP_NOT_FOUND, + CURLE_READ_ERROR, + CURLE_OPERATION_TIMEOUTED, + CURLE_HTTP_POST_ERROR, + CURLE_SSL_CONNECT_ERROR, + ); + + /** + * Executes a CURL request with optional retries and exception on failure + * + * @param resource $ch curl handler + * @throws \RuntimeException + */ + public static function execute($ch, $retries = 5, $closeAfterDone = true) + { + while ($retries--) { + if (curl_exec($ch) === false) { + $curlErrno = curl_errno($ch); + + if (false === in_array($curlErrno, self::$retriableErrorCodes, true) || !$retries) { + $curlError = curl_error($ch); + + if ($closeAfterDone) { + curl_close($ch); + } + + throw new \RuntimeException(sprintf('Curl error (code %s): %s', $curlErrno, $curlError)); + } + + continue; + } + + if ($closeAfterDone) { + curl_close($ch); + } + break; + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php new file mode 100644 index 0000000..7778c22 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DeduplicationHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Simple handler wrapper that deduplicates log records across multiple requests + * + * It also includes the BufferHandler functionality and will buffer + * all messages until the end of the request or flush() is called. + * + * This works by storing all log records' messages above $deduplicationLevel + * to the file specified by $deduplicationStore. When further logs come in at the end of the + * request (or when flush() is called), all those above $deduplicationLevel are checked + * against the existing stored logs. If they match and the timestamps in the stored log is + * not older than $time seconds, the new log record is discarded. If no log record is new, the + * whole data set is discarded. + * + * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers + * that send messages to people, to avoid spamming with the same message over and over in case of + * a major component failure like a database server being down which makes all requests fail in the + * same way. + * + * @author Jordi Boggiano + */ +class DeduplicationHandler extends BufferHandler +{ + /** + * @var string + */ + protected $deduplicationStore; + + /** + * @var int + */ + protected $deduplicationLevel; + + /** + * @var int + */ + protected $time; + + /** + * @var bool + */ + private $gc = false; + + /** + * @param HandlerInterface $handler Handler. + * @param string $deduplicationStore The file/path where the deduplication log should be kept + * @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes + * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = true) + { + parent::__construct($handler, 0, Logger::DEBUG, $bubble, false); + + $this->deduplicationStore = $deduplicationStore === null ? sys_get_temp_dir() . '/monolog-dedup-' . substr(md5(__FILE__), 0, 20) .'.log' : $deduplicationStore; + $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); + $this->time = $time; + } + + public function flush() + { + if ($this->bufferSize === 0) { + return; + } + + $passthru = null; + + foreach ($this->buffer as $record) { + if ($record['level'] >= $this->deduplicationLevel) { + + $passthru = $passthru || !$this->isDuplicate($record); + if ($passthru) { + $this->appendRecord($record); + } + } + } + + // default of null is valid as well as if no record matches duplicationLevel we just pass through + if ($passthru === true || $passthru === null) { + $this->handler->handleBatch($this->buffer); + } + + $this->clear(); + + if ($this->gc) { + $this->collectLogs(); + } + } + + private function isDuplicate(array $record) + { + if (!file_exists($this->deduplicationStore)) { + return false; + } + + $store = file($this->deduplicationStore, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!is_array($store)) { + return false; + } + + $yesterday = time() - 86400; + $timestampValidity = $record['datetime']->getTimestamp() - $this->time; + $expectedMessage = preg_replace('{[\r\n].*}', '', $record['message']); + + for ($i = count($store) - 1; $i >= 0; $i--) { + list($timestamp, $level, $message) = explode(':', $store[$i], 3); + + if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { + return true; + } + + if ($timestamp < $yesterday) { + $this->gc = true; + } + } + + return false; + } + + private function collectLogs() + { + if (!file_exists($this->deduplicationStore)) { + return false; + } + + $handle = fopen($this->deduplicationStore, 'rw+'); + flock($handle, LOCK_EX); + $validLogs = array(); + + $timestampValidity = time() - $this->time; + + while (!feof($handle)) { + $log = fgets($handle); + if (substr($log, 0, 10) >= $timestampValidity) { + $validLogs[] = $log; + } + } + + ftruncate($handle, 0); + rewind($handle); + foreach ($validLogs as $log) { + fwrite($handle, $log); + } + + flock($handle, LOCK_UN); + fclose($handle); + + $this->gc = false; + } + + private function appendRecord(array $record) + { + file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . preg_replace('{[\r\n].*}', '', $record['message']) . "\n", FILE_APPEND); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php new file mode 100644 index 0000000..b91ffec --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; +use Doctrine\CouchDB\CouchDBClient; + +/** + * CouchDB handler for Doctrine CouchDB ODM + * + * @author Markus Bachmann + */ +class DoctrineCouchDBHandler extends AbstractProcessingHandler +{ + private $client; + + public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = true) + { + $this->client = $client; + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $this->client->postDocument($record['formatted']); + } + + protected function getDefaultFormatter() + { + return new NormalizerFormatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php new file mode 100644 index 0000000..237b71f --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/DynamoDbHandler.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 Monolog\Handler; + +use Aws\Sdk; +use Aws\DynamoDb\DynamoDbClient; +use Aws\DynamoDb\Marshaler; +use Monolog\Formatter\ScalarFormatter; +use Monolog\Logger; + +/** + * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) + * + * @link https://github.com/aws/aws-sdk-php/ + * @author Andrew Lawson + */ +class DynamoDbHandler extends AbstractProcessingHandler +{ + const DATE_FORMAT = 'Y-m-d\TH:i:s.uO'; + + /** + * @var DynamoDbClient + */ + protected $client; + + /** + * @var string + */ + protected $table; + + /** + * @var int + */ + protected $version; + + /** + * @var Marshaler + */ + protected $marshaler; + + /** + * @param DynamoDbClient $client + * @param string $table + * @param int $level + * @param bool $bubble + */ + public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = true) + { + if (defined('Aws\Sdk::VERSION') && version_compare(Sdk::VERSION, '3.0', '>=')) { + $this->version = 3; + $this->marshaler = new Marshaler; + } else { + $this->version = 2; + } + + $this->client = $client; + $this->table = $table; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $filtered = $this->filterEmptyFields($record['formatted']); + if ($this->version === 3) { + $formatted = $this->marshaler->marshalItem($filtered); + } else { + $formatted = $this->client->formatAttributes($filtered); + } + + $this->client->putItem(array( + 'TableName' => $this->table, + 'Item' => $formatted, + )); + } + + /** + * @param array $record + * @return array + */ + protected function filterEmptyFields(array $record) + { + return array_filter($record, function ($value) { + return !empty($value) || false === $value || 0 === $value; + }); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new ScalarFormatter(self::DATE_FORMAT); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php new file mode 100644 index 0000000..8196740 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.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 Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Formatter\ElasticaFormatter; +use Monolog\Logger; +use Elastica\Client; +use Elastica\Exception\ExceptionInterface; + +/** + * Elastic Search handler + * + * Usage example: + * + * $client = new \Elastica\Client(); + * $options = array( + * 'index' => 'elastic_index_name', + * 'type' => 'elastic_doc_type', + * ); + * $handler = new ElasticSearchHandler($client, $options); + * $log = new Logger('application'); + * $log->pushHandler($handler); + * + * @author Jelle Vink + */ +class ElasticSearchHandler extends AbstractProcessingHandler +{ + /** + * @var Client + */ + protected $client; + + /** + * @var array Handler config options + */ + protected $options = array(); + + /** + * @param Client $client Elastica Client object + * @param array $options Handler configuration + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->client = $client; + $this->options = array_merge( + array( + 'index' => 'monolog', // Elastic index name + 'type' => 'record', // Elastic document type + 'ignore_error' => false, // Suppress Elastica exceptions + ), + $options + ); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + $this->bulkSend(array($record['formatted'])); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + if ($formatter instanceof ElasticaFormatter) { + return parent::setFormatter($formatter); + } + throw new \InvalidArgumentException('ElasticSearchHandler is only compatible with ElasticaFormatter'); + } + + /** + * Getter options + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new ElasticaFormatter($this->options['index'], $this->options['type']); + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $documents = $this->getFormatter()->formatBatch($records); + $this->bulkSend($documents); + } + + /** + * Use Elasticsearch bulk API to send list of documents + * @param array $documents + * @throws \RuntimeException + */ + protected function bulkSend(array $documents) + { + try { + $this->client->addDocuments($documents); + } catch (ExceptionInterface $e) { + if (!$this->options['ignore_error']) { + throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php new file mode 100644 index 0000000..1447a58 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ErrorLogHandler.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 Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Stores to PHP error_log() handler. + * + * @author Elan Ruusamäe + */ +class ErrorLogHandler extends AbstractProcessingHandler +{ + const OPERATING_SYSTEM = 0; + const SAPI = 4; + + protected $messageType; + protected $expandNewlines; + + /** + * @param int $messageType Says where the error should go. + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries + */ + public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = true, $expandNewlines = false) + { + parent::__construct($level, $bubble); + + if (false === in_array($messageType, self::getAvailableTypes())) { + $message = sprintf('The given message type "%s" is not supported', print_r($messageType, true)); + throw new \InvalidArgumentException($message); + } + + $this->messageType = $messageType; + $this->expandNewlines = $expandNewlines; + } + + /** + * @return array With all available types + */ + public static function getAvailableTypes() + { + return array( + self::OPERATING_SYSTEM, + self::SAPI, + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if ($this->expandNewlines) { + $lines = preg_split('{[\r\n]+}', (string) $record['formatted']); + foreach ($lines as $line) { + error_log($line, $this->messageType); + } + } else { + error_log((string) $record['formatted'], $this->messageType); + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.php new file mode 100644 index 0000000..2a0f7fd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FilterHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Simple handler wrapper that filters records based on a list of levels + * + * It can be configured with an exact list of levels to allow, or a min/max level. + * + * @author Hennadiy Verkh + * @author Jordi Boggiano + */ +class FilterHandler extends AbstractHandler +{ + /** + * Handler or factory callable($record, $this) + * + * @var callable|\Monolog\Handler\HandlerInterface + */ + protected $handler; + + /** + * Minimum level for logs that are passed to handler + * + * @var int[] + */ + protected $acceptedLevels; + + /** + * Whether the messages that are handled can bubble up the stack or not + * + * @var Boolean + */ + protected $bubble; + + /** + * @param callable|HandlerInterface $handler Handler or factory callable($record, $this). + * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided + * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = true) + { + $this->handler = $handler; + $this->bubble = $bubble; + $this->setAcceptedLevels($minLevelOrList, $maxLevel); + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * @return array + */ + public function getAcceptedLevels() + { + return array_flip($this->acceptedLevels); + } + + /** + * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided + * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array + */ + public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY) + { + if (is_array($minLevelOrList)) { + $acceptedLevels = array_map('Monolog\Logger::toMonologLevel', $minLevelOrList); + } else { + $minLevelOrList = Logger::toMonologLevel($minLevelOrList); + $maxLevel = Logger::toMonologLevel($maxLevel); + $acceptedLevels = array_values(array_filter(Logger::getLevels(), function ($level) use ($minLevelOrList, $maxLevel) { + return $level >= $minLevelOrList && $level <= $maxLevel; + })); + } + $this->acceptedLevels = array_flip($acceptedLevels); + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return isset($this->acceptedLevels[$record['level']]); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + // The same logic as in FingersCrossedHandler + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $this->handler->handle($record); + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $filtered = array(); + foreach ($records as $record) { + if ($this->isHandling($record)) { + $filtered[] = $record; + } + } + + $this->handler->handleBatch($filtered); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php new file mode 100644 index 0000000..c3e42ef --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.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 Monolog\Handler\FingersCrossed; + +/** + * Interface for activation strategies for the FingersCrossedHandler. + * + * @author Johannes M. Schmitt + */ +interface ActivationStrategyInterface +{ + /** + * Returns whether the given record activates the handler. + * + * @param array $record + * @return Boolean + */ + public function isHandlerActivated(array $record); +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php new file mode 100644 index 0000000..2a2a64d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.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 Monolog\Handler\FingersCrossed; + +use Monolog\Logger; + +/** + * Channel and Error level based monolog activation strategy. Allows to trigger activation + * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except + * for records of the 'sql' channel; those should trigger activation on level 'WARN'. + * + * Example: + * + * + * $activationStrategy = new ChannelLevelActivationStrategy( + * Logger::CRITICAL, + * array( + * 'request' => Logger::ALERT, + * 'sensitive' => Logger::ERROR, + * ) + * ); + * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); + * + * + * @author Mike Meessen + */ +class ChannelLevelActivationStrategy implements ActivationStrategyInterface +{ + private $defaultActionLevel; + private $channelToActionLevel; + + /** + * @param int $defaultActionLevel The default action level to be used if the record's category doesn't match any + * @param array $channelToActionLevel An array that maps channel names to action levels. + */ + public function __construct($defaultActionLevel, $channelToActionLevel = array()) + { + $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); + $this->channelToActionLevel = array_map('Monolog\Logger::toMonologLevel', $channelToActionLevel); + } + + public function isHandlerActivated(array $record) + { + if (isset($this->channelToActionLevel[$record['channel']])) { + return $record['level'] >= $this->channelToActionLevel[$record['channel']]; + } + + return $record['level'] >= $this->defaultActionLevel; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php new file mode 100644 index 0000000..6e63085 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.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 Monolog\Handler\FingersCrossed; + +use Monolog\Logger; + +/** + * Error level based activation strategy. + * + * @author Johannes M. Schmitt + */ +class ErrorLevelActivationStrategy implements ActivationStrategyInterface +{ + private $actionLevel; + + public function __construct($actionLevel) + { + $this->actionLevel = Logger::toMonologLevel($actionLevel); + } + + public function isHandlerActivated(array $record) + { + return $record['level'] >= $this->actionLevel; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php new file mode 100644 index 0000000..d1dcaac --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ActivationStrategyInterface; +use Monolog\Logger; + +/** + * Buffers all records until a certain level is reached + * + * The advantage of this approach is that you don't get any clutter in your log files. + * Only requests which actually trigger an error (or whatever your actionLevel is) will be + * in the logs, but they will contain all records, not only those above the level threshold. + * + * You can find the various activation strategies in the + * Monolog\Handler\FingersCrossed\ namespace. + * + * @author Jordi Boggiano + */ +class FingersCrossedHandler extends AbstractHandler +{ + protected $handler; + protected $activationStrategy; + protected $buffering = true; + protected $bufferSize; + protected $buffer = array(); + protected $stopBuffering; + protected $passthruLevel; + + /** + * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). + * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action + * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $stopBuffering Whether the handler should stop buffering after being triggered (default true) + * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered + */ + public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = true, $stopBuffering = true, $passthruLevel = null) + { + if (null === $activationStrategy) { + $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); + } + + // convert simple int activationStrategy to an object + if (!$activationStrategy instanceof ActivationStrategyInterface) { + $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); + } + + $this->handler = $handler; + $this->activationStrategy = $activationStrategy; + $this->bufferSize = $bufferSize; + $this->bubble = $bubble; + $this->stopBuffering = $stopBuffering; + + if ($passthruLevel !== null) { + $this->passthruLevel = Logger::toMonologLevel($passthruLevel); + } + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return true; + } + + /** + * Manually activate this logger regardless of the activation strategy + */ + public function activate() + { + if ($this->stopBuffering) { + $this->buffering = false; + } + if (!$this->handler instanceof HandlerInterface) { + $record = end($this->buffer) ?: null; + + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + if ($this->buffering) { + $this->buffer[] = $record; + if ($this->bufferSize > 0 && count($this->buffer) > $this->bufferSize) { + array_shift($this->buffer); + } + if ($this->activationStrategy->isHandlerActivated($record)) { + $this->activate(); + } + } else { + $this->handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function close() + { + if (null !== $this->passthruLevel) { + $level = $this->passthruLevel; + $this->buffer = array_filter($this->buffer, function ($record) use ($level) { + return $record['level'] >= $level; + }); + if (count($this->buffer) > 0) { + $this->handler->handleBatch($this->buffer); + $this->buffer = array(); + } + } + } + + /** + * Resets the state of the handler. Stops forwarding records to the wrapped handler. + */ + public function reset() + { + $this->buffering = true; + } + + /** + * Clears the buffer without flushing any messages down to the wrapped handler. + * + * It also resets the handler to its initial buffering state. + */ + public function clear() + { + $this->buffer = array(); + $this->reset(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php new file mode 100644 index 0000000..fee4795 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FirePHPHandler.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\WildfireFormatter; + +/** + * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. + * + * @author Eric Clemmons (@ericclemmons) + */ +class FirePHPHandler extends AbstractProcessingHandler +{ + /** + * WildFire JSON header message format + */ + const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; + + /** + * FirePHP structure for parsing messages & their presentation + */ + const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; + + /** + * Must reference a "known" plugin, otherwise headers won't display in FirePHP + */ + const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; + + /** + * Header prefix for Wildfire to recognize & parse headers + */ + const HEADER_PREFIX = 'X-Wf'; + + /** + * Whether or not Wildfire vendor-specific headers have been generated & sent yet + */ + protected static $initialized = false; + + /** + * Shared static message index between potentially multiple handlers + * @var int + */ + protected static $messageIndex = 1; + + protected static $sendHeaders = true; + + /** + * Base header creation function used by init headers & record headers + * + * @param array $meta Wildfire Plugin, Protocol & Structure Indexes + * @param string $message Log message + * @return array Complete header string ready for the client as key and message as value + */ + protected function createHeader(array $meta, $message) + { + $header = sprintf('%s-%s', self::HEADER_PREFIX, join('-', $meta)); + + return array($header => $message); + } + + /** + * Creates message header from record + * + * @see createHeader() + * @param array $record + * @return string + */ + protected function createRecordHeader(array $record) + { + // Wildfire is extensible to support multiple protocols & plugins in a single request, + // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. + return $this->createHeader( + array(1, 1, 1, self::$messageIndex++), + $record['formatted'] + ); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new WildfireFormatter(); + } + + /** + * Wildfire initialization headers to enable message parsing + * + * @see createHeader() + * @see sendHeader() + * @return array + */ + protected function getInitHeaders() + { + // Initial payload consists of required headers for Wildfire + return array_merge( + $this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), + $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), + $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI) + ); + } + + /** + * Send header string to the client + * + * @param string $header + * @param string $content + */ + protected function sendHeader($header, $content) + { + if (!headers_sent() && self::$sendHeaders) { + header(sprintf('%s: %s', $header, $content)); + } + } + + /** + * Creates & sends header for a record, ensuring init headers have been sent prior + * + * @see sendHeader() + * @see sendInitHeaders() + * @param array $record + */ + protected function write(array $record) + { + if (!self::$sendHeaders) { + return; + } + + // WildFire-specific headers must be sent prior to any messages + if (!self::$initialized) { + self::$initialized = true; + + self::$sendHeaders = $this->headersAccepted(); + if (!self::$sendHeaders) { + return; + } + + foreach ($this->getInitHeaders() as $header => $content) { + $this->sendHeader($header, $content); + } + } + + $header = $this->createRecordHeader($record); + if (trim(current($header)) !== '') { + $this->sendHeader(key($header), current($header)); + } + } + + /** + * Verifies if the headers are accepted by the current user agent + * + * @return Boolean + */ + protected function headersAccepted() + { + if (!empty($_SERVER['HTTP_USER_AGENT']) && preg_match('{\bFirePHP/\d+\.\d+\b}', $_SERVER['HTTP_USER_AGENT'])) { + return true; + } + + return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); + } + + /** + * BC getter for the sendHeaders property that has been made static + */ + public function __get($property) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + return static::$sendHeaders; + } + + /** + * BC setter for the sendHeaders property that has been made static + */ + public function __set($property, $value) + { + if ('sendHeaders' !== $property) { + throw new \InvalidArgumentException('Undefined property '.$property); + } + + static::$sendHeaders = $value; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.php new file mode 100644 index 0000000..c43c013 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FleepHookHandler.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 Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Sends logs to Fleep.io using Webhook integrations + * + * You'll need a Fleep.io account to use this handler. + * + * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation + * @author Ando Roots + */ +class FleepHookHandler extends SocketHandler +{ + const FLEEP_HOST = 'fleep.io'; + + const FLEEP_HOOK_URI = '/hook/'; + + /** + * @var string Webhook token (specifies the conversation where logs are sent) + */ + protected $token; + + /** + * Construct a new Fleep.io Handler. + * + * For instructions on how to create a new web hook in your conversations + * see https://fleep.io/integrations/webhooks/ + * + * @param string $token Webhook token + * @param bool|int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @throws MissingExtensionException + */ + public function __construct($token, $level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); + } + + $this->token = $token; + + $connectionString = 'ssl://' . self::FLEEP_HOST . ':443'; + parent::__construct($connectionString, $level, $bubble); + } + + /** + * Returns the default formatter to use with this handler + * + * Overloaded to remove empty context and extra arrays from the end of the log message. + * + * @return LineFormatter + */ + protected function getDefaultFormatter() + { + return new LineFormatter(null, null, true, true); + } + + /** + * Handles a log record + * + * @param array $record + */ + public function write(array $record) + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST " . self::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; + $header .= "Host: " . self::FLEEP_HOST . "\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = array( + 'message' => $record['formatted'], + ); + + return http_build_query($dataArray); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.php new file mode 100644 index 0000000..dd9a361 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/FlowdockHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\FlowdockFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Sends notifications through the Flowdock push API + * + * This must be configured with a FlowdockFormatter instance via setFormatter() + * + * Notes: + * API token - Flowdock API token + * + * @author Dominik Liebler + * @see https://www.flowdock.com/api/push + */ +class FlowdockHandler extends SocketHandler +{ + /** + * @var string + */ + protected $apiToken; + + /** + * @param string $apiToken + * @param bool|int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * + * @throws MissingExtensionException if OpenSSL is missing + */ + public function __construct($apiToken, $level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); + } + + parent::__construct('ssl://api.flowdock.com:443', $level, $bubble); + $this->apiToken = $apiToken; + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + if (!$formatter instanceof FlowdockFormatter) { + throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + return parent::setFormatter($formatter); + } + + /** + * Gets the default formatter. + * + * @return FormatterInterface + */ + protected function getDefaultFormatter() + { + throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\Formatter\FlowdockFormatter to function correctly'); + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + parent::write($record); + + $this->closeSocket(); + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + return json_encode($record['formatted']['flowdock']); + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; + $header .= "Host: api.flowdock.com\r\n"; + $header .= "Content-Type: application/json\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php new file mode 100644 index 0000000..d3847d8 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GelfHandler.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\IMessagePublisher; +use Gelf\PublisherInterface; +use Gelf\Publisher; +use InvalidArgumentException; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; + +/** + * Handler to send messages to a Graylog2 (http://www.graylog2.org) server + * + * @author Matt Lehner + * @author Benjamin Zikarsky + */ +class GelfHandler extends AbstractProcessingHandler +{ + /** + * @var Publisher the publisher object that sends the message to the server + */ + protected $publisher; + + /** + * @param PublisherInterface|IMessagePublisher|Publisher $publisher a publisher object + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($publisher, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$publisher instanceof Publisher && !$publisher instanceof IMessagePublisher && !$publisher instanceof PublisherInterface) { + throw new InvalidArgumentException('Invalid publisher, expected a Gelf\Publisher, Gelf\IMessagePublisher or Gelf\PublisherInterface instance'); + } + + $this->publisher = $publisher; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->publisher = null; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->publisher->publish($record['formatted']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new GelfMessageFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.php new file mode 100644 index 0000000..663f5a9 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/GroupHandler.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 Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Forwards records to multiple handlers + * + * @author Lenar Lõhmus + */ +class GroupHandler extends AbstractHandler +{ + protected $handlers; + + /** + * @param array $handlers Array of Handlers. + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(array $handlers, $bubble = true) + { + foreach ($handlers as $handler) { + if (!$handler instanceof HandlerInterface) { + throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); + } + } + + $this->handlers = $handlers; + $this->bubble = $bubble; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + foreach ($this->handlers as $handler) { + $handler->handle($record); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + if ($this->processors) { + $processed = array(); + foreach ($records as $record) { + foreach ($this->processors as $processor) { + $processed[] = call_user_func($processor, $record); + } + } + $records = $processed; + } + + foreach ($this->handlers as $handler) { + $handler->handleBatch($records); + } + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + foreach ($this->handlers as $handler) { + $handler->setFormatter($formatter); + } + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.php new file mode 100644 index 0000000..d920c4b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerInterface.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 Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * Interface that all Monolog Handlers must implement + * + * @author Jordi Boggiano + */ +interface HandlerInterface +{ + /** + * Checks whether the given record will be handled by this handler. + * + * This is mostly done for performance reasons, to avoid calling processors for nothing. + * + * Handlers should still check the record levels within handle(), returning false in isHandling() + * is no guarantee that handle() will not be called, and isHandling() might not be called + * for a given record. + * + * @param array $record Partial log record containing only a level key + * + * @return Boolean + */ + public function isHandling(array $record); + + /** + * Handles a record. + * + * All records may be passed to this method, and the handler should discard + * those that it does not want to handle. + * + * The return value of this function controls the bubbling process of the handler stack. + * Unless the bubbling is interrupted (by returning true), the Logger class will keep on + * calling further handlers in the stack with a given log record. + * + * @param array $record The record to handle + * @return Boolean true means that this handler handled the record, and that bubbling is not permitted. + * false means the record was either not processed or that this handler allows bubbling. + */ + public function handle(array $record); + + /** + * Handles a set of records at once. + * + * @param array $records The records to handle (an array of record arrays) + */ + public function handleBatch(array $records); + + /** + * Adds a processor in the stack. + * + * @param callable $callback + * @return self + */ + public function pushProcessor($callback); + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor(); + + /** + * Sets the formatter. + * + * @param FormatterInterface $formatter + * @return self + */ + public function setFormatter(FormatterInterface $formatter); + + /** + * Gets the formatter. + * + * @return FormatterInterface + */ + public function getFormatter(); +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php b/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.php new file mode 100644 index 0000000..e540d80 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HandlerWrapper.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 Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; + +/** + * This simple wrapper class can be used to extend handlers functionality. + * + * Example: A custom filtering that can be applied to any handler. + * + * Inherit from this class and override handle() like this: + * + * public function handle(array $record) + * { + * if ($record meets certain conditions) { + * return false; + * } + * return $this->handler->handle($record); + * } + * + * @author Alexey Karapetov + */ +class HandlerWrapper implements HandlerInterface +{ + /** + * @var HandlerInterface + */ + protected $handler; + + /** + * HandlerWrapper constructor. + * @param HandlerInterface $handler + */ + public function __construct(HandlerInterface $handler) + { + $this->handler = $handler; + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $this->handler->isHandling($record); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + return $this->handler->handle($record); + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + return $this->handler->handleBatch($records); + } + + /** + * {@inheritdoc} + */ + public function pushProcessor($callback) + { + $this->handler->pushProcessor($callback); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function popProcessor() + { + return $this->handler->popProcessor(); + } + + /** + * {@inheritdoc} + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->handler->setFormatter($formatter); + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFormatter() + { + return $this->handler->getFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.php new file mode 100644 index 0000000..73049f3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/HipChatHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Sends notifications through the hipchat api to a hipchat room + * + * Notes: + * API token - HipChat API token + * Room - HipChat Room Id or name, where messages are sent + * Name - Name used to send the message (from) + * notify - Should the message trigger a notification in the clients + * version - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2) + * + * @author Rafael Dohms + * @see https://www.hipchat.com/docs/api + */ +class HipChatHandler extends SocketHandler +{ + /** + * Use API version 1 + */ + const API_V1 = 'v1'; + + /** + * Use API version v2 + */ + const API_V2 = 'v2'; + + /** + * The maximum allowed length for the name used in the "from" field. + */ + const MAXIMUM_NAME_LENGTH = 15; + + /** + * The maximum allowed length for the message. + */ + const MAXIMUM_MESSAGE_LENGTH = 9500; + + /** + * @var string + */ + private $token; + + /** + * @var string + */ + private $room; + + /** + * @var string + */ + private $name; + + /** + * @var bool + */ + private $notify; + + /** + * @var string + */ + private $format; + + /** + * @var string + */ + private $host; + + /** + * @var string + */ + private $version; + + /** + * @param string $token HipChat API Token + * @param string $room The room that should be alerted of the message (Id or Name) + * @param string $name Name used in the "from" field. + * @param bool $notify Trigger a notification in clients or not + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $useSSL Whether to connect via SSL. + * @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) + * @param string $host The HipChat server hostname. + * @param string $version The HipChat API version (default HipChatHandler::API_V1) + */ + public function __construct($token, $room, $name = 'Monolog', $notify = false, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) + { + if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { + throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); + } + + $connectionString = $useSSL ? 'ssl://'.$host.':443' : $host.':80'; + parent::__construct($connectionString, $level, $bubble); + + $this->token = $token; + $this->name = $name; + $this->notify = $notify; + $this->room = $room; + $this->format = $format; + $this->host = $host; + $this->version = $version; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = array( + 'notify' => $this->version == self::API_V1 ? + ($this->notify ? 1 : 0) : + ($this->notify ? 'true' : 'false'), + 'message' => $record['formatted'], + 'message_format' => $this->format, + 'color' => $this->getAlertColor($record['level']), + ); + + if (!$this->validateStringLength($dataArray['message'], static::MAXIMUM_MESSAGE_LENGTH)) { + if (function_exists('mb_substr')) { + $dataArray['message'] = mb_substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]'; + } else { + $dataArray['message'] = substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH).' [truncated]'; + } + } + + // if we are using the legacy API then we need to send some additional information + if ($this->version == self::API_V1) { + $dataArray['room_id'] = $this->room; + } + + // append the sender name if it is set + // always append it if we use the v1 api (it is required in v1) + if ($this->version == self::API_V1 || $this->name !== null) { + $dataArray['from'] = (string) $this->name; + } + + return http_build_query($dataArray); + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + if ($this->version == self::API_V1) { + $header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n"; + } else { + // needed for rooms with special (spaces, etc) characters in the name + $room = rawurlencode($this->room); + $header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n"; + } + + $header .= "Host: {$this->host}\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * Assigns a color to each level of log records. + * + * @param int $level + * @return string + */ + protected function getAlertColor($level) + { + switch (true) { + case $level >= Logger::ERROR: + return 'red'; + case $level >= Logger::WARNING: + return 'yellow'; + case $level >= Logger::INFO: + return 'green'; + case $level == Logger::DEBUG: + return 'gray'; + default: + return 'yellow'; + } + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + parent::write($record); + $this->closeSocket(); + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + if (count($records) == 0) { + return true; + } + + $batchRecords = $this->combineRecords($records); + + $handled = false; + foreach ($batchRecords as $batchRecord) { + if ($this->isHandling($batchRecord)) { + $this->write($batchRecord); + $handled = true; + } + } + + if (!$handled) { + return false; + } + + return false === $this->bubble; + } + + /** + * Combines multiple records into one. Error level of the combined record + * will be the highest level from the given records. Datetime will be taken + * from the first record. + * + * @param $records + * @return array + */ + private function combineRecords($records) + { + $batchRecord = null; + $batchRecords = array(); + $messages = array(); + $formattedMessages = array(); + $level = 0; + $levelName = null; + $datetime = null; + + foreach ($records as $record) { + $record = $this->processRecord($record); + + if ($record['level'] > $level) { + $level = $record['level']; + $levelName = $record['level_name']; + } + + if (null === $datetime) { + $datetime = $record['datetime']; + } + + $messages[] = $record['message']; + $messageStr = implode(PHP_EOL, $messages); + $formattedMessages[] = $this->getFormatter()->format($record); + $formattedMessageStr = implode('', $formattedMessages); + + $batchRecord = array( + 'message' => $messageStr, + 'formatted' => $formattedMessageStr, + 'context' => array(), + 'extra' => array(), + ); + + if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) { + // Pop the last message and implode the remaining messages + $lastMessage = array_pop($messages); + $lastFormattedMessage = array_pop($formattedMessages); + $batchRecord['message'] = implode(PHP_EOL, $messages); + $batchRecord['formatted'] = implode('', $formattedMessages); + + $batchRecords[] = $batchRecord; + $messages = array($lastMessage); + $formattedMessages = array($lastFormattedMessage); + + $batchRecord = null; + } + } + + if (null !== $batchRecord) { + $batchRecords[] = $batchRecord; + } + + // Set the max level and datetime for all records + foreach ($batchRecords as &$batchRecord) { + $batchRecord = array_merge( + $batchRecord, + array( + 'level' => $level, + 'level_name' => $levelName, + 'datetime' => $datetime, + ) + ); + } + + return $batchRecords; + } + + /** + * Validates the length of a string. + * + * If the `mb_strlen()` function is available, it will use that, as HipChat + * allows UTF-8 characters. Otherwise, it will fall back to `strlen()`. + * + * Note that this might cause false failures in the specific case of using + * a valid name with less than 16 characters, but 16 or more bytes, on a + * system where `mb_strlen()` is unavailable. + * + * @param string $str + * @param int $length + * + * @return bool + */ + private function validateStringLength($str, $length) + { + if (function_exists('mb_strlen')) { + return (mb_strlen($str) <= $length); + } + + return (strlen($str) <= $length); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.php new file mode 100644 index 0000000..d60a3c8 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/IFTTTHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * IFTTTHandler uses cURL to trigger IFTTT Maker actions + * + * Register a secret key and trigger/event name at https://ifttt.com/maker + * + * value1 will be the channel from monolog's Logger constructor, + * value2 will be the level name (ERROR, WARNING, ..) + * value3 will be the log record's message + * + * @author Nehal Patel + */ +class IFTTTHandler extends AbstractProcessingHandler +{ + private $eventName; + private $secretKey; + + /** + * @param string $eventName The name of the IFTTT Maker event that should be triggered + * @param string $secretKey A valid IFTTT secret key + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = true) + { + $this->eventName = $eventName; + $this->secretKey = $secretKey; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + public function write(array $record) + { + $postData = array( + "value1" => $record["channel"], + "value2" => $record["level_name"], + "value3" => $record["message"], + ); + $postString = json_encode($postData); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $postString); + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + "Content-Type: application/json", + )); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php new file mode 100644 index 0000000..494c605 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogEntriesHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandler extends SocketHandler +{ + /** + * @var string + */ + protected $logToken; + + /** + * @param string $token Log token supplied by LogEntries + * @param bool $useSSL Whether or not SSL encryption should be used. + * @param int $level The minimum logging level to trigger this handler + * @param bool $bubble Whether or not messages that are handled should bubble up the stack. + * + * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing + */ + public function __construct($token, $useSSL = true, $level = Logger::DEBUG, $bubble = true) + { + if ($useSSL && !extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); + } + + $endpoint = $useSSL ? 'ssl://data.logentries.com:443' : 'data.logentries.com:80'; + parent::__construct($endpoint, $level, $bubble); + $this->logToken = $token; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + return $this->logToken . ' ' . $record['formatted']; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.php new file mode 100644 index 0000000..bcd62e1 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/LogglyHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LogglyFormatter; + +/** + * Sends errors to Loggly. + * + * @author Przemek Sobstel + * @author Adam Pancutt + * @author Gregory Barchard + */ +class LogglyHandler extends AbstractProcessingHandler +{ + const HOST = 'logs-01.loggly.com'; + const ENDPOINT_SINGLE = 'inputs'; + const ENDPOINT_BATCH = 'bulk'; + + protected $token; + + protected $tag = array(); + + public function __construct($token, $level = Logger::DEBUG, $bubble = true) + { + if (!extension_loaded('curl')) { + throw new \LogicException('The curl extension is needed to use the LogglyHandler'); + } + + $this->token = $token; + + parent::__construct($level, $bubble); + } + + public function setTag($tag) + { + $tag = !empty($tag) ? $tag : array(); + $this->tag = is_array($tag) ? $tag : array($tag); + } + + public function addTag($tag) + { + if (!empty($tag)) { + $tag = is_array($tag) ? $tag : array($tag); + $this->tag = array_unique(array_merge($this->tag, $tag)); + } + } + + protected function write(array $record) + { + $this->send($record["formatted"], self::ENDPOINT_SINGLE); + } + + public function handleBatch(array $records) + { + $level = $this->level; + + $records = array_filter($records, function ($record) use ($level) { + return ($record['level'] >= $level); + }); + + if ($records) { + $this->send($this->getFormatter()->formatBatch($records), self::ENDPOINT_BATCH); + } + } + + protected function send($data, $endpoint) + { + $url = sprintf("https://%s/%s/%s/", self::HOST, $endpoint, $this->token); + + $headers = array('Content-Type: application/json'); + + if (!empty($this->tag)) { + $headers[] = 'X-LOGGLY-TAG: '.implode(',', $this->tag); + } + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + + Curl\Util::execute($ch); + } + + protected function getDefaultFormatter() + { + return new LogglyFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.php new file mode 100644 index 0000000..9e23283 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MailHandler.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 Monolog\Handler; + +/** + * Base class for all mail handlers + * + * @author Gyula Sallai + */ +abstract class MailHandler extends AbstractProcessingHandler +{ + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $messages = array(); + + foreach ($records as $record) { + if ($record['level'] < $this->level) { + continue; + } + $messages[] = $this->processRecord($record); + } + + if (!empty($messages)) { + $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); + } + } + + /** + * Send a mail with the given content + * + * @param string $content formatted email body to be sent + * @param array $records the array of log records that formed this content + */ + abstract protected function send($content, array $records); + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->send((string) $record['formatted'], array($record)); + } + + protected function getHighestRecord(array $records) + { + $highestRecord = null; + foreach ($records as $record) { + if ($highestRecord === null || $highestRecord['level'] < $record['level']) { + $highestRecord = $record; + } + } + + return $highestRecord; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.php new file mode 100644 index 0000000..ab95924 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MandrillHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * MandrillHandler uses cURL to send the emails to the Mandrill API + * + * @author Adam Nicholson + */ +class MandrillHandler extends MailHandler +{ + protected $message; + protected $apiKey; + + /** + * @param string $apiKey A valid Mandrill API key + * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + + if (!$message instanceof \Swift_Message && is_callable($message)) { + $message = call_user_func($message); + } + if (!$message instanceof \Swift_Message) { + throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); + } + $this->message = $message; + $this->apiKey = $apiKey; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $message = clone $this->message; + $message->setBody($content); + $message->setDate(time()); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array( + 'key' => $this->apiKey, + 'raw_message' => (string) $message, + 'async' => false, + ))); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.php new file mode 100644 index 0000000..4724a7e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MissingExtensionException.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 Monolog\Handler; + +/** + * Exception can be thrown if an extension for an handler is missing + * + * @author Christian Bergau + */ +class MissingExtensionException extends \Exception +{ +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.php new file mode 100644 index 0000000..56fe755 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/MongoDBHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; + +/** + * Logs to a MongoDB database. + * + * usage example: + * + * $log = new Logger('application'); + * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); + * $log->pushHandler($mongodb); + * + * @author Thomas Tourlourat + */ +class MongoDBHandler extends AbstractProcessingHandler +{ + protected $mongoCollection; + + public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = true) + { + if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo || $mongo instanceof \MongoDB\Client)) { + throw new \InvalidArgumentException('MongoClient, Mongo or MongoDB\Client instance required'); + } + + $this->mongoCollection = $mongo->selectCollection($database, $collection); + + parent::__construct($level, $bubble); + } + + protected function write(array $record) + { + if ($this->mongoCollection instanceof \MongoDB\Collection) { + $this->mongoCollection->insertOne($record["formatted"]); + } else { + $this->mongoCollection->save($record["formatted"]); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new NormalizerFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php new file mode 100644 index 0000000..d7807fd --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NativeMailerHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +/** + * NativeMailerHandler uses the mail() function to send the emails + * + * @author Christophe Coevoet + * @author Mark Garrett + */ +class NativeMailerHandler extends MailHandler +{ + /** + * The email addresses to which the message will be sent + * @var array + */ + protected $to; + + /** + * The subject of the email + * @var string + */ + protected $subject; + + /** + * Optional headers for the message + * @var array + */ + protected $headers = array(); + + /** + * Optional parameters for the message + * @var array + */ + protected $parameters = array(); + + /** + * The wordwrap length for the message + * @var int + */ + protected $maxColumnWidth; + + /** + * The Content-type for the message + * @var string + */ + protected $contentType = 'text/plain'; + + /** + * The encoding for the message + * @var string + */ + protected $encoding = 'utf-8'; + + /** + * @param string|array $to The receiver of the mail + * @param string $subject The subject of the mail + * @param string $from The sender of the mail + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $maxColumnWidth The maximum column width that the message lines will have + */ + public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = true, $maxColumnWidth = 70) + { + parent::__construct($level, $bubble); + $this->to = is_array($to) ? $to : array($to); + $this->subject = $subject; + $this->addHeader(sprintf('From: %s', $from)); + $this->maxColumnWidth = $maxColumnWidth; + } + + /** + * Add headers to the message + * + * @param string|array $headers Custom added headers + * @return self + */ + public function addHeader($headers) + { + foreach ((array) $headers as $header) { + if (strpos($header, "\n") !== false || strpos($header, "\r") !== false) { + throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); + } + $this->headers[] = $header; + } + + return $this; + } + + /** + * Add parameters to the message + * + * @param string|array $parameters Custom added parameters + * @return self + */ + public function addParameter($parameters) + { + $this->parameters = array_merge($this->parameters, (array) $parameters); + + return $this; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $content = wordwrap($content, $this->maxColumnWidth); + $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); + $headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n"; + if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) { + $headers .= 'MIME-Version: 1.0' . "\r\n"; + } + + $subject = $this->subject; + if ($records) { + $subjectFormatter = new LineFormatter($this->subject); + $subject = $subjectFormatter->format($this->getHighestRecord($records)); + } + + $parameters = implode(' ', $this->parameters); + foreach ($this->to as $to) { + mail($to, $subject, $content, $headers, $parameters); + } + } + + /** + * @return string $contentType + */ + public function getContentType() + { + return $this->contentType; + } + + /** + * @return string $encoding + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML + * messages. + * @return self + */ + public function setContentType($contentType) + { + if (strpos($contentType, "\n") !== false || strpos($contentType, "\r") !== false) { + throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); + } + + $this->contentType = $contentType; + + return $this; + } + + /** + * @param string $encoding + * @return self + */ + public function setEncoding($encoding) + { + if (strpos($encoding, "\n") !== false || strpos($encoding, "\r") !== false) { + throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); + } + + $this->encoding = $encoding; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php new file mode 100644 index 0000000..6718e9e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NewRelicHandler.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; + +/** + * Class to record a log on a NewRelic application. + * Enabling New Relic High Security mode may prevent capture of useful information. + * + * @see https://docs.newrelic.com/docs/agents/php-agent + * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security + */ +class NewRelicHandler extends AbstractProcessingHandler +{ + /** + * Name of the New Relic application that will receive logs from this handler. + * + * @var string + */ + protected $appName; + + /** + * Name of the current transaction + * + * @var string + */ + protected $transactionName; + + /** + * Some context and extra data is passed into the handler as arrays of values. Do we send them as is + * (useful if we are using the API), or explode them for display on the NewRelic RPM website? + * + * @var bool + */ + protected $explodeArrays; + + /** + * {@inheritDoc} + * + * @param string $appName + * @param bool $explodeArrays + * @param string $transactionName + */ + public function __construct( + $level = Logger::ERROR, + $bubble = true, + $appName = null, + $explodeArrays = false, + $transactionName = null + ) { + parent::__construct($level, $bubble); + + $this->appName = $appName; + $this->explodeArrays = $explodeArrays; + $this->transactionName = $transactionName; + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + if (!$this->isNewRelicEnabled()) { + throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); + } + + if ($appName = $this->getAppName($record['context'])) { + $this->setNewRelicAppName($appName); + } + + if ($transactionName = $this->getTransactionName($record['context'])) { + $this->setNewRelicTransactionName($transactionName); + unset($record['formatted']['context']['transaction_name']); + } + + if (isset($record['context']['exception']) && $record['context']['exception'] instanceof \Exception) { + newrelic_notice_error($record['message'], $record['context']['exception']); + unset($record['formatted']['context']['exception']); + } else { + newrelic_notice_error($record['message']); + } + + if (isset($record['formatted']['context']) && is_array($record['formatted']['context'])) { + foreach ($record['formatted']['context'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('context_' . $key, $parameter); + } + } + } + + if (isset($record['formatted']['extra']) && is_array($record['formatted']['extra'])) { + foreach ($record['formatted']['extra'] as $key => $parameter) { + if (is_array($parameter) && $this->explodeArrays) { + foreach ($parameter as $paramKey => $paramValue) { + $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); + } + } else { + $this->setNewRelicParameter('extra_' . $key, $parameter); + } + } + } + } + + /** + * Checks whether the NewRelic extension is enabled in the system. + * + * @return bool + */ + protected function isNewRelicEnabled() + { + return extension_loaded('newrelic'); + } + + /** + * Returns the appname where this log should be sent. Each log can override the default appname, set in this + * handler's constructor, by providing the appname in it's context. + * + * @param array $context + * @return null|string + */ + protected function getAppName(array $context) + { + if (isset($context['appname'])) { + return $context['appname']; + } + + return $this->appName; + } + + /** + * Returns the name of the current transaction. Each log can override the default transaction name, set in this + * handler's constructor, by providing the transaction_name in it's context + * + * @param array $context + * + * @return null|string + */ + protected function getTransactionName(array $context) + { + if (isset($context['transaction_name'])) { + return $context['transaction_name']; + } + + return $this->transactionName; + } + + /** + * Sets the NewRelic application that should receive this log. + * + * @param string $appName + */ + protected function setNewRelicAppName($appName) + { + newrelic_set_appname($appName); + } + + /** + * Overwrites the name of the current transaction + * + * @param string $transactionName + */ + protected function setNewRelicTransactionName($transactionName) + { + newrelic_name_transaction($transactionName); + } + + /** + * @param string $key + * @param mixed $value + */ + protected function setNewRelicParameter($key, $value) + { + if (null === $value || is_scalar($value)) { + newrelic_add_custom_parameter($key, $value); + } else { + newrelic_add_custom_parameter($key, @json_encode($value)); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new NormalizerFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.php new file mode 100644 index 0000000..4b84588 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/NullHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Blackhole + * + * Any record it can handle will be thrown away. This can be used + * to put on top of an existing stack to override it temporarily. + * + * @author Jordi Boggiano + */ +class NullHandler extends AbstractHandler +{ + /** + * @param int $level The minimum logging level at which this handler will be triggered + */ + public function __construct($level = Logger::DEBUG) + { + parent::__construct($level, false); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($record['level'] < $this->level) { + return false; + } + + return true; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php new file mode 100644 index 0000000..1f2076a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.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 Monolog\Handler; + +use Exception; +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; +use PhpConsole\Connector; +use PhpConsole\Handler; +use PhpConsole\Helper; + +/** + * Monolog handler for Google Chrome extension "PHP Console" + * + * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely + * + * Usage: + * 1. Install Google Chrome extension https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef + * 2. See overview https://github.com/barbushin/php-console#overview + * 3. Install PHP Console library https://github.com/barbushin/php-console#installation + * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) + * + * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); + * \Monolog\ErrorHandler::register($logger); + * echo $undefinedVar; + * $logger->addDebug('SELECT * FROM users', array('db', 'time' => 0.012)); + * PC::debug($_SERVER); // PHP Console debugger for any type of vars + * + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + */ +class PHPConsoleHandler extends AbstractProcessingHandler +{ + private $options = array( + 'enabled' => true, // bool Is PHP Console server enabled + 'classesPartialsTraceIgnore' => array('Monolog\\'), // array Hide calls of classes started with... + 'debugTagsKeysInContext' => array(0, 'tag'), // bool Is PHP Console server enabled + 'useOwnErrorsHandler' => false, // bool Enable errors handling + 'useOwnExceptionsHandler' => false, // bool Enable exceptions handling + 'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths + 'registerHelper' => true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') + 'serverEncoding' => null, // string|null Server internal encoding + 'headersLimit' => null, // int|null Set headers size limit for your web-server + 'password' => null, // string|null Protect PHP Console connection by password + 'enableSslOnlyMode' => false, // bool Force connection by SSL for clients with PHP Console installed + 'ipMasks' => array(), // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') + 'enableEvalListener' => false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) + 'dumperDetectCallbacks' => false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings + 'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level + 'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number + 'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item + 'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON + 'detectDumpTraceAndSource' => false, // bool Autodetect and append trace data to debug + 'dataStorage' => null, // PhpConsole\Storage|null Fixes problem with custom $_SESSION handler(see http://goo.gl/Ne8juJ) + ); + + /** @var Connector */ + private $connector; + + /** + * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details + * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) + * @param int $level + * @param bool $bubble + * @throws Exception + */ + public function __construct(array $options = array(), Connector $connector = null, $level = Logger::DEBUG, $bubble = true) + { + if (!class_exists('PhpConsole\Connector')) { + throw new Exception('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + parent::__construct($level, $bubble); + $this->options = $this->initOptions($options); + $this->connector = $this->initConnector($connector); + } + + private function initOptions(array $options) + { + $wrongOptions = array_diff(array_keys($options), array_keys($this->options)); + if ($wrongOptions) { + throw new Exception('Unknown options: ' . implode(', ', $wrongOptions)); + } + + return array_replace($this->options, $options); + } + + private function initConnector(Connector $connector = null) + { + if (!$connector) { + if ($this->options['dataStorage']) { + Connector::setPostponeStorage($this->options['dataStorage']); + } + $connector = Connector::getInstance(); + } + + if ($this->options['registerHelper'] && !Helper::isRegistered()) { + Helper::register(); + } + + if ($this->options['enabled'] && $connector->isActiveClient()) { + if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { + $handler = Handler::getInstance(); + $handler->setHandleErrors($this->options['useOwnErrorsHandler']); + $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); + $handler->start(); + } + if ($this->options['sourcesBasePath']) { + $connector->setSourcesBasePath($this->options['sourcesBasePath']); + } + if ($this->options['serverEncoding']) { + $connector->setServerEncoding($this->options['serverEncoding']); + } + if ($this->options['password']) { + $connector->setPassword($this->options['password']); + } + if ($this->options['enableSslOnlyMode']) { + $connector->enableSslOnlyMode(); + } + if ($this->options['ipMasks']) { + $connector->setAllowedIpMasks($this->options['ipMasks']); + } + if ($this->options['headersLimit']) { + $connector->setHeadersLimit($this->options['headersLimit']); + } + if ($this->options['detectDumpTraceAndSource']) { + $connector->getDebugDispatcher()->detectTraceAndSource = true; + } + $dumper = $connector->getDumper(); + $dumper->levelLimit = $this->options['dumperLevelLimit']; + $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; + $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; + $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; + $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; + if ($this->options['enableEvalListener']) { + $connector->startEvalRequestsListener(); + } + } + + return $connector; + } + + public function getConnector() + { + return $this->connector; + } + + public function getOptions() + { + return $this->options; + } + + public function handle(array $record) + { + if ($this->options['enabled'] && $this->connector->isActiveClient()) { + return parent::handle($record); + } + + return !$this->bubble; + } + + /** + * Writes the record down to the log of the implementing handler + * + * @param array $record + * @return void + */ + protected function write(array $record) + { + if ($record['level'] < Logger::NOTICE) { + $this->handleDebugRecord($record); + } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) { + $this->handleExceptionRecord($record); + } else { + $this->handleErrorRecord($record); + } + } + + private function handleDebugRecord(array $record) + { + $tags = $this->getRecordTags($record); + $message = $record['message']; + if ($record['context']) { + $message .= ' ' . json_encode($this->connector->getDumper()->dump(array_filter($record['context']))); + } + $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); + } + + private function handleExceptionRecord(array $record) + { + $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); + } + + private function handleErrorRecord(array $record) + { + $context = $record['context']; + + $this->connector->getErrorsDispatcher()->dispatchError( + isset($context['code']) ? $context['code'] : null, + isset($context['message']) ? $context['message'] : $record['message'], + isset($context['file']) ? $context['file'] : null, + isset($context['line']) ? $context['line'] : null, + $this->options['classesPartialsTraceIgnore'] + ); + } + + private function getRecordTags(array &$record) + { + $tags = null; + if (!empty($record['context'])) { + $context = & $record['context']; + foreach ($this->options['debugTagsKeysInContext'] as $key) { + if (!empty($context[$key])) { + $tags = $context[$key]; + if ($key === 0) { + array_shift($context); + } else { + unset($context[$key]); + } + break; + } + } + } + + return $tags ?: strtolower($record['level_name']); + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('%message%'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.php new file mode 100644 index 0000000..1ae8584 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PsrHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Psr\Log\LoggerInterface; + +/** + * Proxies log messages to an existing PSR-3 compliant logger. + * + * @author Michael Moussa + */ +class PsrHandler extends AbstractHandler +{ + /** + * PSR-3 compliant logger + * + * @var LoggerInterface + */ + protected $logger; + + /** + * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->logger = $logger; + } + + /** + * {@inheritDoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + $this->logger->log(strtolower($record['level_name']), $record['message'], $record['context']); + + return false === $this->bubble; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.php new file mode 100644 index 0000000..bba7200 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/PushoverHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Sends notifications through the pushover api to mobile phones + * + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + */ +class PushoverHandler extends SocketHandler +{ + private $token; + private $users; + private $title; + private $user; + private $retry; + private $expire; + + private $highPriorityLevel; + private $emergencyLevel; + private $useFormattedMessage = false; + + /** + * All parameters that can be sent to Pushover + * @see https://pushover.net/api + * @var array + */ + private $parameterNames = array( + 'token' => true, + 'user' => true, + 'message' => true, + 'device' => true, + 'title' => true, + 'url' => true, + 'url_title' => true, + 'priority' => true, + 'timestamp' => true, + 'sound' => true, + 'retry' => true, + 'expire' => true, + 'callback' => true, + ); + + /** + * Sounds the api supports by default + * @see https://pushover.net/api#sounds + * @var array + */ + private $sounds = array( + 'pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', + 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', + 'persistent', 'echo', 'updown', 'none', + ); + + /** + * @param string $token Pushover api token + * @param string|array $users Pushover user id or array of ids the message will be sent to + * @param string $title Title sent to the Pushover API + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param Boolean $useSSL Whether to connect via SSL. Required when pushing messages to users that are not + * the pushover.net app owner. OpenSSL is required for this option. + * @param int $highPriorityLevel The minimum logging level at which this handler will start + * sending "high priority" requests to the Pushover API + * @param int $emergencyLevel The minimum logging level at which this handler will start + * sending "emergency" requests to the Pushover API + * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user. + * @param int $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds). + */ + public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = true, $useSSL = true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200) + { + $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; + parent::__construct($connectionString, $level, $bubble); + + $this->token = $token; + $this->users = (array) $users; + $this->title = $title ?: gethostname(); + $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); + $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); + $this->retry = $retry; + $this->expire = $expire; + } + + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + private function buildContent($record) + { + // Pushover has a limit of 512 characters on title and message combined. + $maxMessageLength = 512 - strlen($this->title); + + $message = ($this->useFormattedMessage) ? $record['formatted'] : $record['message']; + $message = substr($message, 0, $maxMessageLength); + + $timestamp = $record['datetime']->getTimestamp(); + + $dataArray = array( + 'token' => $this->token, + 'user' => $this->user, + 'message' => $message, + 'title' => $this->title, + 'timestamp' => $timestamp, + ); + + if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { + $dataArray['priority'] = 2; + $dataArray['retry'] = $this->retry; + $dataArray['expire'] = $this->expire; + } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { + $dataArray['priority'] = 1; + } + + // First determine the available parameters + $context = array_intersect_key($record['context'], $this->parameterNames); + $extra = array_intersect_key($record['extra'], $this->parameterNames); + + // Least important info should be merged with subsequent info + $dataArray = array_merge($extra, $context, $dataArray); + + // Only pass sounds that are supported by the API + if (isset($dataArray['sound']) && !in_array($dataArray['sound'], $this->sounds)) { + unset($dataArray['sound']); + } + + return http_build_query($dataArray); + } + + private function buildHeader($content) + { + $header = "POST /1/messages.json HTTP/1.1\r\n"; + $header .= "Host: api.pushover.net\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + protected function write(array $record) + { + foreach ($this->users as $user) { + $this->user = $user; + + parent::write($record); + $this->closeSocket(); + } + + $this->user = null; + } + + public function setHighPriorityLevel($value) + { + $this->highPriorityLevel = $value; + } + + public function setEmergencyLevel($value) + { + $this->emergencyLevel = $value; + } + + /** + * Use the formatted message? + * @param bool $value + */ + public function useFormattedMessage($value) + { + $this->useFormattedMessage = (boolean) $value; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php new file mode 100644 index 0000000..53a8b39 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RavenHandler.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Raven_Client; + +/** + * Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server + * using raven-php (https://github.com/getsentry/raven-php) + * + * @author Marc Abramowitz + */ +class RavenHandler extends AbstractProcessingHandler +{ + /** + * Translates Monolog log levels to Raven log levels. + */ + private $logLevels = array( + Logger::DEBUG => Raven_Client::DEBUG, + Logger::INFO => Raven_Client::INFO, + Logger::NOTICE => Raven_Client::INFO, + Logger::WARNING => Raven_Client::WARNING, + Logger::ERROR => Raven_Client::ERROR, + Logger::CRITICAL => Raven_Client::FATAL, + Logger::ALERT => Raven_Client::FATAL, + Logger::EMERGENCY => Raven_Client::FATAL, + ); + + /** + * @var string should represent the current version of the calling + * software. Can be any string (git commit, version number) + */ + private $release; + + /** + * @var Raven_Client the client object that sends the message to the server + */ + protected $ravenClient; + + /** + * @var LineFormatter The formatter to use for the logs generated via handleBatch() + */ + protected $batchFormatter; + + /** + * @param Raven_Client $ravenClient + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->ravenClient = $ravenClient; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + $level = $this->level; + + // filter records based on their level + $records = array_filter($records, function ($record) use ($level) { + return $record['level'] >= $level; + }); + + if (!$records) { + return; + } + + // the record with the highest severity is the "main" one + $record = array_reduce($records, function ($highest, $record) { + if ($record['level'] > $highest['level']) { + return $record; + } + + return $highest; + }); + + // the other ones are added as a context item + $logs = array(); + foreach ($records as $r) { + $logs[] = $this->processRecord($r); + } + + if ($logs) { + $record['context']['logs'] = (string) $this->getBatchFormatter()->formatBatch($logs); + } + + $this->handle($record); + } + + /** + * Sets the formatter for the logs generated by handleBatch(). + * + * @param FormatterInterface $formatter + */ + public function setBatchFormatter(FormatterInterface $formatter) + { + $this->batchFormatter = $formatter; + } + + /** + * Gets the formatter for the logs generated by handleBatch(). + * + * @return FormatterInterface + */ + public function getBatchFormatter() + { + if (!$this->batchFormatter) { + $this->batchFormatter = $this->getDefaultBatchFormatter(); + } + + return $this->batchFormatter; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $previousUserContext = false; + $options = array(); + $options['level'] = $this->logLevels[$record['level']]; + $options['tags'] = array(); + if (!empty($record['extra']['tags'])) { + $options['tags'] = array_merge($options['tags'], $record['extra']['tags']); + unset($record['extra']['tags']); + } + if (!empty($record['context']['tags'])) { + $options['tags'] = array_merge($options['tags'], $record['context']['tags']); + unset($record['context']['tags']); + } + if (!empty($record['context']['fingerprint'])) { + $options['fingerprint'] = $record['context']['fingerprint']; + unset($record['context']['fingerprint']); + } + if (!empty($record['context']['logger'])) { + $options['logger'] = $record['context']['logger']; + unset($record['context']['logger']); + } else { + $options['logger'] = $record['channel']; + } + foreach ($this->getExtraParameters() as $key) { + foreach (array('extra', 'context') as $source) { + if (!empty($record[$source][$key])) { + $options[$key] = $record[$source][$key]; + unset($record[$source][$key]); + } + } + } + if (!empty($record['context'])) { + $options['extra']['context'] = $record['context']; + if (!empty($record['context']['user'])) { + $previousUserContext = $this->ravenClient->context->user; + $this->ravenClient->user_context($record['context']['user']); + unset($options['extra']['context']['user']); + } + } + if (!empty($record['extra'])) { + $options['extra']['extra'] = $record['extra']; + } + + if (!empty($this->release) && !isset($options['release'])) { + $options['release'] = $this->release; + } + + if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) { + $options['extra']['message'] = $record['formatted']; + $this->ravenClient->captureException($record['context']['exception'], $options); + } else { + $this->ravenClient->captureMessage($record['formatted'], array(), $options); + } + + if ($previousUserContext !== false) { + $this->ravenClient->user_context($previousUserContext); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter('[%channel%] %message%'); + } + + /** + * Gets the default formatter for the logs generated by handleBatch(). + * + * @return FormatterInterface + */ + protected function getDefaultBatchFormatter() + { + return new LineFormatter(); + } + + /** + * Gets extra parameters supported by Raven that can be found in "extra" and "context" + * + * @return array + */ + protected function getExtraParameters() + { + return array('checksum', 'release', 'event_id'); + } + + /** + * @param string $value + * @return self + */ + public function setRelease($value) + { + $this->release = $value; + + return $this; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.php new file mode 100644 index 0000000..590f996 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RedisHandler.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 Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; + +/** + * Logs to a Redis key using rpush + * + * usage example: + * + * $log = new Logger('application'); + * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); + * $log->pushHandler($redis); + * + * @author Thomas Tourlourat + */ +class RedisHandler extends AbstractProcessingHandler +{ + private $redisClient; + private $redisKey; + protected $capSize; + + /** + * @param \Predis\Client|\Redis $redis The redis instance + * @param string $key The key name to push records to + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $capSize Number of entries to limit list size to + */ + public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = true, $capSize = false) + { + if (!(($redis instanceof \Predis\Client) || ($redis instanceof \Redis))) { + throw new \InvalidArgumentException('Predis\Client or Redis instance required'); + } + + $this->redisClient = $redis; + $this->redisKey = $key; + $this->capSize = $capSize; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritDoc} + */ + protected function write(array $record) + { + if ($this->capSize) { + $this->writeCapped($record); + } else { + $this->redisClient->rpush($this->redisKey, $record["formatted"]); + } + } + + /** + * Write and cap the collection + * Writes the record to the redis list and caps its + * + * @param array $record associative record array + * @return void + */ + protected function writeCapped(array $record) + { + if ($this->redisClient instanceof \Redis) { + $this->redisClient->multi() + ->rpush($this->redisKey, $record["formatted"]) + ->ltrim($this->redisKey, -$this->capSize, -1) + ->exec(); + } else { + $redisKey = $this->redisKey; + $capSize = $this->capSize; + $this->redisClient->transaction(function ($tx) use ($record, $redisKey, $capSize) { + $tx->rpush($redisKey, $record["formatted"]); + $tx->ltrim($redisKey, -$capSize, -1); + }); + } + } + + /** + * {@inheritDoc} + */ + protected function getDefaultFormatter() + { + return new LineFormatter(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.php new file mode 100644 index 0000000..6c8a3e3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RollbarHandler.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 Monolog\Handler; + +use RollbarNotifier; +use Exception; +use Monolog\Logger; + +/** + * Sends errors to Rollbar + * + * If the context data contains a `payload` key, that is used as an array + * of payload options to RollbarNotifier's report_message/report_exception methods. + * + * Rollbar's context info will contain the context + extra keys from the log record + * merged, and then on top of that a few keys: + * + * - level (rollbar level name) + * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) + * - channel + * - datetime (unix timestamp) + * + * @author Paul Statezny + */ +class RollbarHandler extends AbstractProcessingHandler +{ + /** + * Rollbar notifier + * + * @var RollbarNotifier + */ + protected $rollbarNotifier; + + protected $levelMap = array( + Logger::DEBUG => 'debug', + Logger::INFO => 'info', + Logger::NOTICE => 'info', + Logger::WARNING => 'warning', + Logger::ERROR => 'error', + Logger::CRITICAL => 'critical', + Logger::ALERT => 'critical', + Logger::EMERGENCY => 'critical', + ); + + /** + * Records whether any log records have been added since the last flush of the rollbar notifier + * + * @var bool + */ + private $hasRecords = false; + + protected $initialized = false; + + /** + * @param RollbarNotifier $rollbarNotifier RollbarNotifier object constructed with valid token + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(RollbarNotifier $rollbarNotifier, $level = Logger::ERROR, $bubble = true) + { + $this->rollbarNotifier = $rollbarNotifier; + + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (!$this->initialized) { + // __destructor() doesn't get called on Fatal errors + register_shutdown_function(array($this, 'close')); + $this->initialized = true; + } + + $context = $record['context']; + $payload = array(); + if (isset($context['payload'])) { + $payload = $context['payload']; + unset($context['payload']); + } + $context = array_merge($context, $record['extra'], array( + 'level' => $this->levelMap[$record['level']], + 'monolog_level' => $record['level_name'], + 'channel' => $record['channel'], + 'datetime' => $record['datetime']->format('U'), + )); + + if (isset($context['exception']) && $context['exception'] instanceof Exception) { + $payload['level'] = $context['level']; + $exception = $context['exception']; + unset($context['exception']); + + $this->rollbarNotifier->report_exception($exception, $context, $payload); + } else { + $this->rollbarNotifier->report_message( + $record['message'], + $context['level'], + $context, + $payload + ); + } + + $this->hasRecords = true; + } + + public function flush() + { + if ($this->hasRecords) { + $this->rollbarNotifier->flush(); + $this->hasRecords = false; + } + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->flush(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php new file mode 100644 index 0000000..3b60b3d --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/RotatingFileHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores logs to files that are rotated every day and a limited number of files are kept. + * + * This rotation is only intended to be used as a workaround. Using logrotate to + * handle the rotation is strongly encouraged when you can use it. + * + * @author Christophe Coevoet + * @author Jordi Boggiano + */ +class RotatingFileHandler extends StreamHandler +{ + const FILE_PER_DAY = 'Y-m-d'; + const FILE_PER_MONTH = 'Y-m'; + const FILE_PER_YEAR = 'Y'; + + protected $filename; + protected $maxFiles; + protected $mustRotate; + protected $nextRotation; + protected $filenameFormat; + protected $dateFormat; + + /** + * @param string $filename + * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param Boolean $useLocking Try to lock log file before doing any writes + */ + public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) + { + $this->filename = $filename; + $this->maxFiles = (int) $maxFiles; + $this->nextRotation = new \DateTime('tomorrow'); + $this->filenameFormat = '{filename}-{date}'; + $this->dateFormat = 'Y-m-d'; + + parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); + } + + /** + * {@inheritdoc} + */ + public function close() + { + parent::close(); + + if (true === $this->mustRotate) { + $this->rotate(); + } + } + + public function setFilenameFormat($filenameFormat, $dateFormat) + { + if (!preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { + trigger_error( + 'Invalid date format - format must be one of '. + 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") '. + 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the '. + 'date formats using slashes, underscores and/or dots instead of dashes.', + E_USER_DEPRECATED + ); + } + if (substr_count($filenameFormat, '{date}') === 0) { + trigger_error( + 'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.', + E_USER_DEPRECATED + ); + } + $this->filenameFormat = $filenameFormat; + $this->dateFormat = $dateFormat; + $this->url = $this->getTimedFilename(); + $this->close(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + // on the first record written, if the log is new, we should rotate (once per day) + if (null === $this->mustRotate) { + $this->mustRotate = !file_exists($this->url); + } + + if ($this->nextRotation < $record['datetime']) { + $this->mustRotate = true; + $this->close(); + } + + parent::write($record); + } + + /** + * Rotates the files. + */ + protected function rotate() + { + // update filename + $this->url = $this->getTimedFilename(); + $this->nextRotation = new \DateTime('tomorrow'); + + // skip GC of old logs if files are unlimited + if (0 === $this->maxFiles) { + return; + } + + $logFiles = glob($this->getGlobPattern()); + if ($this->maxFiles >= count($logFiles)) { + // no files to remove + return; + } + + // Sorting the files by name to remove the older ones + usort($logFiles, function ($a, $b) { + return strcmp($b, $a); + }); + + foreach (array_slice($logFiles, $this->maxFiles) as $file) { + if (is_writable($file)) { + // suppress errors here as unlink() might fail if two processes + // are cleaning up/rotating at the same time + set_error_handler(function ($errno, $errstr, $errfile, $errline) {}); + unlink($file); + restore_error_handler(); + } + } + + $this->mustRotate = false; + } + + protected function getTimedFilename() + { + $fileInfo = pathinfo($this->filename); + $timedFilename = str_replace( + array('{filename}', '{date}'), + array($fileInfo['filename'], date($this->dateFormat)), + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + + if (!empty($fileInfo['extension'])) { + $timedFilename .= '.'.$fileInfo['extension']; + } + + return $timedFilename; + } + + protected function getGlobPattern() + { + $fileInfo = pathinfo($this->filename); + $glob = str_replace( + array('{filename}', '{date}'), + array($fileInfo['filename'], '*'), + $fileInfo['dirname'] . '/' . $this->filenameFormat + ); + if (!empty($fileInfo['extension'])) { + $glob .= '.'.$fileInfo['extension']; + } + + return $glob; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.php new file mode 100644 index 0000000..9509ae3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SamplingHandler.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 Monolog\Handler; + +/** + * Sampling handler + * + * A sampled event stream can be useful for logging high frequency events in + * a production environment where you only need an idea of what is happening + * and are not concerned with capturing every occurrence. Since the decision to + * handle or not handle a particular event is determined randomly, the + * resulting sampled log is not guaranteed to contain 1/N of the events that + * occurred in the application, but based on the Law of large numbers, it will + * tend to be close to this ratio with a large number of attempts. + * + * @author Bryan Davis + * @author Kunal Mehta + */ +class SamplingHandler extends AbstractHandler +{ + /** + * @var callable|HandlerInterface $handler + */ + protected $handler; + + /** + * @var int $factor + */ + protected $factor; + + /** + * @param callable|HandlerInterface $handler Handler or factory callable($record, $fingersCrossedHandler). + * @param int $factor Sample factor + */ + public function __construct($handler, $factor) + { + parent::__construct(); + $this->handler = $handler; + $this->factor = $factor; + + if (!$this->handler instanceof HandlerInterface && !is_callable($this->handler)) { + throw new \RuntimeException("The given handler (".json_encode($this->handler).") is not a callable nor a Monolog\Handler\HandlerInterface object"); + } + } + + public function isHandling(array $record) + { + return $this->handler->isHandling($record); + } + + public function handle(array $record) + { + if ($this->isHandling($record) && mt_rand(1, $this->factor) === 1) { + // The same logic as in FingersCrossedHandler + if (!$this->handler instanceof HandlerInterface) { + $this->handler = call_user_func($this->handler, $record, $this); + if (!$this->handler instanceof HandlerInterface) { + throw new \RuntimeException("The factory callable should return a HandlerInterface"); + } + } + + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + $this->handler->handle($record); + } + + return false === $this->bubble; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php new file mode 100644 index 0000000..38bc838 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php @@ -0,0 +1,294 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Slack; + +use Monolog\Logger; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Formatter\FormatterInterface; + +/** + * Slack record utility helping to log to Slack webhooks or API. + * + * @author Greg Kedzierski + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + * @see https://api.slack.com/docs/message-attachments + */ +class SlackRecord +{ + const COLOR_DANGER = 'danger'; + + const COLOR_WARNING = 'warning'; + + const COLOR_GOOD = 'good'; + + const COLOR_DEFAULT = '#e3e4e6'; + + /** + * Slack channel (encoded ID or name) + * @var string|null + */ + private $channel; + + /** + * Name of a bot + * @var string|null + */ + private $username; + + /** + * User icon e.g. 'ghost', 'http://example.com/user.png' + * @var string + */ + private $userIcon; + + /** + * Whether the message should be added to Slack as attachment (plain text otherwise) + * @var bool + */ + private $useAttachment; + + /** + * Whether the the context/extra messages added to Slack as attachments are in a short style + * @var bool + */ + private $useShortAttachment; + + /** + * Whether the attachment should include context and extra data + * @var bool + */ + private $includeContextAndExtra; + + /** + * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @var array + */ + private $excludeFields; + + /** + * @var FormatterInterface + */ + private $formatter; + + /** + * @var NormalizerFormatter + */ + private $normalizerFormatter; + + public function __construct($channel = null, $username = null, $useAttachment = true, $userIcon = null, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array(), FormatterInterface $formatter = null) + { + $this->channel = $channel; + $this->username = $username; + $this->userIcon = trim($userIcon, ':'); + $this->useAttachment = $useAttachment; + $this->useShortAttachment = $useShortAttachment; + $this->includeContextAndExtra = $includeContextAndExtra; + $this->excludeFields = $excludeFields; + $this->formatter = $formatter; + + if ($this->includeContextAndExtra) { + $this->normalizerFormatter = new NormalizerFormatter(); + } + } + + public function getSlackData(array $record) + { + $dataArray = array(); + $record = $this->excludeFields($record); + + if ($this->username) { + $dataArray['username'] = $this->username; + } + + if ($this->channel) { + $dataArray['channel'] = $this->channel; + } + + if ($this->formatter && !$this->useAttachment) { + $message = $this->formatter->format($record); + } else { + $message = $record['message']; + } + + if ($this->useAttachment) { + $attachment = array( + 'fallback' => $message, + 'text' => $message, + 'color' => $this->getAttachmentColor($record['level']), + 'fields' => array(), + 'mrkdwn_in' => array('fields'), + 'ts' => $record['datetime']->getTimestamp() + ); + + if ($this->useShortAttachment) { + $attachment['title'] = $record['level_name']; + } else { + $attachment['title'] = 'Message'; + $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); + } + + + if ($this->includeContextAndExtra) { + foreach (array('extra', 'context') as $key) { + if (empty($record[$key])) { + continue; + } + + if ($this->useShortAttachment) { + $attachment['fields'][] = $this->generateAttachmentField( + ucfirst($key), + $record[$key] + ); + } else { + // Add all extra fields as individual fields in attachment + $attachment['fields'] = array_merge( + $attachment['fields'], + $this->generateAttachmentFields($record[$key]) + ); + } + } + } + + $dataArray['attachments'] = array($attachment); + } else { + $dataArray['text'] = $message; + } + + if ($this->userIcon) { + if (filter_var($this->userIcon, FILTER_VALIDATE_URL)) { + $dataArray['icon_url'] = $this->userIcon; + } else { + $dataArray['icon_emoji'] = ":{$this->userIcon}:"; + } + } + + return $dataArray; + } + + /** + * Returned a Slack message attachment color associated with + * provided level. + * + * @param int $level + * @return string + */ + public function getAttachmentColor($level) + { + switch (true) { + case $level >= Logger::ERROR: + return self::COLOR_DANGER; + case $level >= Logger::WARNING: + return self::COLOR_WARNING; + case $level >= Logger::INFO: + return self::COLOR_GOOD; + default: + return self::COLOR_DEFAULT; + } + } + + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param array $fields + * + * @return string + */ + public function stringify($fields) + { + $normalized = $this->normalizerFormatter->format($fields); + $prettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128; + + $hasSecondDimension = count(array_filter($normalized, 'is_array')); + $hasNonNumericKeys = !count(array_filter(array_keys($normalized), 'is_numeric')); + + return $hasSecondDimension || $hasNonNumericKeys + ? json_encode($normalized, $prettyPrintFlag) + : json_encode($normalized); + } + + /** + * Sets the formatter + * + * @param FormatterInterface $formatter + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * Generates attachment field + * + * @param string $title + * @param string|array $value\ + * + * @return array + */ + private function generateAttachmentField($title, $value) + { + $value = is_array($value) + ? sprintf('```%s```', $this->stringify($value)) + : $value; + + return array( + 'title' => $title, + 'value' => $value, + 'short' => false + ); + } + + /** + * Generates a collection of attachment fields from array + * + * @param array $data + * + * @return array + */ + private function generateAttachmentFields(array $data) + { + $fields = array(); + foreach ($data as $key => $value) { + $fields[] = $this->generateAttachmentField($key, $value); + } + + return $fields; + } + + /** + * Get a copy of record with fields excluded according to $this->excludeFields + * + * @param array $record + * + * @return array + */ + private function excludeFields(array $record) + { + foreach ($this->excludeFields as $field) { + $keys = explode('.', $field); + $node = &$record; + $lastKey = end($keys); + foreach ($keys as $key) { + if (!isset($node[$key])) { + break; + } + if ($lastKey === $key) { + unset($node[$key]); + break; + } + $node = &$node[$key]; + } + } + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.php new file mode 100644 index 0000000..3ac4d83 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackHandler.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 Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack API + * + * @author Greg Kedzierski + * @see https://api.slack.com/ + */ +class SlackHandler extends SocketHandler +{ + /** + * Slack API token + * @var string + */ + private $token; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $token Slack API token + * @param string $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + * @throws MissingExtensionException If no OpenSSL PHP extension configured + */ + public function __construct($token, $channel, $username = null, $useAttachment = true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = true, $useShortAttachment = false, $includeContextAndExtra = false, array $excludeFields = array()) + { + if (!extension_loaded('openssl')) { + throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); + } + + parent::__construct('ssl://slack.com:443', $level, $bubble); + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields, + $this->formatter + ); + + $this->token = $token; + } + + public function getSlackRecord() + { + return $this->slackRecord; + } + + /** + * {@inheritdoc} + * + * @param array $record + * @return string + */ + protected function generateDataStream($record) + { + $content = $this->buildContent($record); + + return $this->buildHeader($content) . $content; + } + + /** + * Builds the body of API call + * + * @param array $record + * @return string + */ + private function buildContent($record) + { + $dataArray = $this->prepareContentData($record); + + return http_build_query($dataArray); + } + + /** + * Prepares content data + * + * @param array $record + * @return array + */ + protected function prepareContentData($record) + { + $dataArray = $this->slackRecord->getSlackData($record); + $dataArray['token'] = $this->token; + + if (!empty($dataArray['attachments'])) { + $dataArray['attachments'] = json_encode($dataArray['attachments']); + } + + return $dataArray; + } + + /** + * Builds the header of the API Call + * + * @param string $content + * @return string + */ + private function buildHeader($content) + { + $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; + $header .= "Host: slack.com\r\n"; + $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $header .= "Content-Length: " . strlen($content) . "\r\n"; + $header .= "\r\n"; + + return $header; + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + parent::write($record); + $this->finalizeWrite(); + } + + /** + * Finalizes the request by reading some bytes and then closing the socket + * + * If we do not read some but close the socket too early, slack sometimes + * drops the request entirely. + */ + protected function finalizeWrite() + { + $res = $this->getResource(); + if (is_resource($res)) { + @fread($res, 2048); + } + $this->closeSocket(); + } + + /** + * Returned a Slack message attachment color associated with + * provided level. + * + * @param int $level + * @return string + * @deprecated Use underlying SlackRecord instead + */ + protected function getAttachmentColor($level) + { + trigger_error( + 'SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.', + E_USER_DEPRECATED + ); + + return $this->slackRecord->getAttachmentColor($level); + } + + /** + * Stringifies an array of key/value pairs to be used in attachment fields + * + * @param array $fields + * @return string + * @deprecated Use underlying SlackRecord instead + */ + protected function stringify($fields) + { + trigger_error( + 'SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.', + E_USER_DEPRECATED + ); + + return $this->slackRecord->stringify($fields); + } + + public function setFormatter(FormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter() + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php new file mode 100644 index 0000000..9a1bbb4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.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 Monolog\Handler; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Monolog\Handler\Slack\SlackRecord; + +/** + * Sends notifications through Slack Webhooks + * + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + */ +class SlackWebhookHandler extends AbstractProcessingHandler +{ + /** + * Slack Webhook token + * @var string + */ + private $webhookUrl; + + /** + * Instance of the SlackRecord util class preparing data for Slack API. + * @var SlackRecord + */ + private $slackRecord; + + /** + * @param string $webhookUrl Slack Webhook URL + * @param string|null $channel Slack channel (encoded ID or name) + * @param string|null $username Name of a bot + * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) + * @param string|null $iconEmoji The emoji name to use (or null) + * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style + * @param bool $includeContextAndExtra Whether the attachment should include context and extra data + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + * @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] + */ + public function __construct($webhookUrl, $channel = null, $username = null, $useAttachment = true, $iconEmoji = null, $useShortAttachment = false, $includeContextAndExtra = false, $level = Logger::CRITICAL, $bubble = true, array $excludeFields = array()) + { + parent::__construct($level, $bubble); + + $this->webhookUrl = $webhookUrl; + + $this->slackRecord = new SlackRecord( + $channel, + $username, + $useAttachment, + $iconEmoji, + $useShortAttachment, + $includeContextAndExtra, + $excludeFields, + $this->formatter + ); + } + + public function getSlackRecord() + { + return $this->slackRecord; + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + $postData = $this->slackRecord->getSlackData($record); + $postString = json_encode($postData); + + $ch = curl_init(); + $options = array( + CURLOPT_URL => $this->webhookUrl, + CURLOPT_POST => true, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => array('Content-type: application/json'), + CURLOPT_POSTFIELDS => $postString + ); + if (defined('CURLOPT_SAFE_UPLOAD')) { + $options[CURLOPT_SAFE_UPLOAD] = true; + } + + curl_setopt_array($ch, $options); + + Curl\Util::execute($ch); + } + + public function setFormatter(FormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->slackRecord->setFormatter($formatter); + + return $this; + } + + public function getFormatter() + { + $formatter = parent::getFormatter(); + $this->slackRecord->setFormatter($formatter); + + return $formatter; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SlackbotHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SlackbotHandler.php new file mode 100644 index 0000000..baead52 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SlackbotHandler.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Sends notifications through Slack's Slackbot + * + * @author Haralan Dobrev + * @see https://slack.com/apps/A0F81R8ET-slackbot + */ +class SlackbotHandler extends AbstractProcessingHandler +{ + /** + * The slug of the Slack team + * @var string + */ + private $slackTeam; + + /** + * Slackbot token + * @var string + */ + private $token; + + /** + * Slack channel name + * @var string + */ + private $channel; + + /** + * @param string $slackTeam Slack team slug + * @param string $token Slackbot token + * @param string $channel Slack channel (encoded ID or name) + * @param int $level The minimum logging level at which this handler will be triggered + * @param bool $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($slackTeam, $token, $channel, $level = Logger::CRITICAL, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->slackTeam = $slackTeam; + $this->token = $token; + $this->channel = $channel; + } + + /** + * {@inheritdoc} + * + * @param array $record + */ + protected function write(array $record) + { + $slackbotUrl = sprintf( + 'https://%s.slack.com/services/hooks/slackbot?token=%s&channel=%s', + $this->slackTeam, + $this->token, + $this->channel + ); + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $slackbotUrl); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $record['message']); + + Curl\Util::execute($ch); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php new file mode 100644 index 0000000..7a61bf4 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SocketHandler.php @@ -0,0 +1,346 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any socket - uses fsockopen() or pfsockopen(). + * + * @author Pablo de Leon Belloc + * @see http://php.net/manual/en/function.fsockopen.php + */ +class SocketHandler extends AbstractProcessingHandler +{ + private $connectionString; + private $connectionTimeout; + private $resource; + private $timeout = 0; + private $writingTimeout = 10; + private $lastSentBytes = null; + private $persistent = false; + private $errno; + private $errstr; + private $lastWritingAt; + + /** + * @param string $connectionString Socket connection string + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct($connectionString, $level = Logger::DEBUG, $bubble = true) + { + parent::__construct($level, $bubble); + $this->connectionString = $connectionString; + $this->connectionTimeout = (float) ini_get('default_socket_timeout'); + } + + /** + * Connect (if necessary) and write to the socket + * + * @param array $record + * + * @throws \UnexpectedValueException + * @throws \RuntimeException + */ + protected function write(array $record) + { + $this->connectIfNotConnected(); + $data = $this->generateDataStream($record); + $this->writeToSocket($data); + } + + /** + * We will not close a PersistentSocket instance so it can be reused in other requests. + */ + public function close() + { + if (!$this->isPersistent()) { + $this->closeSocket(); + } + } + + /** + * Close socket, if open + */ + public function closeSocket() + { + if (is_resource($this->resource)) { + fclose($this->resource); + $this->resource = null; + } + } + + /** + * Set socket connection to nbe persistent. It only has effect before the connection is initiated. + * + * @param bool $persistent + */ + public function setPersistent($persistent) + { + $this->persistent = (boolean) $persistent; + } + + /** + * Set connection timeout. Only has effect before we connect. + * + * @param float $seconds + * + * @see http://php.net/manual/en/function.fsockopen.php + */ + public function setConnectionTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->connectionTimeout = (float) $seconds; + } + + /** + * Set write timeout. Only has effect before we connect. + * + * @param float $seconds + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + public function setTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->timeout = (float) $seconds; + } + + /** + * Set writing timeout. Only has effect during connection in the writing cycle. + * + * @param float $seconds 0 for no timeout + */ + public function setWritingTimeout($seconds) + { + $this->validateTimeout($seconds); + $this->writingTimeout = (float) $seconds; + } + + /** + * Get current connection string + * + * @return string + */ + public function getConnectionString() + { + return $this->connectionString; + } + + /** + * Get persistent setting + * + * @return bool + */ + public function isPersistent() + { + return $this->persistent; + } + + /** + * Get current connection timeout setting + * + * @return float + */ + public function getConnectionTimeout() + { + return $this->connectionTimeout; + } + + /** + * Get current in-transfer timeout + * + * @return float + */ + public function getTimeout() + { + return $this->timeout; + } + + /** + * Get current local writing timeout + * + * @return float + */ + public function getWritingTimeout() + { + return $this->writingTimeout; + } + + /** + * Check to see if the socket is currently available. + * + * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. + * + * @return bool + */ + public function isConnected() + { + return is_resource($this->resource) + && !feof($this->resource); // on TCP - other party can close connection. + } + + /** + * Wrapper to allow mocking + */ + protected function pfsockopen() + { + return @pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + */ + protected function fsockopen() + { + return @fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); + } + + /** + * Wrapper to allow mocking + * + * @see http://php.net/manual/en/function.stream-set-timeout.php + */ + protected function streamSetTimeout() + { + $seconds = floor($this->timeout); + $microseconds = round(($this->timeout - $seconds) * 1e6); + + return stream_set_timeout($this->resource, $seconds, $microseconds); + } + + /** + * Wrapper to allow mocking + */ + protected function fwrite($data) + { + return @fwrite($this->resource, $data); + } + + /** + * Wrapper to allow mocking + */ + protected function streamGetMetadata() + { + return stream_get_meta_data($this->resource); + } + + private function validateTimeout($value) + { + $ok = filter_var($value, FILTER_VALIDATE_FLOAT); + if ($ok === false || $value < 0) { + throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got $value)"); + } + } + + private function connectIfNotConnected() + { + if ($this->isConnected()) { + return; + } + $this->connect(); + } + + protected function generateDataStream($record) + { + return (string) $record['formatted']; + } + + /** + * @return resource|null + */ + protected function getResource() + { + return $this->resource; + } + + private function connect() + { + $this->createSocketResource(); + $this->setSocketTimeout(); + } + + private function createSocketResource() + { + if ($this->isPersistent()) { + $resource = $this->pfsockopen(); + } else { + $resource = $this->fsockopen(); + } + if (!$resource) { + throw new \UnexpectedValueException("Failed connecting to $this->connectionString ($this->errno: $this->errstr)"); + } + $this->resource = $resource; + } + + private function setSocketTimeout() + { + if (!$this->streamSetTimeout()) { + throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); + } + } + + private function writeToSocket($data) + { + $length = strlen($data); + $sent = 0; + $this->lastSentBytes = $sent; + while ($this->isConnected() && $sent < $length) { + if (0 == $sent) { + $chunk = $this->fwrite($data); + } else { + $chunk = $this->fwrite(substr($data, $sent)); + } + if ($chunk === false) { + throw new \RuntimeException("Could not write to socket"); + } + $sent += $chunk; + $socketInfo = $this->streamGetMetadata(); + if ($socketInfo['timed_out']) { + throw new \RuntimeException("Write timed-out"); + } + + if ($this->writingIsTimedOut($sent)) { + throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)"); + } + } + if (!$this->isConnected() && $sent < $length) { + throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)"); + } + } + + private function writingIsTimedOut($sent) + { + $writingTimeout = (int) floor($this->writingTimeout); + if (0 === $writingTimeout) { + return false; + } + + if ($sent !== $this->lastSentBytes) { + $this->lastWritingAt = time(); + $this->lastSentBytes = $sent; + + return false; + } else { + usleep(100); + } + + if ((time() - $this->lastWritingAt) >= $writingTimeout) { + $this->closeSocket(); + + return true; + } + + return false; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.php new file mode 100644 index 0000000..09a1573 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/StreamHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Stores to any stream resource + * + * Can be used to store into php://stderr, remote and local files, etc. + * + * @author Jordi Boggiano + */ +class StreamHandler extends AbstractProcessingHandler +{ + protected $stream; + protected $url; + private $errorMessage; + protected $filePermission; + protected $useLocking; + private $dirCreated; + + /** + * @param resource|string $stream + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) + * @param Boolean $useLocking Try to lock log file before doing any writes + * + * @throws \Exception If a missing directory is not buildable + * @throws \InvalidArgumentException If stream is not a resource or string + */ + public function __construct($stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false) + { + parent::__construct($level, $bubble); + if (is_resource($stream)) { + $this->stream = $stream; + } elseif (is_string($stream)) { + $this->url = $stream; + } else { + throw new \InvalidArgumentException('A stream must either be a resource or a string.'); + } + + $this->filePermission = $filePermission; + $this->useLocking = $useLocking; + } + + /** + * {@inheritdoc} + */ + public function close() + { + if ($this->url && is_resource($this->stream)) { + fclose($this->stream); + } + $this->stream = null; + } + + /** + * Return the currently active stream if it is open + * + * @return resource|null + */ + public function getStream() + { + return $this->stream; + } + + /** + * Return the stream URL if it was configured with a URL and not an active resource + * + * @return string|null + */ + public function getUrl() + { + return $this->url; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (!is_resource($this->stream)) { + if (null === $this->url || '' === $this->url) { + throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); + } + $this->createDir(); + $this->errorMessage = null; + set_error_handler(array($this, 'customErrorHandler')); + $this->stream = fopen($this->url, 'a'); + if ($this->filePermission !== null) { + @chmod($this->url, $this->filePermission); + } + restore_error_handler(); + if (!is_resource($this->stream)) { + $this->stream = null; + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url)); + } + } + + if ($this->useLocking) { + // ignoring errors here, there's not much we can do about them + flock($this->stream, LOCK_EX); + } + + $this->streamWrite($this->stream, $record); + + if ($this->useLocking) { + flock($this->stream, LOCK_UN); + } + } + + /** + * Write to stream + * @param resource $stream + * @param array $record + */ + protected function streamWrite($stream, array $record) + { + fwrite($stream, (string) $record['formatted']); + } + + private function customErrorHandler($code, $msg) + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + } + + /** + * @param string $stream + * + * @return null|string + */ + private function getDirFromStream($stream) + { + $pos = strpos($stream, '://'); + if ($pos === false) { + return dirname($stream); + } + + if ('file://' === substr($stream, 0, 7)) { + return dirname(substr($stream, 7)); + } + + return; + } + + private function createDir() + { + // Do not try to create dir if it has already been tried. + if ($this->dirCreated) { + return; + } + + $dir = $this->getDirFromStream($this->url); + if (null !== $dir && !is_dir($dir)) { + $this->errorMessage = null; + set_error_handler(array($this, 'customErrorHandler')); + $status = mkdir($dir, 0777, true); + restore_error_handler(); + if (false === $status) { + throw new \UnexpectedValueException(sprintf('There is no existing directory at "%s" and its not buildable: '.$this->errorMessage, $dir)); + } + } + $this->dirCreated = true; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..72f44a5 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; +use Swift; + +/** + * SwiftMailerHandler uses Swift_Mailer to send the emails + * + * @author Gyula Sallai + */ +class SwiftMailerHandler extends MailHandler +{ + protected $mailer; + private $messageTemplate; + + /** + * @param \Swift_Mailer $mailer The mailer to use + * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + */ + public function __construct(\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = true) + { + parent::__construct($level, $bubble); + + $this->mailer = $mailer; + $this->messageTemplate = $message; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + $this->mailer->send($this->buildMessage($content, $records)); + } + + /** + * Creates instance of Swift_Message to be sent + * + * @param string $content formatted email body to be sent + * @param array $records Log records that formed the content + * @return \Swift_Message + */ + protected function buildMessage($content, array $records) + { + $message = null; + if ($this->messageTemplate instanceof \Swift_Message) { + $message = clone $this->messageTemplate; + $message->generateId(); + } elseif (is_callable($this->messageTemplate)) { + $message = call_user_func($this->messageTemplate, $content, $records); + } + + if (!$message instanceof \Swift_Message) { + throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it'); + } + + if ($records) { + $subjectFormatter = new LineFormatter($message->getSubject()); + $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); + } + + $message->setBody($content); + if (version_compare(Swift::VERSION, '6.0.0', '>=')) { + $message->setDate(new \DateTimeImmutable()); + } else { + $message->setDate(time()); + } + + return $message; + } + + /** + * BC getter, to be removed in 2.0 + */ + public function __get($name) + { + if ($name === 'message') { + trigger_error('SwiftMailerHandler->message is deprecated, use ->buildMessage() instead to retrieve the message', E_USER_DEPRECATED); + + return $this->buildMessage(null, array()); + } + + throw new \InvalidArgumentException('Invalid property '.$name); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.php new file mode 100644 index 0000000..376bc3b --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogHandler.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 Monolog\Handler; + +use Monolog\Logger; + +/** + * Logs to syslog service. + * + * usage example: + * + * $log = new Logger('application'); + * $syslog = new SyslogHandler('myfacility', 'local6'); + * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); + * $syslog->setFormatter($formatter); + * $log->pushHandler($syslog); + * + * @author Sven Paulus + */ +class SyslogHandler extends AbstractSyslogHandler +{ + protected $ident; + protected $logopts; + + /** + * @param string $ident + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID + */ + public function __construct($ident, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $logopts = LOG_PID) + { + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + $this->logopts = $logopts; + } + + /** + * {@inheritdoc} + */ + public function close() + { + closelog(); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + if (!openlog($this->ident, $this->logopts, $this->facility)) { + throw new \LogicException('Can\'t open syslog for ident "'.$this->ident.'" and facility "'.$this->facility.'"'); + } + syslog($this->logLevels[$record['level']], (string) $record['formatted']); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php new file mode 100644 index 0000000..3bff085 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.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 Monolog\Handler\SyslogUdp; + +class UdpSocket +{ + const DATAGRAM_MAX_LENGTH = 65023; + + protected $ip; + protected $port; + protected $socket; + + public function __construct($ip, $port = 514) + { + $this->ip = $ip; + $this->port = $port; + $this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); + } + + public function write($line, $header = "") + { + $this->send($this->assembleMessage($line, $header)); + } + + public function close() + { + if (is_resource($this->socket)) { + socket_close($this->socket); + $this->socket = null; + } + } + + protected function send($chunk) + { + if (!is_resource($this->socket)) { + throw new \LogicException('The UdpSocket to '.$this->ip.':'.$this->port.' has been closed and can not be written to anymore'); + } + socket_sendto($this->socket, $chunk, strlen($chunk), $flags = 0, $this->ip, $this->port); + } + + protected function assembleMessage($line, $header) + { + $chunkSize = self::DATAGRAM_MAX_LENGTH - strlen($header); + + return $header . substr($line, 0, $chunkSize); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php new file mode 100644 index 0000000..4718711 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\Handler\SyslogUdp\UdpSocket; + +/** + * A Handler for logging to a remote syslogd server. + * + * @author Jesper Skovgaard Nielsen + */ +class SyslogUdpHandler extends AbstractSyslogHandler +{ + protected $socket; + protected $ident; + + /** + * @param string $host + * @param int $port + * @param mixed $facility + * @param int $level The minimum logging level at which this handler will be triggered + * @param Boolean $bubble Whether the messages that are handled can bubble up the stack or not + * @param string $ident Program name or tag for each log message. + */ + public function __construct($host, $port = 514, $facility = LOG_USER, $level = Logger::DEBUG, $bubble = true, $ident = 'php') + { + parent::__construct($facility, $level, $bubble); + + $this->ident = $ident; + + $this->socket = new UdpSocket($host, $port ?: 514); + } + + protected function write(array $record) + { + $lines = $this->splitMessageIntoLines($record['formatted']); + + $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']]); + + foreach ($lines as $line) { + $this->socket->write($line, $header); + } + } + + public function close() + { + $this->socket->close(); + } + + private function splitMessageIntoLines($message) + { + if (is_array($message)) { + $message = implode("\n", $message); + } + + return preg_split('/$\R?^/m', $message, -1, PREG_SPLIT_NO_EMPTY); + } + + /** + * Make common syslog header (see rfc5424) + */ + protected function makeCommonSyslogHeader($severity) + { + $priority = $severity + $this->facility; + + if (!$pid = getmypid()) { + $pid = '-'; + } + + if (!$hostname = gethostname()) { + $hostname = '-'; + } + + return "<$priority>1 " . + $this->getDateTime() . " " . + $hostname . " " . + $this->ident . " " . + $pid . " - - "; + } + + protected function getDateTime() + { + return date(\DateTime::RFC3339); + } + + /** + * Inject your own socket, mainly used for testing + */ + public function setSocket($socket) + { + $this->socket = $socket; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php new file mode 100644 index 0000000..e39cfc6 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/TestHandler.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +/** + * Used for testing purposes. + * + * It records all records and gives you access to them for verification. + * + * @author Jordi Boggiano + * + * @method bool hasEmergency($record) + * @method bool hasAlert($record) + * @method bool hasCritical($record) + * @method bool hasError($record) + * @method bool hasWarning($record) + * @method bool hasNotice($record) + * @method bool hasInfo($record) + * @method bool hasDebug($record) + * + * @method bool hasEmergencyRecords() + * @method bool hasAlertRecords() + * @method bool hasCriticalRecords() + * @method bool hasErrorRecords() + * @method bool hasWarningRecords() + * @method bool hasNoticeRecords() + * @method bool hasInfoRecords() + * @method bool hasDebugRecords() + * + * @method bool hasEmergencyThatContains($message) + * @method bool hasAlertThatContains($message) + * @method bool hasCriticalThatContains($message) + * @method bool hasErrorThatContains($message) + * @method bool hasWarningThatContains($message) + * @method bool hasNoticeThatContains($message) + * @method bool hasInfoThatContains($message) + * @method bool hasDebugThatContains($message) + * + * @method bool hasEmergencyThatMatches($message) + * @method bool hasAlertThatMatches($message) + * @method bool hasCriticalThatMatches($message) + * @method bool hasErrorThatMatches($message) + * @method bool hasWarningThatMatches($message) + * @method bool hasNoticeThatMatches($message) + * @method bool hasInfoThatMatches($message) + * @method bool hasDebugThatMatches($message) + * + * @method bool hasEmergencyThatPasses($message) + * @method bool hasAlertThatPasses($message) + * @method bool hasCriticalThatPasses($message) + * @method bool hasErrorThatPasses($message) + * @method bool hasWarningThatPasses($message) + * @method bool hasNoticeThatPasses($message) + * @method bool hasInfoThatPasses($message) + * @method bool hasDebugThatPasses($message) + */ +class TestHandler extends AbstractProcessingHandler +{ + protected $records = array(); + protected $recordsByLevel = array(); + + public function getRecords() + { + return $this->records; + } + + public function clear() + { + $this->records = array(); + $this->recordsByLevel = array(); + } + + public function hasRecords($level) + { + return isset($this->recordsByLevel[$level]); + } + + public function hasRecord($record, $level) + { + if (is_array($record)) { + $record = $record['message']; + } + + return $this->hasRecordThatPasses(function ($rec) use ($record) { + return $rec['message'] === $record; + }, $level); + } + + public function hasRecordThatContains($message, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($message) { + return strpos($rec['message'], $message) !== false; + }, $level); + } + + public function hasRecordThatMatches($regex, $level) + { + return $this->hasRecordThatPasses(function ($rec) use ($regex) { + return preg_match($regex, $rec['message']) > 0; + }, $level); + } + + public function hasRecordThatPasses($predicate, $level) + { + if (!is_callable($predicate)) { + throw new \InvalidArgumentException("Expected a callable for hasRecordThatSucceeds"); + } + + if (!isset($this->recordsByLevel[$level])) { + return false; + } + + foreach ($this->recordsByLevel[$level] as $i => $rec) { + if (call_user_func($predicate, $rec, $i)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->recordsByLevel[$record['level']][] = $record; + $this->records[] = $record; + } + + public function __call($method, $args) + { + if (preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { + $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; + $level = constant('Monolog\Logger::' . strtoupper($matches[2])); + if (method_exists($this, $genericMethod)) { + $args[] = $level; + + return call_user_func_array(array($this, $genericMethod), $args); + } + } + + throw new \BadMethodCallException('Call to undefined method ' . get_class($this) . '::' . $method . '()'); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php new file mode 100644 index 0000000..2732ba3 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.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 Monolog\Handler; + +/** + * Forwards records to multiple handlers suppressing failures of each handler + * and continuing through to give every handler a chance to succeed. + * + * @author Craig D'Amelio + */ +class WhatFailureGroupHandler extends GroupHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + } + + foreach ($this->handlers as $handler) { + try { + $handler->handle($record); + } catch (\Exception $e) { + // What failure? + } catch (\Throwable $e) { + // What failure? + } + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + public function handleBatch(array $records) + { + foreach ($this->handlers as $handler) { + try { + $handler->handleBatch($records); + } catch (\Exception $e) { + // What failure? + } catch (\Throwable $e) { + // What failure? + } + } + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php b/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php new file mode 100644 index 0000000..f22cf21 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.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 Monolog\Handler; + +use Monolog\Formatter\NormalizerFormatter; +use Monolog\Logger; + +/** + * Handler sending logs to Zend Monitor + * + * @author Christian Bergau + */ +class ZendMonitorHandler extends AbstractProcessingHandler +{ + /** + * Monolog level / ZendMonitor Custom Event priority map + * + * @var array + */ + protected $levelMap = array( + Logger::DEBUG => 1, + Logger::INFO => 2, + Logger::NOTICE => 3, + Logger::WARNING => 4, + Logger::ERROR => 5, + Logger::CRITICAL => 6, + Logger::ALERT => 7, + Logger::EMERGENCY => 0, + ); + + /** + * Construct + * + * @param int $level + * @param bool $bubble + * @throws MissingExtensionException + */ + public function __construct($level = Logger::DEBUG, $bubble = true) + { + if (!function_exists('zend_monitor_custom_event')) { + throw new MissingExtensionException('You must have Zend Server installed in order to use this handler'); + } + parent::__construct($level, $bubble); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + $this->writeZendMonitorCustomEvent( + $this->levelMap[$record['level']], + $record['message'], + $record['formatted'] + ); + } + + /** + * Write a record to Zend Monitor + * + * @param int $level + * @param string $message + * @param array $formatted + */ + protected function writeZendMonitorCustomEvent($level, $message, $formatted) + { + zend_monitor_custom_event($level, $message, $formatted); + } + + /** + * {@inheritdoc} + */ + public function getDefaultFormatter() + { + return new NormalizerFormatter(); + } + + /** + * Get the level map + * + * @return array + */ + public function getLevelMap() + { + return $this->levelMap; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Logger.php b/vendor/monolog/monolog/src/Monolog/Logger.php new file mode 100644 index 0000000..49d00af --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Logger.php @@ -0,0 +1,700 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog; + +use Monolog\Handler\HandlerInterface; +use Monolog\Handler\StreamHandler; +use Psr\Log\LoggerInterface; +use Psr\Log\InvalidArgumentException; + +/** + * Monolog log channel + * + * It contains a stack of Handlers and a stack of Processors, + * and uses them to store records that are added to it. + * + * @author Jordi Boggiano + */ +class Logger implements LoggerInterface +{ + /** + * Detailed debug information + */ + const DEBUG = 100; + + /** + * Interesting events + * + * Examples: User logs in, SQL logs. + */ + const INFO = 200; + + /** + * Uncommon events + */ + const NOTICE = 250; + + /** + * Exceptional occurrences that are not errors + * + * Examples: Use of deprecated APIs, poor use of an API, + * undesirable things that are not necessarily wrong. + */ + const WARNING = 300; + + /** + * Runtime errors + */ + const ERROR = 400; + + /** + * Critical conditions + * + * Example: Application component unavailable, unexpected exception. + */ + const CRITICAL = 500; + + /** + * Action must be taken immediately + * + * Example: Entire website down, database unavailable, etc. + * This should trigger the SMS alerts and wake you up. + */ + const ALERT = 550; + + /** + * Urgent alert. + */ + const EMERGENCY = 600; + + /** + * Monolog API version + * + * This is only bumped when API breaks are done and should + * follow the major version of the library + * + * @var int + */ + const API = 1; + + /** + * Logging levels from syslog protocol defined in RFC 5424 + * + * @var array $levels Logging levels + */ + protected static $levels = array( + self::DEBUG => 'DEBUG', + self::INFO => 'INFO', + self::NOTICE => 'NOTICE', + self::WARNING => 'WARNING', + self::ERROR => 'ERROR', + self::CRITICAL => 'CRITICAL', + self::ALERT => 'ALERT', + self::EMERGENCY => 'EMERGENCY', + ); + + /** + * @var \DateTimeZone + */ + protected static $timezone; + + /** + * @var string + */ + protected $name; + + /** + * The handler stack + * + * @var HandlerInterface[] + */ + protected $handlers; + + /** + * Processors that will process all log records + * + * To process records of a single handler instead, add the processor on that specific handler + * + * @var callable[] + */ + protected $processors; + + /** + * @var bool + */ + protected $microsecondTimestamps = true; + + /** + * @param string $name The logging channel + * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. + * @param callable[] $processors Optional array of processors + */ + public function __construct($name, array $handlers = array(), array $processors = array()) + { + $this->name = $name; + $this->handlers = $handlers; + $this->processors = $processors; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Return a new cloned instance with the name changed + * + * @return static + */ + public function withName($name) + { + $new = clone $this; + $new->name = $name; + + return $new; + } + + /** + * Pushes a handler on to the stack. + * + * @param HandlerInterface $handler + * @return $this + */ + public function pushHandler(HandlerInterface $handler) + { + array_unshift($this->handlers, $handler); + + return $this; + } + + /** + * Pops a handler from the stack + * + * @return HandlerInterface + */ + public function popHandler() + { + if (!$this->handlers) { + throw new \LogicException('You tried to pop from an empty handler stack.'); + } + + return array_shift($this->handlers); + } + + /** + * Set handlers, replacing all existing ones. + * + * If a map is passed, keys will be ignored. + * + * @param HandlerInterface[] $handlers + * @return $this + */ + public function setHandlers(array $handlers) + { + $this->handlers = array(); + foreach (array_reverse($handlers) as $handler) { + $this->pushHandler($handler); + } + + return $this; + } + + /** + * @return HandlerInterface[] + */ + public function getHandlers() + { + return $this->handlers; + } + + /** + * Adds a processor on to the stack. + * + * @param callable $callback + * @return $this + */ + public function pushProcessor($callback) + { + if (!is_callable($callback)) { + throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given'); + } + array_unshift($this->processors, $callback); + + return $this; + } + + /** + * Removes the processor on top of the stack and returns it. + * + * @return callable + */ + public function popProcessor() + { + if (!$this->processors) { + throw new \LogicException('You tried to pop from an empty processor stack.'); + } + + return array_shift($this->processors); + } + + /** + * @return callable[] + */ + public function getProcessors() + { + return $this->processors; + } + + /** + * Control the use of microsecond resolution timestamps in the 'datetime' + * member of new records. + * + * Generating microsecond resolution timestamps by calling + * microtime(true), formatting the result via sprintf() and then parsing + * the resulting string via \DateTime::createFromFormat() can incur + * a measurable runtime overhead vs simple usage of DateTime to capture + * a second resolution timestamp in systems which generate a large number + * of log events. + * + * @param bool $micro True to use microtime() to create timestamps + */ + public function useMicrosecondTimestamps($micro) + { + $this->microsecondTimestamps = (bool) $micro; + } + + /** + * Adds a log record. + * + * @param int $level The logging level + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addRecord($level, $message, array $context = array()) + { + if (!$this->handlers) { + $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG)); + } + + $levelName = static::getLevelName($level); + + // check if any handler will handle this message so we can return early and save cycles + $handlerKey = null; + reset($this->handlers); + while ($handler = current($this->handlers)) { + if ($handler->isHandling(array('level' => $level))) { + $handlerKey = key($this->handlers); + break; + } + + next($this->handlers); + } + + if (null === $handlerKey) { + return false; + } + + if (!static::$timezone) { + static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC'); + } + + // php7.1+ always has microseconds enabled, so we do not need this hack + if ($this->microsecondTimestamps && PHP_VERSION_ID < 70100) { + $ts = \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone); + } else { + $ts = new \DateTime(null, static::$timezone); + } + $ts->setTimezone(static::$timezone); + + $record = array( + 'message' => (string) $message, + 'context' => $context, + 'level' => $level, + 'level_name' => $levelName, + 'channel' => $this->name, + 'datetime' => $ts, + 'extra' => array(), + ); + + foreach ($this->processors as $processor) { + $record = call_user_func($processor, $record); + } + + while ($handler = current($this->handlers)) { + if (true === $handler->handle($record)) { + break; + } + + next($this->handlers); + } + + return true; + } + + /** + * Adds a log record at the DEBUG level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addDebug($message, array $context = array()) + { + return $this->addRecord(static::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addInfo($message, array $context = array()) + { + return $this->addRecord(static::INFO, $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addNotice($message, array $context = array()) + { + return $this->addRecord(static::NOTICE, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addWarning($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addError($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addCritical($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addAlert($message, array $context = array()) + { + return $this->addRecord(static::ALERT, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function addEmergency($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + + /** + * Gets all supported logging levels. + * + * @return array Assoc array with human-readable level names => level codes. + */ + public static function getLevels() + { + return array_flip(static::$levels); + } + + /** + * Gets the name of the logging level. + * + * @param int $level + * @return string + */ + public static function getLevelName($level) + { + if (!isset(static::$levels[$level])) { + throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels))); + } + + return static::$levels[$level]; + } + + /** + * Converts PSR-3 levels to Monolog ones if necessary + * + * @param string|int Level number (monolog) or name (PSR-3) + * @return int + */ + public static function toMonologLevel($level) + { + if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) { + return constant(__CLASS__.'::'.strtoupper($level)); + } + + return $level; + } + + /** + * Checks whether the Logger has a handler that listens on the given level + * + * @param int $level + * @return Boolean + */ + public function isHandling($level) + { + $record = array( + 'level' => $level, + ); + + foreach ($this->handlers as $handler) { + if ($handler->isHandling($record)) { + return true; + } + } + + return false; + } + + /** + * Adds a log record at an arbitrary level. + * + * This method allows for compatibility with common interfaces. + * + * @param mixed $level The log level + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function log($level, $message, array $context = array()) + { + $level = static::toMonologLevel($level); + + return $this->addRecord($level, $message, $context); + } + + /** + * Adds a log record at the DEBUG level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function debug($message, array $context = array()) + { + return $this->addRecord(static::DEBUG, $message, $context); + } + + /** + * Adds a log record at the INFO level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function info($message, array $context = array()) + { + return $this->addRecord(static::INFO, $message, $context); + } + + /** + * Adds a log record at the NOTICE level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function notice($message, array $context = array()) + { + return $this->addRecord(static::NOTICE, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function warn($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + + /** + * Adds a log record at the WARNING level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function warning($message, array $context = array()) + { + return $this->addRecord(static::WARNING, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function err($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + + /** + * Adds a log record at the ERROR level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function error($message, array $context = array()) + { + return $this->addRecord(static::ERROR, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function crit($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the CRITICAL level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function critical($message, array $context = array()) + { + return $this->addRecord(static::CRITICAL, $message, $context); + } + + /** + * Adds a log record at the ALERT level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function alert($message, array $context = array()) + { + return $this->addRecord(static::ALERT, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function emerg($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + + /** + * Adds a log record at the EMERGENCY level. + * + * This method allows for compatibility with common interfaces. + * + * @param string $message The log message + * @param array $context The log context + * @return Boolean Whether the record has been processed + */ + public function emergency($message, array $context = array()) + { + return $this->addRecord(static::EMERGENCY, $message, $context); + } + + /** + * Set the timezone to be used for the timestamp of log records. + * + * This is stored globally for all Logger instances + * + * @param \DateTimeZone $tz Timezone object + */ + public static function setTimezone(\DateTimeZone $tz) + { + self::$timezone = $tz; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.php new file mode 100644 index 0000000..1899400 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/GitProcessor.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 Monolog\Processor; + +use Monolog\Logger; + +/** + * Injects Git branch and Git commit SHA in all records + * + * @author Nick Otter + * @author Jordi Boggiano + */ +class GitProcessor +{ + private $level; + private static $cache; + + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['git'] = self::getGitInfo(); + + return $record; + } + + private static function getGitInfo() + { + if (self::$cache) { + return self::$cache; + } + + $branches = `git branch -v --no-abbrev`; + if (preg_match('{^\* (.+?)\s+([a-f0-9]{40})(?:\s|$)}m', $branches, $matches)) { + return self::$cache = array( + 'branch' => $matches[1], + 'commit' => $matches[2], + ); + } + + return self::$cache = array(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php new file mode 100644 index 0000000..2c07cae --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.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 Monolog\Processor; + +use Monolog\Logger; + +/** + * Injects line/file:class/function where the log message came from + * + * Warning: This only works if the handler processes the logs directly. + * If you put the processor on a handler that is behind a FingersCrossedHandler + * for example, the processor will only be called once the trigger level is reached, + * and all the log records will have the same file/line/.. data from the call that + * triggered the FingersCrossedHandler. + * + * @author Jordi Boggiano + */ +class IntrospectionProcessor +{ + private $level; + + private $skipClassesPartials; + + private $skipStackFramesCount; + + private $skipFunctions = array( + 'call_user_func', + 'call_user_func_array', + ); + + public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array(), $skipStackFramesCount = 0) + { + $this->level = Logger::toMonologLevel($level); + $this->skipClassesPartials = array_merge(array('Monolog\\'), $skipClassesPartials); + $this->skipStackFramesCount = $skipStackFramesCount; + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + /* + * http://php.net/manual/en/function.debug-backtrace.php + * As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added. + * Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'. + */ + $trace = debug_backtrace((PHP_VERSION_ID < 50306) ? 2 : DEBUG_BACKTRACE_IGNORE_ARGS); + + // skip first since it's always the current method + array_shift($trace); + // the call_user_func call is also skipped + array_shift($trace); + + $i = 0; + + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + if (isset($trace[$i]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (strpos($trace[$i]['class'], $part) !== false) { + $i++; + continue 2; + } + } + } elseif (in_array($trace[$i]['function'], $this->skipFunctions)) { + $i++; + continue; + } + + break; + } + + $i += $this->skipStackFramesCount; + + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + array( + 'file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, + 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, + 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ) + ); + + return $record; + } + + private function isTraceClassOrSkippedFunction(array $trace, $index) + { + if (!isset($trace[$index])) { + return false; + } + + return isset($trace[$index]['class']) || in_array($trace[$index]['function'], $this->skipFunctions); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php new file mode 100644 index 0000000..0543e92 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.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 Monolog\Processor; + +/** + * Injects memory_get_peak_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryPeakUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_peak_usage($this->realUsage); + $formatted = $this->formatBytes($bytes); + + $record['extra']['memory_peak_usage'] = $formatted; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.php new file mode 100644 index 0000000..85f9dc5 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryProcessor.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 Monolog\Processor; + +/** + * Some methods that are common for all memory processors + * + * @author Rob Jensen + */ +abstract class MemoryProcessor +{ + /** + * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. + */ + protected $realUsage; + + /** + * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + protected $useFormatting; + + /** + * @param bool $realUsage Set this to true to get the real size of memory allocated from system. + * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) + */ + public function __construct($realUsage = true, $useFormatting = true) + { + $this->realUsage = (boolean) $realUsage; + $this->useFormatting = (boolean) $useFormatting; + } + + /** + * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is + * + * @param int $bytes + * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as is + */ + protected function formatBytes($bytes) + { + $bytes = (int) $bytes; + + if (!$this->useFormatting) { + return $bytes; + } + + if ($bytes > 1024 * 1024) { + return round($bytes / 1024 / 1024, 2).' MB'; + } elseif ($bytes > 1024) { + return round($bytes / 1024, 2).' KB'; + } + + return $bytes . ' B'; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php new file mode 100644 index 0000000..2783d65 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.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 Monolog\Processor; + +/** + * Injects memory_get_usage in all records + * + * @see Monolog\Processor\MemoryProcessor::__construct() for options + * @author Rob Jensen + */ +class MemoryUsageProcessor extends MemoryProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $bytes = memory_get_usage($this->realUsage); + $formatted = $this->formatBytes($bytes); + + $record['extra']['memory_usage'] = $formatted; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.php new file mode 100644 index 0000000..7c07a7e --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/MercurialProcessor.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 Monolog\Processor; + +use Monolog\Logger; + +/** + * Injects Hg branch and Hg revision number in all records + * + * @author Jonathan A. Schweder + */ +class MercurialProcessor +{ + private $level; + private static $cache; + + public function __construct($level = Logger::DEBUG) + { + $this->level = Logger::toMonologLevel($level); + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + + $record['extra']['hg'] = self::getMercurialInfo(); + + return $record; + } + + private static function getMercurialInfo() + { + if (self::$cache) { + return self::$cache; + } + + $result = explode(' ', trim(`hg id -nb`)); + if (count($result) >= 3) { + return self::$cache = array( + 'branch' => $result[1], + 'revision' => $result[2], + ); + } + + return self::$cache = array(); + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php new file mode 100644 index 0000000..9d3f559 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.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 Monolog\Processor; + +/** + * Adds value of getmypid into records + * + * @author Andreas Hörnicke + */ +class ProcessIdProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + $record['extra']['process_id'] = getmypid(); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php new file mode 100644 index 0000000..c2686ce --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.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 Monolog\Processor; + +/** + * Processes a record's message according to PSR-3 rules + * + * It replaces {foo} with the value from $context['foo'] + * + * @author Jordi Boggiano + */ +class PsrLogMessageProcessor +{ + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + if (false === strpos($record['message'], '{')) { + return $record; + } + + $replacements = array(); + foreach ($record['context'] as $key => $val) { + if (is_null($val) || is_scalar($val) || (is_object($val) && method_exists($val, "__toString"))) { + $replacements['{'.$key.'}'] = $val; + } elseif (is_object($val)) { + $replacements['{'.$key.'}'] = '[object '.get_class($val).']'; + } else { + $replacements['{'.$key.'}'] = '['.gettype($val).']'; + } + } + + $record['message'] = strtr($record['message'], $replacements); + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.php new file mode 100644 index 0000000..7e2df2a --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/TagProcessor.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 Monolog\Processor; + +/** + * Adds a tags array into record + * + * @author Martijn Riemers + */ +class TagProcessor +{ + private $tags; + + public function __construct(array $tags = array()) + { + $this->setTags($tags); + } + + public function addTags(array $tags = array()) + { + $this->tags = array_merge($this->tags, $tags); + } + + public function setTags(array $tags = array()) + { + $this->tags = $tags; + } + + public function __invoke(array $record) + { + $record['extra']['tags'] = $this->tags; + + return $record; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.php new file mode 100644 index 0000000..812707c --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/UidProcessor.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 Monolog\Processor; + +/** + * Adds a unique identifier into records + * + * @author Simon Mönch + */ +class UidProcessor +{ + private $uid; + + public function __construct($length = 7) + { + if (!is_int($length) || $length > 32 || $length < 1) { + throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); + } + + $this->uid = substr(hash('md5', uniqid('', true)), 0, $length); + } + + public function __invoke(array $record) + { + $record['extra']['uid'] = $this->uid; + + return $record; + } + + /** + * @return string + */ + public function getUid() + { + return $this->uid; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.php new file mode 100644 index 0000000..ea1d897 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Processor/WebProcessor.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 Monolog\Processor; + +/** + * Injects url/method and remote IP of the current web request in all records + * + * @author Jordi Boggiano + */ +class WebProcessor +{ + /** + * @var array|\ArrayAccess + */ + protected $serverData; + + /** + * Default fields + * + * Array is structured as [key in record.extra => key in $serverData] + * + * @var array + */ + protected $extraFields = array( + 'url' => 'REQUEST_URI', + 'ip' => 'REMOTE_ADDR', + 'http_method' => 'REQUEST_METHOD', + 'server' => 'SERVER_NAME', + 'referrer' => 'HTTP_REFERER', + ); + + /** + * @param array|\ArrayAccess $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data + * @param array|null $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer + */ + public function __construct($serverData = null, array $extraFields = null) + { + if (null === $serverData) { + $this->serverData = &$_SERVER; + } elseif (is_array($serverData) || $serverData instanceof \ArrayAccess) { + $this->serverData = $serverData; + } else { + throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); + } + + if (null !== $extraFields) { + if (isset($extraFields[0])) { + foreach (array_keys($this->extraFields) as $fieldName) { + if (!in_array($fieldName, $extraFields)) { + unset($this->extraFields[$fieldName]); + } + } + } else { + $this->extraFields = $extraFields; + } + } + } + + /** + * @param array $record + * @return array + */ + public function __invoke(array $record) + { + // skip processing if for some reason request data + // is not present (CLI or wonky SAPIs) + if (!isset($this->serverData['REQUEST_URI'])) { + return $record; + } + + $record['extra'] = $this->appendExtraFields($record['extra']); + + return $record; + } + + /** + * @param string $extraName + * @param string $serverName + * @return $this + */ + public function addExtraField($extraName, $serverName) + { + $this->extraFields[$extraName] = $serverName; + + return $this; + } + + /** + * @param array $extra + * @return array + */ + private function appendExtraFields(array $extra) + { + foreach ($this->extraFields as $extraName => $serverName) { + $extra[$extraName] = isset($this->serverData[$serverName]) ? $this->serverData[$serverName] : null; + } + + if (isset($this->serverData['UNIQUE_ID'])) { + $extra['unique_id'] = $this->serverData['UNIQUE_ID']; + } + + return $extra; + } +} diff --git a/vendor/monolog/monolog/src/Monolog/Registry.php b/vendor/monolog/monolog/src/Monolog/Registry.php new file mode 100644 index 0000000..159b751 --- /dev/null +++ b/vendor/monolog/monolog/src/Monolog/Registry.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 Monolog; + +use InvalidArgumentException; + +/** + * Monolog log registry + * + * Allows to get `Logger` instances in the global scope + * via static method calls on this class. + * + * + * $application = new Monolog\Logger('application'); + * $api = new Monolog\Logger('api'); + * + * Monolog\Registry::addLogger($application); + * Monolog\Registry::addLogger($api); + * + * function testLogger() + * { + * Monolog\Registry::api()->addError('Sent to $api Logger instance'); + * Monolog\Registry::application()->addError('Sent to $application Logger instance'); + * } + * + * + * @author Tomas Tatarko + */ +class Registry +{ + /** + * List of all loggers in the registry (by named indexes) + * + * @var Logger[] + */ + private static $loggers = array(); + + /** + * Adds new logging channel to the registry + * + * @param Logger $logger Instance of the logging channel + * @param string|null $name Name of the logging channel ($logger->getName() by default) + * @param bool $overwrite Overwrite instance in the registry if the given name already exists? + * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists + */ + public static function addLogger(Logger $logger, $name = null, $overwrite = false) + { + $name = $name ?: $logger->getName(); + + if (isset(self::$loggers[$name]) && !$overwrite) { + throw new InvalidArgumentException('Logger with the given name already exists'); + } + + self::$loggers[$name] = $logger; + } + + /** + * Checks if such logging channel exists by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function hasLogger($logger) + { + if ($logger instanceof Logger) { + $index = array_search($logger, self::$loggers, true); + + return false !== $index; + } else { + return isset(self::$loggers[$logger]); + } + } + + /** + * Removes instance from registry by name or instance + * + * @param string|Logger $logger Name or logger instance + */ + public static function removeLogger($logger) + { + if ($logger instanceof Logger) { + if (false !== ($idx = array_search($logger, self::$loggers, true))) { + unset(self::$loggers[$idx]); + } + } else { + unset(self::$loggers[$logger]); + } + } + + /** + * Clears the registry + */ + public static function clear() + { + self::$loggers = array(); + } + + /** + * Gets Logger instance from the registry + * + * @param string $name Name of the requested Logger instance + * @throws \InvalidArgumentException If named Logger instance is not in the registry + * @return Logger Requested instance of Logger + */ + public static function getInstance($name) + { + if (!isset(self::$loggers[$name])) { + throw new InvalidArgumentException(sprintf('Requested "%s" logger instance is not in the registry', $name)); + } + + return self::$loggers[$name]; + } + + /** + * Gets Logger instance from the registry via static method call + * + * @param string $name Name of the requested Logger instance + * @param array $arguments Arguments passed to static method call + * @throws \InvalidArgumentException If named Logger instance is not in the registry + * @return Logger Requested instance of Logger + */ + public static function __callStatic($name, $arguments) + { + return self::getInstance($name); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/ErrorHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/ErrorHandlerTest.php new file mode 100644 index 0000000..a9a3f30 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/ErrorHandlerTest.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 Monolog; + +use Monolog\Handler\TestHandler; + +class ErrorHandlerTest extends \PHPUnit_Framework_TestCase +{ + public function testHandleError() + { + $logger = new Logger('test', array($handler = new TestHandler)); + $errHandler = new ErrorHandler($logger); + + $errHandler->registerErrorHandler(array(E_USER_NOTICE => Logger::EMERGENCY), false); + trigger_error('Foo', E_USER_ERROR); + $this->assertCount(1, $handler->getRecords()); + $this->assertTrue($handler->hasErrorRecords()); + trigger_error('Foo', E_USER_NOTICE); + $this->assertCount(2, $handler->getRecords()); + $this->assertTrue($handler->hasEmergencyRecords()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/ChromePHPFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/ChromePHPFormatterTest.php new file mode 100644 index 0000000..71c4204 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/ChromePHPFormatterTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +class ChromePHPFormatterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Monolog\Formatter\ChromePHPFormatter::format + */ + public function testDefaultFormat() + { + $formatter = new ChromePHPFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('ip' => '127.0.0.1'), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertEquals( + array( + 'meh', + array( + 'message' => 'log', + 'context' => array('from' => 'logger'), + 'extra' => array('ip' => '127.0.0.1'), + ), + 'unknown', + 'error', + ), + $message + ); + } + + /** + * @covers Monolog\Formatter\ChromePHPFormatter::format + */ + public function testFormatWithFileAndLine() + { + $formatter = new ChromePHPFormatter(); + $record = array( + 'level' => Logger::CRITICAL, + 'level_name' => 'CRITICAL', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('ip' => '127.0.0.1', 'file' => 'test', 'line' => 14), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertEquals( + array( + 'meh', + array( + 'message' => 'log', + 'context' => array('from' => 'logger'), + 'extra' => array('ip' => '127.0.0.1'), + ), + 'test : 14', + 'error', + ), + $message + ); + } + + /** + * @covers Monolog\Formatter\ChromePHPFormatter::format + */ + public function testFormatWithoutContext() + { + $formatter = new ChromePHPFormatter(); + $record = array( + 'level' => Logger::DEBUG, + 'level_name' => 'DEBUG', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertEquals( + array( + 'meh', + 'log', + 'unknown', + 'log', + ), + $message + ); + } + + /** + * @covers Monolog\Formatter\ChromePHPFormatter::formatBatch + */ + public function testBatchFormatThrowException() + { + $formatter = new ChromePHPFormatter(); + $records = array( + array( + 'level' => Logger::INFO, + 'level_name' => 'INFO', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ), + array( + 'level' => Logger::WARNING, + 'level_name' => 'WARNING', + 'channel' => 'foo', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log2', + ), + ); + + $this->assertEquals( + array( + array( + 'meh', + 'log', + 'unknown', + 'info', + ), + array( + 'foo', + 'log2', + 'unknown', + 'warn', + ), + ), + $formatter->formatBatch($records) + ); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/ElasticaFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/ElasticaFormatterTest.php new file mode 100644 index 0000000..90cc48d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/ElasticaFormatterTest.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 Monolog\Formatter; + +use Monolog\Logger; + +class ElasticaFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!class_exists("Elastica\Document")) { + $this->markTestSkipped("ruflin/elastica not installed"); + } + } + + /** + * @covers Monolog\Formatter\ElasticaFormatter::__construct + * @covers Monolog\Formatter\ElasticaFormatter::format + * @covers Monolog\Formatter\ElasticaFormatter::getDocument + */ + public function testFormat() + { + // test log message + $msg = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('foo' => 7, 'bar', 'class' => new \stdClass), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + // expected values + $expected = $msg; + $expected['datetime'] = '1970-01-01T00:00:00.000000+00:00'; + $expected['context'] = array( + 'class' => '[object] (stdClass: {})', + 'foo' => 7, + 0 => 'bar', + ); + + // format log message + $formatter = new ElasticaFormatter('my_index', 'doc_type'); + $doc = $formatter->format($msg); + $this->assertInstanceOf('Elastica\Document', $doc); + + // Document parameters + $params = $doc->getParams(); + $this->assertEquals('my_index', $params['_index']); + $this->assertEquals('doc_type', $params['_type']); + + // Document data values + $data = $doc->getData(); + foreach (array_keys($expected) as $key) { + $this->assertEquals($expected[$key], $data[$key]); + } + } + + /** + * @covers Monolog\Formatter\ElasticaFormatter::getIndex + * @covers Monolog\Formatter\ElasticaFormatter::getType + */ + public function testGetters() + { + $formatter = new ElasticaFormatter('my_index', 'doc_type'); + $this->assertEquals('my_index', $formatter->getIndex()); + $this->assertEquals('doc_type', $formatter->getType()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/FlowdockFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/FlowdockFormatterTest.php new file mode 100644 index 0000000..1b2fd97 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/FlowdockFormatterTest.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 Monolog\Formatter; + +use Monolog\Logger; +use Monolog\TestCase; + +class FlowdockFormatterTest extends TestCase +{ + /** + * @covers Monolog\Formatter\FlowdockFormatter::format + */ + public function testFormat() + { + $formatter = new FlowdockFormatter('test_source', 'source@test.com'); + $record = $this->getRecord(); + + $expected = array( + 'source' => 'test_source', + 'from_address' => 'source@test.com', + 'subject' => 'in test_source: WARNING - test', + 'content' => 'test', + 'tags' => array('#logs', '#warning', '#test'), + 'project' => 'test_source', + ); + $formatted = $formatter->format($record); + + $this->assertEquals($expected, $formatted['flowdock']); + } + + /** + * @ covers Monolog\Formatter\FlowdockFormatter::formatBatch + */ + public function testFormatBatch() + { + $formatter = new FlowdockFormatter('test_source', 'source@test.com'); + $records = array( + $this->getRecord(Logger::WARNING), + $this->getRecord(Logger::DEBUG), + ); + $formatted = $formatter->formatBatch($records); + + $this->assertArrayHasKey('flowdock', $formatted[0]); + $this->assertArrayHasKey('flowdock', $formatted[1]); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php new file mode 100644 index 0000000..622b2ba --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/FluentdFormatterTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; +use Monolog\TestCase; + +class FluentdFormatterTest extends TestCase +{ + /** + * @covers Monolog\Formatter\FluentdFormatter::__construct + * @covers Monolog\Formatter\FluentdFormatter::isUsingLevelsInTag + */ + public function testConstruct() + { + $formatter = new FluentdFormatter(); + $this->assertEquals(false, $formatter->isUsingLevelsInTag()); + $formatter = new FluentdFormatter(false); + $this->assertEquals(false, $formatter->isUsingLevelsInTag()); + $formatter = new FluentdFormatter(true); + $this->assertEquals(true, $formatter->isUsingLevelsInTag()); + } + + /** + * @covers Monolog\Formatter\FluentdFormatter::format + */ + public function testFormat() + { + $record = $this->getRecord(Logger::WARNING); + $record['datetime'] = new \DateTime("@0"); + + $formatter = new FluentdFormatter(); + $this->assertEquals( + '["test",0,{"message":"test","extra":[],"level":300,"level_name":"WARNING"}]', + $formatter->format($record) + ); + } + + /** + * @covers Monolog\Formatter\FluentdFormatter::format + */ + public function testFormatWithTag() + { + $record = $this->getRecord(Logger::ERROR); + $record['datetime'] = new \DateTime("@0"); + + $formatter = new FluentdFormatter(true); + $this->assertEquals( + '["test.error",0,{"message":"test","extra":[]}]', + $formatter->format($record) + ); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.php new file mode 100644 index 0000000..4a24761 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/GelfMessageFormatterTest.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 Monolog\Formatter; + +use Monolog\Logger; + +class GelfMessageFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!class_exists('\Gelf\Message')) { + $this->markTestSkipped("graylog2/gelf-php or mlehner/gelf-php is not installed"); + } + } + + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + */ + public function testDefaultFormatter() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + $this->assertEquals(0, $message->getTimestamp()); + $this->assertEquals('log', $message->getShortMessage()); + $this->assertEquals('meh', $message->getFacility()); + $this->assertEquals(null, $message->getLine()); + $this->assertEquals(null, $message->getFile()); + $this->assertEquals($this->isLegacy() ? 3 : 'error', $message->getLevel()); + $this->assertNotEmpty($message->getHost()); + + $formatter = new GelfMessageFormatter('mysystem'); + + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + $this->assertEquals('mysystem', $message->getHost()); + } + + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + */ + public function testFormatWithFileAndLine() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('file' => 'test', 'line' => 14), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + $this->assertEquals('test', $message->getFile()); + $this->assertEquals(14, $message->getLine()); + } + + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + * @expectedException InvalidArgumentException + */ + public function testFormatInvalidFails() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + ); + + $formatter->format($record); + } + + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + */ + public function testFormatWithContext() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + + $message_array = $message->toArray(); + + $this->assertArrayHasKey('_ctxt_from', $message_array); + $this->assertEquals('logger', $message_array['_ctxt_from']); + + // Test with extraPrefix + $formatter = new GelfMessageFormatter(null, null, 'CTX'); + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + + $message_array = $message->toArray(); + + $this->assertArrayHasKey('_CTXfrom', $message_array); + $this->assertEquals('logger', $message_array['_CTXfrom']); + } + + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + */ + public function testFormatWithContextContainingException() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger', 'exception' => array( + 'class' => '\Exception', + 'file' => '/some/file/in/dir.php:56', + 'trace' => array('/some/file/1.php:23', '/some/file/2.php:3'), + )), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + + $this->assertEquals("/some/file/in/dir.php", $message->getFile()); + $this->assertEquals("56", $message->getLine()); + } + + /** + * @covers Monolog\Formatter\GelfMessageFormatter::format + */ + public function testFormatWithExtra() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + + $message_array = $message->toArray(); + + $this->assertArrayHasKey('_key', $message_array); + $this->assertEquals('pair', $message_array['_key']); + + // Test with extraPrefix + $formatter = new GelfMessageFormatter(null, 'EXT'); + $message = $formatter->format($record); + + $this->assertInstanceOf('Gelf\Message', $message); + + $message_array = $message->toArray(); + + $this->assertArrayHasKey('_EXTkey', $message_array); + $this->assertEquals('pair', $message_array['_EXTkey']); + } + + public function testFormatWithLargeData() + { + $formatter = new GelfMessageFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('exception' => str_repeat(' ', 32767)), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => str_repeat(' ', 32767)), + 'message' => 'log' + ); + $message = $formatter->format($record); + $messageArray = $message->toArray(); + + // 200 for padding + metadata + $length = 200; + + foreach ($messageArray as $key => $value) { + if (!in_array($key, array('level', 'timestamp'))) { + $length += strlen($value); + } + } + + $this->assertLessThanOrEqual(65792, $length, 'The message length is no longer than the maximum allowed length'); + } + + public function testFormatWithUnlimitedLength() + { + $formatter = new GelfMessageFormatter('LONG_SYSTEM_NAME', null, 'ctxt_', PHP_INT_MAX); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('exception' => str_repeat(' ', 32767 * 2)), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => str_repeat(' ', 32767 * 2)), + 'message' => 'log' + ); + $message = $formatter->format($record); + $messageArray = $message->toArray(); + + // 200 for padding + metadata + $length = 200; + + foreach ($messageArray as $key => $value) { + if (!in_array($key, array('level', 'timestamp'))) { + $length += strlen($value); + } + } + + $this->assertGreaterThanOrEqual(131289, $length, 'The message should not be truncated'); + } + + private function isLegacy() + { + return interface_exists('\Gelf\IMessagePublisher'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.php new file mode 100644 index 0000000..c9445f3 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/JsonFormatterTest.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 Monolog\Formatter; + +use Monolog\Logger; +use Monolog\TestCase; + +class JsonFormatterTest extends TestCase +{ + /** + * @covers Monolog\Formatter\JsonFormatter::__construct + * @covers Monolog\Formatter\JsonFormatter::getBatchMode + * @covers Monolog\Formatter\JsonFormatter::isAppendingNewlines + */ + public function testConstruct() + { + $formatter = new JsonFormatter(); + $this->assertEquals(JsonFormatter::BATCH_MODE_JSON, $formatter->getBatchMode()); + $this->assertEquals(true, $formatter->isAppendingNewlines()); + $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_NEWLINES, false); + $this->assertEquals(JsonFormatter::BATCH_MODE_NEWLINES, $formatter->getBatchMode()); + $this->assertEquals(false, $formatter->isAppendingNewlines()); + } + + /** + * @covers Monolog\Formatter\JsonFormatter::format + */ + public function testFormat() + { + $formatter = new JsonFormatter(); + $record = $this->getRecord(); + $this->assertEquals(json_encode($record)."\n", $formatter->format($record)); + + $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, false); + $record = $this->getRecord(); + $this->assertEquals(json_encode($record), $formatter->format($record)); + } + + /** + * @covers Monolog\Formatter\JsonFormatter::formatBatch + * @covers Monolog\Formatter\JsonFormatter::formatBatchJson + */ + public function testFormatBatch() + { + $formatter = new JsonFormatter(); + $records = array( + $this->getRecord(Logger::WARNING), + $this->getRecord(Logger::DEBUG), + ); + $this->assertEquals(json_encode($records), $formatter->formatBatch($records)); + } + + /** + * @covers Monolog\Formatter\JsonFormatter::formatBatch + * @covers Monolog\Formatter\JsonFormatter::formatBatchNewlines + */ + public function testFormatBatchNewlines() + { + $formatter = new JsonFormatter(JsonFormatter::BATCH_MODE_NEWLINES); + $records = $expected = array( + $this->getRecord(Logger::WARNING), + $this->getRecord(Logger::DEBUG), + ); + array_walk($expected, function (&$value, $key) { + $value = json_encode($value); + }); + $this->assertEquals(implode("\n", $expected), $formatter->formatBatch($records)); + } + + public function testDefFormatWithException() + { + $formatter = new JsonFormatter(); + $exception = new \RuntimeException('Foo'); + $formattedException = $this->formatException($exception); + + $message = $this->formatRecordWithExceptionInContext($formatter, $exception); + + $this->assertContextContainsFormattedException($formattedException, $message); + } + + public function testDefFormatWithPreviousException() + { + $formatter = new JsonFormatter(); + $exception = new \RuntimeException('Foo', 0, new \LogicException('Wut?')); + $formattedPrevException = $this->formatException($exception->getPrevious()); + $formattedException = $this->formatException($exception, $formattedPrevException); + + $message = $this->formatRecordWithExceptionInContext($formatter, $exception); + + $this->assertContextContainsFormattedException($formattedException, $message); + } + + public function testDefFormatWithThrowable() + { + if (!class_exists('Error') || !is_subclass_of('Error', 'Throwable')) { + $this->markTestSkipped('Requires PHP >=7'); + } + + $formatter = new JsonFormatter(); + $throwable = new \Error('Foo'); + $formattedThrowable = $this->formatException($throwable); + + $message = $this->formatRecordWithExceptionInContext($formatter, $throwable); + + $this->assertContextContainsFormattedException($formattedThrowable, $message); + } + + /** + * @param string $expected + * @param string $actual + * + * @internal param string $exception + */ + private function assertContextContainsFormattedException($expected, $actual) + { + $this->assertEquals( + '{"level_name":"CRITICAL","channel":"core","context":{"exception":'.$expected.'},"datetime":null,"extra":[],"message":"foobar"}'."\n", + $actual + ); + } + + /** + * @param JsonFormatter $formatter + * @param \Exception|\Throwable $exception + * + * @return string + */ + private function formatRecordWithExceptionInContext(JsonFormatter $formatter, $exception) + { + $message = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'core', + 'context' => array('exception' => $exception), + 'datetime' => null, + 'extra' => array(), + 'message' => 'foobar', + )); + return $message; + } + + /** + * @param \Exception|\Throwable $exception + * + * @return string + */ + private function formatExceptionFilePathWithLine($exception) + { + $options = 0; + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; + } + $path = substr(json_encode($exception->getFile(), $options), 1, -1); + return $path . ':' . $exception->getLine(); + } + + /** + * @param \Exception|\Throwable $exception + * + * @param null|string $previous + * + * @return string + */ + private function formatException($exception, $previous = null) + { + $formattedException = + '{"class":"' . get_class($exception) . + '","message":"' . $exception->getMessage() . + '","code":' . $exception->getCode() . + ',"file":"' . $this->formatExceptionFilePathWithLine($exception) . + ($previous ? '","previous":' . $previous : '"') . + '}'; + return $formattedException; + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php new file mode 100644 index 0000000..310d93c --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/LineFormatterTest.php @@ -0,0 +1,222 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * @covers Monolog\Formatter\LineFormatter + */ +class LineFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function testDefFormatWithString() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->format(array( + 'level_name' => 'WARNING', + 'channel' => 'log', + 'context' => array(), + 'message' => 'foo', + 'datetime' => new \DateTime, + 'extra' => array(), + )); + $this->assertEquals('['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message); + } + + public function testDefFormatWithArrayContext() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'message' => 'foo', + 'datetime' => new \DateTime, + 'extra' => array(), + 'context' => array( + 'foo' => 'bar', + 'baz' => 'qux', + 'bool' => false, + 'null' => null, + ), + )); + $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foo {"foo":"bar","baz":"qux","bool":false,"null":null} []'."\n", $message); + } + + public function testDefFormatExtras() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array('ip' => '127.0.0.1'), + 'message' => 'log', + )); + $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log [] {"ip":"127.0.0.1"}'."\n", $message); + } + + public function testFormatExtras() + { + $formatter = new LineFormatter("[%datetime%] %channel%.%level_name%: %message% %context% %extra.file% %extra%\n", 'Y-m-d'); + $message = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array('ip' => '127.0.0.1', 'file' => 'test'), + 'message' => 'log', + )); + $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log [] test {"ip":"127.0.0.1"}'."\n", $message); + } + + public function testContextAndExtraOptionallyNotShownIfEmpty() + { + $formatter = new LineFormatter(null, 'Y-m-d', false, true); + $message = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array(), + 'message' => 'log', + )); + $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: log '."\n", $message); + } + + public function testContextAndExtraReplacement() + { + $formatter = new LineFormatter('%context.foo% => %extra.foo%'); + $message = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('foo' => 'bar'), + 'datetime' => new \DateTime, + 'extra' => array('foo' => 'xbar'), + 'message' => 'log', + )); + $this->assertEquals('bar => xbar', $message); + } + + public function testDefFormatWithObject() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array('foo' => new TestFoo, 'bar' => new TestBar, 'baz' => array(), 'res' => fopen('php://memory', 'rb')), + 'message' => 'foobar', + )); + + $this->assertEquals('['.date('Y-m-d').'] meh.ERROR: foobar [] {"foo":"[object] (Monolog\\\\Formatter\\\\TestFoo: {\\"foo\\":\\"foo\\"})","bar":"[object] (Monolog\\\\Formatter\\\\TestBar: bar)","baz":[],"res":"[resource] (stream)"}'."\n", $message); + } + + public function testDefFormatWithException() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'core', + 'context' => array('exception' => new \RuntimeException('Foo')), + 'datetime' => new \DateTime, + 'extra' => array(), + 'message' => 'foobar', + )); + + $path = str_replace('\\/', '/', json_encode(__FILE__)); + + $this->assertEquals('['.date('Y-m-d').'] core.CRITICAL: foobar {"exception":"[object] (RuntimeException(code: 0): Foo at '.substr($path, 1, -1).':'.(__LINE__ - 8).')"} []'."\n", $message); + } + + public function testDefFormatWithPreviousException() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $previous = new \LogicException('Wut?'); + $message = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'core', + 'context' => array('exception' => new \RuntimeException('Foo', 0, $previous)), + 'datetime' => new \DateTime, + 'extra' => array(), + 'message' => 'foobar', + )); + + $path = str_replace('\\/', '/', json_encode(__FILE__)); + + $this->assertEquals('['.date('Y-m-d').'] core.CRITICAL: foobar {"exception":"[object] (RuntimeException(code: 0): Foo at '.substr($path, 1, -1).':'.(__LINE__ - 8).', LogicException(code: 0): Wut? at '.substr($path, 1, -1).':'.(__LINE__ - 12).')"} []'."\n", $message); + } + + public function testBatchFormat() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->formatBatch(array( + array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array(), + ), + array( + 'level_name' => 'WARNING', + 'channel' => 'log', + 'message' => 'foo', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array(), + ), + )); + $this->assertEquals('['.date('Y-m-d').'] test.CRITICAL: bar [] []'."\n".'['.date('Y-m-d').'] log.WARNING: foo [] []'."\n", $message); + } + + public function testFormatShouldStripInlineLineBreaks() + { + $formatter = new LineFormatter(null, 'Y-m-d'); + $message = $formatter->format( + array( + 'message' => "foo\nbar", + 'context' => array(), + 'extra' => array(), + ) + ); + + $this->assertRegExp('/foo bar/', $message); + } + + public function testFormatShouldNotStripInlineLineBreaksWhenFlagIsSet() + { + $formatter = new LineFormatter(null, 'Y-m-d', true); + $message = $formatter->format( + array( + 'message' => "foo\nbar", + 'context' => array(), + 'extra' => array(), + ) + ); + + $this->assertRegExp('/foo\nbar/', $message); + } +} + +class TestFoo +{ + public $foo = 'foo'; +} + +class TestBar +{ + public function __toString() + { + return 'bar'; + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/LogglyFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/LogglyFormatterTest.php new file mode 100644 index 0000000..6d59b3f --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/LogglyFormatterTest.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 Monolog\Formatter; + +use Monolog\TestCase; + +class LogglyFormatterTest extends TestCase +{ + /** + * @covers Monolog\Formatter\LogglyFormatter::__construct + */ + public function testConstruct() + { + $formatter = new LogglyFormatter(); + $this->assertEquals(LogglyFormatter::BATCH_MODE_NEWLINES, $formatter->getBatchMode()); + $formatter = new LogglyFormatter(LogglyFormatter::BATCH_MODE_JSON); + $this->assertEquals(LogglyFormatter::BATCH_MODE_JSON, $formatter->getBatchMode()); + } + + /** + * @covers Monolog\Formatter\LogglyFormatter::format + */ + public function testFormat() + { + $formatter = new LogglyFormatter(); + $record = $this->getRecord(); + $formatted_decoded = json_decode($formatter->format($record), true); + $this->assertArrayHasKey("timestamp", $formatted_decoded); + $this->assertEquals(new \DateTime($formatted_decoded["timestamp"]), $record["datetime"]); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.php new file mode 100644 index 0000000..9f6b1cc --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/LogstashFormatterTest.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 Monolog\Formatter; + +use Monolog\Logger; + +class LogstashFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function tearDown() + { + \PHPUnit_Framework_Error_Warning::$enabled = true; + + return parent::tearDown(); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testDefaultFormatter() + { + $formatter = new LogstashFormatter('test', 'hostname'); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']); + $this->assertEquals('log', $message['@message']); + $this->assertEquals('meh', $message['@fields']['channel']); + $this->assertContains('meh', $message['@tags']); + $this->assertEquals(Logger::ERROR, $message['@fields']['level']); + $this->assertEquals('test', $message['@type']); + $this->assertEquals('hostname', $message['@source']); + + $formatter = new LogstashFormatter('mysystem'); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals('mysystem', $message['@type']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testFormatWithFileAndLine() + { + $formatter = new LogstashFormatter('test'); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('file' => 'test', 'line' => 14), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals('test', $message['@fields']['file']); + $this->assertEquals(14, $message['@fields']['line']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testFormatWithContext() + { + $formatter = new LogstashFormatter('test'); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $message_array = $message['@fields']; + + $this->assertArrayHasKey('ctxt_from', $message_array); + $this->assertEquals('logger', $message_array['ctxt_from']); + + // Test with extraPrefix + $formatter = new LogstashFormatter('test', null, null, 'CTX'); + $message = json_decode($formatter->format($record), true); + + $message_array = $message['@fields']; + + $this->assertArrayHasKey('CTXfrom', $message_array); + $this->assertEquals('logger', $message_array['CTXfrom']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testFormatWithExtra() + { + $formatter = new LogstashFormatter('test'); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $message_array = $message['@fields']; + + $this->assertArrayHasKey('key', $message_array); + $this->assertEquals('pair', $message_array['key']); + + // Test with extraPrefix + $formatter = new LogstashFormatter('test', null, 'EXT'); + $message = json_decode($formatter->format($record), true); + + $message_array = $message['@fields']; + + $this->assertArrayHasKey('EXTkey', $message_array); + $this->assertEquals('pair', $message_array['EXTkey']); + } + + public function testFormatWithApplicationName() + { + $formatter = new LogstashFormatter('app', 'test'); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertArrayHasKey('@type', $message); + $this->assertEquals('app', $message['@type']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testDefaultFormatterV1() + { + $formatter = new LogstashFormatter('test', 'hostname', null, 'ctxt_', LogstashFormatter::V1); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']); + $this->assertEquals("1", $message['@version']); + $this->assertEquals('log', $message['message']); + $this->assertEquals('meh', $message['channel']); + $this->assertEquals('ERROR', $message['level']); + $this->assertEquals('test', $message['type']); + $this->assertEquals('hostname', $message['host']); + + $formatter = new LogstashFormatter('mysystem', null, null, 'ctxt_', LogstashFormatter::V1); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals('mysystem', $message['type']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testFormatWithFileAndLineV1() + { + $formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('file' => 'test', 'line' => 14), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals('test', $message['file']); + $this->assertEquals(14, $message['line']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testFormatWithContextV1() + { + $formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertArrayHasKey('ctxt_from', $message); + $this->assertEquals('logger', $message['ctxt_from']); + + // Test with extraPrefix + $formatter = new LogstashFormatter('test', null, null, 'CTX', LogstashFormatter::V1); + $message = json_decode($formatter->format($record), true); + + $this->assertArrayHasKey('CTXfrom', $message); + $this->assertEquals('logger', $message['CTXfrom']); + } + + /** + * @covers Monolog\Formatter\LogstashFormatter::format + */ + public function testFormatWithExtraV1() + { + $formatter = new LogstashFormatter('test', null, null, 'ctxt_', LogstashFormatter::V1); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertArrayHasKey('key', $message); + $this->assertEquals('pair', $message['key']); + + // Test with extraPrefix + $formatter = new LogstashFormatter('test', null, 'EXT', 'ctxt_', LogstashFormatter::V1); + $message = json_decode($formatter->format($record), true); + + $this->assertArrayHasKey('EXTkey', $message); + $this->assertEquals('pair', $message['EXTkey']); + } + + public function testFormatWithApplicationNameV1() + { + $formatter = new LogstashFormatter('app', 'test', null, 'ctxt_', LogstashFormatter::V1); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('key' => 'pair'), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertArrayHasKey('type', $message); + $this->assertEquals('app', $message['type']); + } + + public function testFormatWithLatin9Data() + { + if (version_compare(PHP_VERSION, '5.5.0', '<')) { + // Ignore the warning that will be emitted by PHP <5.5.0 + \PHPUnit_Framework_Error_Warning::$enabled = false; + } + $formatter = new LogstashFormatter('test', 'hostname'); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => '¯\_(ツ)_/¯', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array( + 'user_agent' => "\xD6WN; FBCR/OrangeEspa\xF1a; Vers\xE3o/4.0; F\xE4rist", + ), + 'message' => 'log', + ); + + $message = json_decode($formatter->format($record), true); + + $this->assertEquals("1970-01-01T00:00:00.000000+00:00", $message['@timestamp']); + $this->assertEquals('log', $message['@message']); + $this->assertEquals('¯\_(ツ)_/¯', $message['@fields']['channel']); + $this->assertContains('¯\_(ツ)_/¯', $message['@tags']); + $this->assertEquals(Logger::ERROR, $message['@fields']['level']); + $this->assertEquals('test', $message['@type']); + $this->assertEquals('hostname', $message['@source']); + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { + $this->assertEquals('ÖWN; FBCR/OrangeEspaña; Versão/4.0; Färist', $message['@fields']['user_agent']); + } else { + // PHP <5.5 does not return false for an element encoding failure, + // instead it emits a warning (possibly) and nulls the value. + $this->assertEquals(null, $message['@fields']['user_agent']); + } + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/MongoDBFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/MongoDBFormatterTest.php new file mode 100644 index 0000000..52e699e --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/MongoDBFormatterTest.php @@ -0,0 +1,262 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +use Monolog\Logger; + +/** + * @author Florian Plattner + */ +class MongoDBFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!class_exists('MongoDate')) { + $this->markTestSkipped('mongo extension not installed'); + } + } + + public function constructArgumentProvider() + { + return array( + array(1, true, 1, true), + array(0, false, 0, false), + ); + } + + /** + * @param $traceDepth + * @param $traceAsString + * @param $expectedTraceDepth + * @param $expectedTraceAsString + * + * @dataProvider constructArgumentProvider + */ + public function testConstruct($traceDepth, $traceAsString, $expectedTraceDepth, $expectedTraceAsString) + { + $formatter = new MongoDBFormatter($traceDepth, $traceAsString); + + $reflTrace = new \ReflectionProperty($formatter, 'exceptionTraceAsString'); + $reflTrace->setAccessible(true); + $this->assertEquals($expectedTraceAsString, $reflTrace->getValue($formatter)); + + $reflDepth = new\ReflectionProperty($formatter, 'maxNestingLevel'); + $reflDepth->setAccessible(true); + $this->assertEquals($expectedTraceDepth, $reflDepth->getValue($formatter)); + } + + public function testSimpleFormat() + { + $record = array( + 'message' => 'some log message', + 'context' => array(), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => new \DateTime('2014-02-01 00:00:00'), + 'extra' => array(), + ); + + $formatter = new MongoDBFormatter(); + $formattedRecord = $formatter->format($record); + + $this->assertCount(7, $formattedRecord); + $this->assertEquals('some log message', $formattedRecord['message']); + $this->assertEquals(array(), $formattedRecord['context']); + $this->assertEquals(Logger::WARNING, $formattedRecord['level']); + $this->assertEquals(Logger::getLevelName(Logger::WARNING), $formattedRecord['level_name']); + $this->assertEquals('test', $formattedRecord['channel']); + $this->assertInstanceOf('\MongoDate', $formattedRecord['datetime']); + $this->assertEquals('0.00000000 1391212800', $formattedRecord['datetime']->__toString()); + $this->assertEquals(array(), $formattedRecord['extra']); + } + + public function testRecursiveFormat() + { + $someObject = new \stdClass(); + $someObject->foo = 'something'; + $someObject->bar = 'stuff'; + + $record = array( + 'message' => 'some log message', + 'context' => array( + 'stuff' => new \DateTime('2014-02-01 02:31:33'), + 'some_object' => $someObject, + 'context_string' => 'some string', + 'context_int' => 123456, + 'except' => new \Exception('exception message', 987), + ), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => new \DateTime('2014-02-01 00:00:00'), + 'extra' => array(), + ); + + $formatter = new MongoDBFormatter(); + $formattedRecord = $formatter->format($record); + + $this->assertCount(5, $formattedRecord['context']); + $this->assertInstanceOf('\MongoDate', $formattedRecord['context']['stuff']); + $this->assertEquals('0.00000000 1391221893', $formattedRecord['context']['stuff']->__toString()); + $this->assertEquals( + array( + 'foo' => 'something', + 'bar' => 'stuff', + 'class' => 'stdClass', + ), + $formattedRecord['context']['some_object'] + ); + $this->assertEquals('some string', $formattedRecord['context']['context_string']); + $this->assertEquals(123456, $formattedRecord['context']['context_int']); + + $this->assertCount(5, $formattedRecord['context']['except']); + $this->assertEquals('exception message', $formattedRecord['context']['except']['message']); + $this->assertEquals(987, $formattedRecord['context']['except']['code']); + $this->assertInternalType('string', $formattedRecord['context']['except']['file']); + $this->assertInternalType('integer', $formattedRecord['context']['except']['code']); + $this->assertInternalType('string', $formattedRecord['context']['except']['trace']); + $this->assertEquals('Exception', $formattedRecord['context']['except']['class']); + } + + public function testFormatDepthArray() + { + $record = array( + 'message' => 'some log message', + 'context' => array( + 'nest2' => array( + 'property' => 'anything', + 'nest3' => array( + 'nest4' => 'value', + 'property' => 'nothing', + ), + ), + ), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => new \DateTime('2014-02-01 00:00:00'), + 'extra' => array(), + ); + + $formatter = new MongoDBFormatter(2); + $formattedResult = $formatter->format($record); + + $this->assertEquals( + array( + 'nest2' => array( + 'property' => 'anything', + 'nest3' => '[...]', + ), + ), + $formattedResult['context'] + ); + } + + public function testFormatDepthArrayInfiniteNesting() + { + $record = array( + 'message' => 'some log message', + 'context' => array( + 'nest2' => array( + 'property' => 'something', + 'nest3' => array( + 'property' => 'anything', + 'nest4' => array( + 'property' => 'nothing', + ), + ), + ), + ), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => new \DateTime('2014-02-01 00:00:00'), + 'extra' => array(), + ); + + $formatter = new MongoDBFormatter(0); + $formattedResult = $formatter->format($record); + + $this->assertEquals( + array( + 'nest2' => array( + 'property' => 'something', + 'nest3' => array( + 'property' => 'anything', + 'nest4' => array( + 'property' => 'nothing', + ), + ), + ), + ), + $formattedResult['context'] + ); + } + + public function testFormatDepthObjects() + { + $someObject = new \stdClass(); + $someObject->property = 'anything'; + $someObject->nest3 = new \stdClass(); + $someObject->nest3->property = 'nothing'; + $someObject->nest3->nest4 = 'invisible'; + + $record = array( + 'message' => 'some log message', + 'context' => array( + 'nest2' => $someObject, + ), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => new \DateTime('2014-02-01 00:00:00'), + 'extra' => array(), + ); + + $formatter = new MongoDBFormatter(2, true); + $formattedResult = $formatter->format($record); + + $this->assertEquals( + array( + 'nest2' => array( + 'property' => 'anything', + 'nest3' => '[...]', + 'class' => 'stdClass', + ), + ), + $formattedResult['context'] + ); + } + + public function testFormatDepthException() + { + $record = array( + 'message' => 'some log message', + 'context' => array( + 'nest2' => new \Exception('exception message', 987), + ), + 'level' => Logger::WARNING, + 'level_name' => Logger::getLevelName(Logger::WARNING), + 'channel' => 'test', + 'datetime' => new \DateTime('2014-02-01 00:00:00'), + 'extra' => array(), + ); + + $formatter = new MongoDBFormatter(2, false); + $formattedRecord = $formatter->format($record); + + $this->assertEquals('exception message', $formattedRecord['context']['nest2']['message']); + $this->assertEquals(987, $formattedRecord['context']['nest2']['code']); + $this->assertEquals('[...]', $formattedRecord['context']['nest2']['trace']); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php new file mode 100644 index 0000000..57bcdf9 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/NormalizerFormatterTest.php @@ -0,0 +1,423 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Formatter; + +/** + * @covers Monolog\Formatter\NormalizerFormatter + */ +class NormalizerFormatterTest extends \PHPUnit_Framework_TestCase +{ + public function tearDown() + { + \PHPUnit_Framework_Error_Warning::$enabled = true; + + return parent::tearDown(); + } + + public function testFormat() + { + $formatter = new NormalizerFormatter('Y-m-d'); + $formatted = $formatter->format(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'message' => 'foo', + 'datetime' => new \DateTime, + 'extra' => array('foo' => new TestFooNorm, 'bar' => new TestBarNorm, 'baz' => array(), 'res' => fopen('php://memory', 'rb')), + 'context' => array( + 'foo' => 'bar', + 'baz' => 'qux', + 'inf' => INF, + '-inf' => -INF, + 'nan' => acos(4), + ), + )); + + $this->assertEquals(array( + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'message' => 'foo', + 'datetime' => date('Y-m-d'), + 'extra' => array( + 'foo' => '[object] (Monolog\\Formatter\\TestFooNorm: {"foo":"foo"})', + 'bar' => '[object] (Monolog\\Formatter\\TestBarNorm: bar)', + 'baz' => array(), + 'res' => '[resource] (stream)', + ), + 'context' => array( + 'foo' => 'bar', + 'baz' => 'qux', + 'inf' => 'INF', + '-inf' => '-INF', + 'nan' => 'NaN', + ), + ), $formatted); + } + + public function testFormatExceptions() + { + $formatter = new NormalizerFormatter('Y-m-d'); + $e = new \LogicException('bar'); + $e2 = new \RuntimeException('foo', 0, $e); + $formatted = $formatter->format(array( + 'exception' => $e2, + )); + + $this->assertGreaterThan(5, count($formatted['exception']['trace'])); + $this->assertTrue(isset($formatted['exception']['previous'])); + unset($formatted['exception']['trace'], $formatted['exception']['previous']); + + $this->assertEquals(array( + 'exception' => array( + 'class' => get_class($e2), + 'message' => $e2->getMessage(), + 'code' => $e2->getCode(), + 'file' => $e2->getFile().':'.$e2->getLine(), + ), + ), $formatted); + } + + public function testFormatSoapFaultException() + { + if (!class_exists('SoapFault')) { + $this->markTestSkipped('Requires the soap extension'); + } + + $formatter = new NormalizerFormatter('Y-m-d'); + $e = new \SoapFault('foo', 'bar', 'hello', 'world'); + $formatted = $formatter->format(array( + 'exception' => $e, + )); + + unset($formatted['exception']['trace']); + + $this->assertEquals(array( + 'exception' => array( + 'class' => 'SoapFault', + 'message' => 'bar', + 'code' => 0, + 'file' => $e->getFile().':'.$e->getLine(), + 'faultcode' => 'foo', + 'faultactor' => 'hello', + 'detail' => 'world', + ), + ), $formatted); + } + + public function testFormatToStringExceptionHandle() + { + $formatter = new NormalizerFormatter('Y-m-d'); + $this->setExpectedException('RuntimeException', 'Could not convert to string'); + $formatter->format(array( + 'myObject' => new TestToStringError(), + )); + } + + public function testBatchFormat() + { + $formatter = new NormalizerFormatter('Y-m-d'); + $formatted = $formatter->formatBatch(array( + array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array(), + ), + array( + 'level_name' => 'WARNING', + 'channel' => 'log', + 'message' => 'foo', + 'context' => array(), + 'datetime' => new \DateTime, + 'extra' => array(), + ), + )); + $this->assertEquals(array( + array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array(), + 'datetime' => date('Y-m-d'), + 'extra' => array(), + ), + array( + 'level_name' => 'WARNING', + 'channel' => 'log', + 'message' => 'foo', + 'context' => array(), + 'datetime' => date('Y-m-d'), + 'extra' => array(), + ), + ), $formatted); + } + + /** + * Test issue #137 + */ + public function testIgnoresRecursiveObjectReferences() + { + // set up the recursion + $foo = new \stdClass(); + $bar = new \stdClass(); + + $foo->bar = $bar; + $bar->foo = $foo; + + // set an error handler to assert that the error is not raised anymore + $that = $this; + set_error_handler(function ($level, $message, $file, $line, $context) use ($that) { + if (error_reporting() & $level) { + restore_error_handler(); + $that->fail("$message should not be raised"); + } + }); + + $formatter = new NormalizerFormatter(); + $reflMethod = new \ReflectionMethod($formatter, 'toJson'); + $reflMethod->setAccessible(true); + $res = $reflMethod->invoke($formatter, array($foo, $bar), true); + + restore_error_handler(); + + $this->assertEquals(@json_encode(array($foo, $bar)), $res); + } + + public function testIgnoresInvalidTypes() + { + // set up the recursion + $resource = fopen(__FILE__, 'r'); + + // set an error handler to assert that the error is not raised anymore + $that = $this; + set_error_handler(function ($level, $message, $file, $line, $context) use ($that) { + if (error_reporting() & $level) { + restore_error_handler(); + $that->fail("$message should not be raised"); + } + }); + + $formatter = new NormalizerFormatter(); + $reflMethod = new \ReflectionMethod($formatter, 'toJson'); + $reflMethod->setAccessible(true); + $res = $reflMethod->invoke($formatter, array($resource), true); + + restore_error_handler(); + + $this->assertEquals(@json_encode(array($resource)), $res); + } + + public function testNormalizeHandleLargeArrays() + { + $formatter = new NormalizerFormatter(); + $largeArray = range(1, 2000); + + $res = $formatter->format(array( + 'level_name' => 'CRITICAL', + 'channel' => 'test', + 'message' => 'bar', + 'context' => array($largeArray), + 'datetime' => new \DateTime, + 'extra' => array(), + )); + + $this->assertCount(1000, $res['context'][0]); + $this->assertEquals('Over 1000 items (2000 total), aborting normalization', $res['context'][0]['...']); + } + + /** + * @expectedException RuntimeException + */ + public function testThrowsOnInvalidEncoding() + { + if (version_compare(PHP_VERSION, '5.5.0', '<')) { + // Ignore the warning that will be emitted by PHP <5.5.0 + \PHPUnit_Framework_Error_Warning::$enabled = false; + } + $formatter = new NormalizerFormatter(); + $reflMethod = new \ReflectionMethod($formatter, 'toJson'); + $reflMethod->setAccessible(true); + + // send an invalid unicode sequence as a object that can't be cleaned + $record = new \stdClass; + $record->message = "\xB1\x31"; + $res = $reflMethod->invoke($formatter, $record); + if (PHP_VERSION_ID < 50500 && $res === '{"message":null}') { + throw new \RuntimeException('PHP 5.3/5.4 throw a warning and null the value instead of returning false entirely'); + } + } + + public function testConvertsInvalidEncodingAsLatin9() + { + if (version_compare(PHP_VERSION, '5.5.0', '<')) { + // Ignore the warning that will be emitted by PHP <5.5.0 + \PHPUnit_Framework_Error_Warning::$enabled = false; + } + $formatter = new NormalizerFormatter(); + $reflMethod = new \ReflectionMethod($formatter, 'toJson'); + $reflMethod->setAccessible(true); + + $res = $reflMethod->invoke($formatter, array('message' => "\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE")); + + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { + $this->assertSame('{"message":"€ŠšŽžŒœŸ"}', $res); + } else { + // PHP <5.5 does not return false for an element encoding failure, + // instead it emits a warning (possibly) and nulls the value. + $this->assertSame('{"message":null}', $res); + } + } + + /** + * @param mixed $in Input + * @param mixed $expect Expected output + * @covers Monolog\Formatter\NormalizerFormatter::detectAndCleanUtf8 + * @dataProvider providesDetectAndCleanUtf8 + */ + public function testDetectAndCleanUtf8($in, $expect) + { + $formatter = new NormalizerFormatter(); + $formatter->detectAndCleanUtf8($in); + $this->assertSame($expect, $in); + } + + public function providesDetectAndCleanUtf8() + { + $obj = new \stdClass; + + return array( + 'null' => array(null, null), + 'int' => array(123, 123), + 'float' => array(123.45, 123.45), + 'bool false' => array(false, false), + 'bool true' => array(true, true), + 'ascii string' => array('abcdef', 'abcdef'), + 'latin9 string' => array("\xB1\x31\xA4\xA6\xA8\xB4\xB8\xBC\xBD\xBE\xFF", '±1€ŠšŽžŒœŸÿ'), + 'unicode string' => array('¤¦¨´¸¼½¾€ŠšŽžŒœŸ', '¤¦¨´¸¼½¾€ŠšŽžŒœŸ'), + 'empty array' => array(array(), array()), + 'array' => array(array('abcdef'), array('abcdef')), + 'object' => array($obj, $obj), + ); + } + + /** + * @param int $code + * @param string $msg + * @dataProvider providesHandleJsonErrorFailure + */ + public function testHandleJsonErrorFailure($code, $msg) + { + $formatter = new NormalizerFormatter(); + $reflMethod = new \ReflectionMethod($formatter, 'handleJsonError'); + $reflMethod->setAccessible(true); + + $this->setExpectedException('RuntimeException', $msg); + $reflMethod->invoke($formatter, $code, 'faked'); + } + + public function providesHandleJsonErrorFailure() + { + return array( + 'depth' => array(JSON_ERROR_DEPTH, 'Maximum stack depth exceeded'), + 'state' => array(JSON_ERROR_STATE_MISMATCH, 'Underflow or the modes mismatch'), + 'ctrl' => array(JSON_ERROR_CTRL_CHAR, 'Unexpected control character found'), + 'default' => array(-1, 'Unknown error'), + ); + } + + public function testExceptionTraceWithArgs() + { + if (defined('HHVM_VERSION')) { + $this->markTestSkipped('Not supported in HHVM since it detects errors differently'); + } + + // This happens i.e. in React promises or Guzzle streams where stream wrappers are registered + // and no file or line are included in the trace because it's treated as internal function + set_error_handler(function ($errno, $errstr, $errfile, $errline) { + throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); + }); + + try { + // This will contain $resource and $wrappedResource as arguments in the trace item + $resource = fopen('php://memory', 'rw+'); + fwrite($resource, 'test_resource'); + $wrappedResource = new TestFooNorm; + $wrappedResource->foo = $resource; + // Just do something stupid with a resource/wrapped resource as argument + array_keys($wrappedResource); + } catch (\Exception $e) { + restore_error_handler(); + } + + $formatter = new NormalizerFormatter(); + $record = array('context' => array('exception' => $e)); + $result = $formatter->format($record); + + $this->assertRegExp( + '%"resource":"\[resource\] \(stream\)"%', + $result['context']['exception']['trace'][0] + ); + + if (version_compare(PHP_VERSION, '5.5.0', '>=')) { + $pattern = '%"wrappedResource":"\[object\] \(Monolog\\\\\\\\Formatter\\\\\\\\TestFooNorm: \)"%'; + } else { + $pattern = '%\\\\"foo\\\\":null%'; + } + + // Tests that the wrapped resource is ignored while encoding, only works for PHP <= 5.4 + $this->assertRegExp( + $pattern, + $result['context']['exception']['trace'][0] + ); + } +} + +class TestFooNorm +{ + public $foo = 'foo'; +} + +class TestBarNorm +{ + public function __toString() + { + return 'bar'; + } +} + +class TestStreamFoo +{ + public $foo; + public $resource; + + public function __construct($resource) + { + $this->resource = $resource; + $this->foo = 'BAR'; + } + + public function __toString() + { + fseek($this->resource, 0); + + return $this->foo . ' - ' . (string) stream_get_contents($this->resource); + } +} + +class TestToStringError +{ + public function __toString() + { + throw new \RuntimeException('Could not convert to string'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/ScalarFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/ScalarFormatterTest.php new file mode 100644 index 0000000..b1c8fd4 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/ScalarFormatterTest.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 Monolog\Formatter; + +class ScalarFormatterTest extends \PHPUnit_Framework_TestCase +{ + private $formatter; + + public function setUp() + { + $this->formatter = new ScalarFormatter(); + } + + public function buildTrace(\Exception $e) + { + $data = array(); + $trace = $e->getTrace(); + foreach ($trace as $frame) { + if (isset($frame['file'])) { + $data[] = $frame['file'].':'.$frame['line']; + } else { + $data[] = json_encode($frame); + } + } + + return $data; + } + + public function encodeJson($data) + { + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + return json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + } + + return json_encode($data); + } + + public function testFormat() + { + $exception = new \Exception('foo'); + $formatted = $this->formatter->format(array( + 'foo' => 'string', + 'bar' => 1, + 'baz' => false, + 'bam' => array(1, 2, 3), + 'bat' => array('foo' => 'bar'), + 'bap' => \DateTime::createFromFormat(\DateTime::ISO8601, '1970-01-01T00:00:00+0000'), + 'ban' => $exception, + )); + + $this->assertSame(array( + 'foo' => 'string', + 'bar' => 1, + 'baz' => false, + 'bam' => $this->encodeJson(array(1, 2, 3)), + 'bat' => $this->encodeJson(array('foo' => 'bar')), + 'bap' => '1970-01-01 00:00:00', + 'ban' => $this->encodeJson(array( + 'class' => get_class($exception), + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + 'trace' => $this->buildTrace($exception), + )), + ), $formatted); + } + + public function testFormatWithErrorContext() + { + $context = array('file' => 'foo', 'line' => 1); + $formatted = $this->formatter->format(array( + 'context' => $context, + )); + + $this->assertSame(array( + 'context' => $this->encodeJson($context), + ), $formatted); + } + + public function testFormatWithExceptionContext() + { + $exception = new \Exception('foo'); + $formatted = $this->formatter->format(array( + 'context' => array( + 'exception' => $exception, + ), + )); + + $this->assertSame(array( + 'context' => $this->encodeJson(array( + 'exception' => array( + 'class' => get_class($exception), + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile() . ':' . $exception->getLine(), + 'trace' => $this->buildTrace($exception), + ), + )), + ), $formatted); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.php b/vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.php new file mode 100644 index 0000000..52f15a3 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Formatter/WildfireFormatterTest.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 Monolog\Formatter; + +use Monolog\Logger; + +class WildfireFormatterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Monolog\Formatter\WildfireFormatter::format + */ + public function testDefaultFormat() + { + $wildfire = new WildfireFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('ip' => '127.0.0.1'), + 'message' => 'log', + ); + + $message = $wildfire->format($record); + + $this->assertEquals( + '125|[{"Type":"ERROR","File":"","Line":"","Label":"meh"},' + .'{"message":"log","context":{"from":"logger"},"extra":{"ip":"127.0.0.1"}}]|', + $message + ); + } + + /** + * @covers Monolog\Formatter\WildfireFormatter::format + */ + public function testFormatWithFileAndLine() + { + $wildfire = new WildfireFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('from' => 'logger'), + 'datetime' => new \DateTime("@0"), + 'extra' => array('ip' => '127.0.0.1', 'file' => 'test', 'line' => 14), + 'message' => 'log', + ); + + $message = $wildfire->format($record); + + $this->assertEquals( + '129|[{"Type":"ERROR","File":"test","Line":14,"Label":"meh"},' + .'{"message":"log","context":{"from":"logger"},"extra":{"ip":"127.0.0.1"}}]|', + $message + ); + } + + /** + * @covers Monolog\Formatter\WildfireFormatter::format + */ + public function testFormatWithoutContext() + { + $wildfire = new WildfireFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $message = $wildfire->format($record); + + $this->assertEquals( + '58|[{"Type":"ERROR","File":"","Line":"","Label":"meh"},"log"]|', + $message + ); + } + + /** + * @covers Monolog\Formatter\WildfireFormatter::formatBatch + * @expectedException BadMethodCallException + */ + public function testBatchFormatThrowException() + { + $wildfire = new WildfireFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array(), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $wildfire->formatBatch(array($record)); + } + + /** + * @covers Monolog\Formatter\WildfireFormatter::format + */ + public function testTableFormat() + { + $wildfire = new WildfireFormatter(); + $record = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'table-channel', + 'context' => array( + WildfireFormatter::TABLE => array( + array('col1', 'col2', 'col3'), + array('val1', 'val2', 'val3'), + array('foo1', 'foo2', 'foo3'), + array('bar1', 'bar2', 'bar3'), + ), + ), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'table-message', + ); + + $message = $wildfire->format($record); + + $this->assertEquals( + '171|[{"Type":"TABLE","File":"","Line":"","Label":"table-channel: table-message"},[["col1","col2","col3"],["val1","val2","val3"],["foo1","foo2","foo3"],["bar1","bar2","bar3"]]]|', + $message + ); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.php new file mode 100644 index 0000000..568eb9d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/AbstractHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; +use Monolog\Processor\WebProcessor; + +class AbstractHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\AbstractHandler::__construct + * @covers Monolog\Handler\AbstractHandler::getLevel + * @covers Monolog\Handler\AbstractHandler::setLevel + * @covers Monolog\Handler\AbstractHandler::getBubble + * @covers Monolog\Handler\AbstractHandler::setBubble + * @covers Monolog\Handler\AbstractHandler::getFormatter + * @covers Monolog\Handler\AbstractHandler::setFormatter + */ + public function testConstructAndGetSet() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler', array(Logger::WARNING, false)); + $this->assertEquals(Logger::WARNING, $handler->getLevel()); + $this->assertEquals(false, $handler->getBubble()); + + $handler->setLevel(Logger::ERROR); + $handler->setBubble(true); + $handler->setFormatter($formatter = new LineFormatter); + $this->assertEquals(Logger::ERROR, $handler->getLevel()); + $this->assertEquals(true, $handler->getBubble()); + $this->assertSame($formatter, $handler->getFormatter()); + } + + /** + * @covers Monolog\Handler\AbstractHandler::handleBatch + */ + public function testHandleBatch() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); + $handler->expects($this->exactly(2)) + ->method('handle'); + $handler->handleBatch(array($this->getRecord(), $this->getRecord())); + } + + /** + * @covers Monolog\Handler\AbstractHandler::isHandling + */ + public function testIsHandling() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler', array(Logger::WARNING, false)); + $this->assertTrue($handler->isHandling($this->getRecord())); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\AbstractHandler::__construct + */ + public function testHandlesPsrStyleLevels() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler', array('warning', false)); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); + $handler->setLevel('debug'); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\AbstractHandler::getFormatter + * @covers Monolog\Handler\AbstractHandler::getDefaultFormatter + */ + public function testGetFormatterInitializesDefault() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); + $this->assertInstanceOf('Monolog\Formatter\LineFormatter', $handler->getFormatter()); + } + + /** + * @covers Monolog\Handler\AbstractHandler::pushProcessor + * @covers Monolog\Handler\AbstractHandler::popProcessor + * @expectedException LogicException + */ + public function testPushPopProcessor() + { + $logger = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); + $processor1 = new WebProcessor; + $processor2 = new WebProcessor; + + $logger->pushProcessor($processor1); + $logger->pushProcessor($processor2); + + $this->assertEquals($processor2, $logger->popProcessor()); + $this->assertEquals($processor1, $logger->popProcessor()); + $logger->popProcessor(); + } + + /** + * @covers Monolog\Handler\AbstractHandler::pushProcessor + * @expectedException InvalidArgumentException + */ + public function testPushProcessorWithNonCallable() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractHandler'); + + $handler->pushProcessor(new \stdClass()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/AbstractProcessingHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/AbstractProcessingHandlerTest.php new file mode 100644 index 0000000..24d4f63 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/AbstractProcessingHandlerTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Processor\WebProcessor; + +class AbstractProcessingHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\AbstractProcessingHandler::handle + */ + public function testHandleLowerLevelMessage() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractProcessingHandler', array(Logger::WARNING, true)); + $this->assertFalse($handler->handle($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\AbstractProcessingHandler::handle + */ + public function testHandleBubbling() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractProcessingHandler', array(Logger::DEBUG, true)); + $this->assertFalse($handler->handle($this->getRecord())); + } + + /** + * @covers Monolog\Handler\AbstractProcessingHandler::handle + */ + public function testHandleNotBubbling() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractProcessingHandler', array(Logger::DEBUG, false)); + $this->assertTrue($handler->handle($this->getRecord())); + } + + /** + * @covers Monolog\Handler\AbstractProcessingHandler::handle + */ + public function testHandleIsFalseWhenNotHandled() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractProcessingHandler', array(Logger::WARNING, false)); + $this->assertTrue($handler->handle($this->getRecord())); + $this->assertFalse($handler->handle($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\AbstractProcessingHandler::processRecord + */ + public function testProcessRecord() + { + $handler = $this->getMockForAbstractClass('Monolog\Handler\AbstractProcessingHandler'); + $handler->pushProcessor(new WebProcessor(array( + 'REQUEST_URI' => '', + 'REQUEST_METHOD' => '', + 'REMOTE_ADDR' => '', + 'SERVER_NAME' => '', + 'UNIQUE_ID' => '', + ))); + $handledRecord = null; + $handler->expects($this->once()) + ->method('write') + ->will($this->returnCallback(function ($record) use (&$handledRecord) { + $handledRecord = $record; + })) + ; + $handler->handle($this->getRecord()); + $this->assertEquals(6, count($handledRecord['extra'])); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.php new file mode 100644 index 0000000..8e0e723 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/AmqpHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use PhpAmqpLib\Message\AMQPMessage; +use PhpAmqpLib\Connection\AMQPConnection; + +/** + * @covers Monolog\Handler\RotatingFileHandler + */ +class AmqpHandlerTest extends TestCase +{ + public function testHandleAmqpExt() + { + if (!class_exists('AMQPConnection') || !class_exists('AMQPExchange')) { + $this->markTestSkipped("amqp-php not installed"); + } + + if (!class_exists('AMQPChannel')) { + $this->markTestSkipped("Please update AMQP to version >= 1.0"); + } + + $messages = array(); + + $exchange = $this->getMock('AMQPExchange', array('publish', 'setName'), array(), '', false); + $exchange->expects($this->once()) + ->method('setName') + ->with('log') + ; + $exchange->expects($this->any()) + ->method('publish') + ->will($this->returnCallback(function ($message, $routing_key, $flags = 0, $attributes = array()) use (&$messages) { + $messages[] = array($message, $routing_key, $flags, $attributes); + })) + ; + + $handler = new AmqpHandler($exchange, 'log'); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $expected = array( + array( + 'message' => 'test', + 'context' => array( + 'data' => array(), + 'foo' => 34, + ), + 'level' => 300, + 'level_name' => 'WARNING', + 'channel' => 'test', + 'extra' => array(), + ), + 'warn.test', + 0, + array( + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ), + ); + + $handler->handle($record); + + $this->assertCount(1, $messages); + $messages[0][0] = json_decode($messages[0][0], true); + unset($messages[0][0]['datetime']); + $this->assertEquals($expected, $messages[0]); + } + + public function testHandlePhpAmqpLib() + { + if (!class_exists('PhpAmqpLib\Connection\AMQPConnection')) { + $this->markTestSkipped("php-amqplib not installed"); + } + + $messages = array(); + + $exchange = $this->getMock('PhpAmqpLib\Channel\AMQPChannel', array('basic_publish', '__destruct'), array(), '', false); + + $exchange->expects($this->any()) + ->method('basic_publish') + ->will($this->returnCallback(function (AMQPMessage $msg, $exchange = "", $routing_key = "", $mandatory = false, $immediate = false, $ticket = null) use (&$messages) { + $messages[] = array($msg, $exchange, $routing_key, $mandatory, $immediate, $ticket); + })) + ; + + $handler = new AmqpHandler($exchange, 'log'); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $expected = array( + array( + 'message' => 'test', + 'context' => array( + 'data' => array(), + 'foo' => 34, + ), + 'level' => 300, + 'level_name' => 'WARNING', + 'channel' => 'test', + 'extra' => array(), + ), + 'log', + 'warn.test', + false, + false, + null, + array( + 'delivery_mode' => 2, + 'content_type' => 'application/json', + ), + ); + + $handler->handle($record); + + $this->assertCount(1, $messages); + + /* @var $msg AMQPMessage */ + $msg = $messages[0][0]; + $messages[0][0] = json_decode($msg->body, true); + $messages[0][] = $msg->get_properties(); + unset($messages[0][0]['datetime']); + + $this->assertEquals($expected, $messages[0]); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php new file mode 100644 index 0000000..ffb1d74 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/BrowserConsoleHandlerTest.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\BrowserConsoleHandlerTest + */ +class BrowserConsoleHandlerTest extends TestCase +{ + protected function setUp() + { + BrowserConsoleHandler::reset(); + } + + protected function generateScript() + { + $reflMethod = new \ReflectionMethod('Monolog\Handler\BrowserConsoleHandler', 'generateScript'); + $reflMethod->setAccessible(true); + + return $reflMethod->invoke(null); + } + + public function testStyling() + { + $handler = new BrowserConsoleHandler(); + $handler->setFormatter($this->getIdentityFormatter()); + + $handler->handle($this->getRecord(Logger::DEBUG, 'foo[[bar]]{color: red}')); + + $expected = <<assertEquals($expected, $this->generateScript()); + } + + public function testEscaping() + { + $handler = new BrowserConsoleHandler(); + $handler->setFormatter($this->getIdentityFormatter()); + + $handler->handle($this->getRecord(Logger::DEBUG, "[foo] [[\"bar\n[baz]\"]]{color: red}")); + + $expected = <<assertEquals($expected, $this->generateScript()); + } + + public function testAutolabel() + { + $handler = new BrowserConsoleHandler(); + $handler->setFormatter($this->getIdentityFormatter()); + + $handler->handle($this->getRecord(Logger::DEBUG, '[[foo]]{macro: autolabel}')); + $handler->handle($this->getRecord(Logger::DEBUG, '[[bar]]{macro: autolabel}')); + $handler->handle($this->getRecord(Logger::DEBUG, '[[foo]]{macro: autolabel}')); + + $expected = <<assertEquals($expected, $this->generateScript()); + } + + public function testContext() + { + $handler = new BrowserConsoleHandler(); + $handler->setFormatter($this->getIdentityFormatter()); + + $handler->handle($this->getRecord(Logger::DEBUG, 'test', array('foo' => 'bar'))); + + $expected = <<assertEquals($expected, $this->generateScript()); + } + + public function testConcurrentHandlers() + { + $handler1 = new BrowserConsoleHandler(); + $handler1->setFormatter($this->getIdentityFormatter()); + + $handler2 = new BrowserConsoleHandler(); + $handler2->setFormatter($this->getIdentityFormatter()); + + $handler1->handle($this->getRecord(Logger::DEBUG, 'test1')); + $handler2->handle($this->getRecord(Logger::DEBUG, 'test2')); + $handler1->handle($this->getRecord(Logger::DEBUG, 'test3')); + $handler2->handle($this->getRecord(Logger::DEBUG, 'test4')); + + $expected = <<assertEquals($expected, $this->generateScript()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php new file mode 100644 index 0000000..da8b3c3 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/BufferHandlerTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class BufferHandlerTest extends TestCase +{ + private $shutdownCheckHandler; + + /** + * @covers Monolog\Handler\BufferHandler::__construct + * @covers Monolog\Handler\BufferHandler::handle + * @covers Monolog\Handler\BufferHandler::close + */ + public function testHandleBuffers() + { + $test = new TestHandler(); + $handler = new BufferHandler($test); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertFalse($test->hasDebugRecords()); + $this->assertFalse($test->hasInfoRecords()); + $handler->close(); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + } + + /** + * @covers Monolog\Handler\BufferHandler::close + * @covers Monolog\Handler\BufferHandler::flush + */ + public function testPropagatesRecordsAtEndOfRequest() + { + $test = new TestHandler(); + $handler = new BufferHandler($test); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->shutdownCheckHandler = $test; + register_shutdown_function(array($this, 'checkPropagation')); + } + + public function checkPropagation() + { + if (!$this->shutdownCheckHandler->hasWarningRecords() || !$this->shutdownCheckHandler->hasDebugRecords()) { + echo '!!! BufferHandlerTest::testPropagatesRecordsAtEndOfRequest failed to verify that the messages have been propagated' . PHP_EOL; + exit(1); + } + } + + /** + * @covers Monolog\Handler\BufferHandler::handle + */ + public function testHandleBufferLimit() + { + $test = new TestHandler(); + $handler = new BufferHandler($test, 2); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->close(); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertFalse($test->hasDebugRecords()); + } + + /** + * @covers Monolog\Handler\BufferHandler::handle + */ + public function testHandleBufferLimitWithFlushOnOverflow() + { + $test = new TestHandler(); + $handler = new BufferHandler($test, 3, Logger::DEBUG, true, true); + + // send two records + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->assertFalse($test->hasDebugRecords()); + $this->assertCount(0, $test->getRecords()); + + // overflow + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertTrue($test->hasDebugRecords()); + $this->assertCount(3, $test->getRecords()); + + // should buffer again + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertCount(3, $test->getRecords()); + + $handler->close(); + $this->assertCount(5, $test->getRecords()); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasInfoRecords()); + } + + /** + * @covers Monolog\Handler\BufferHandler::handle + */ + public function testHandleLevel() + { + $test = new TestHandler(); + $handler = new BufferHandler($test, 0, Logger::INFO); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->close(); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertFalse($test->hasDebugRecords()); + } + + /** + * @covers Monolog\Handler\BufferHandler::flush + */ + public function testFlush() + { + $test = new TestHandler(); + $handler = new BufferHandler($test, 0); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->flush(); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue($test->hasDebugRecords()); + $this->assertFalse($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\BufferHandler::handle + */ + public function testHandleUsesProcessors() + { + $test = new TestHandler(); + $handler = new BufferHandler($test); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->flush(); + $this->assertTrue($test->hasWarningRecords()); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.php new file mode 100644 index 0000000..0449f8b --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/ChromePHPHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\ChromePHPHandler + */ +class ChromePHPHandlerTest extends TestCase +{ + protected function setUp() + { + TestChromePHPHandler::reset(); + $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; Chrome/1.0'; + } + + /** + * @dataProvider agentsProvider + */ + public function testHeaders($agent) + { + $_SERVER['HTTP_USER_AGENT'] = $agent; + + $handler = new TestChromePHPHandler(); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::WARNING)); + + $expected = array( + 'X-ChromeLogger-Data' => base64_encode(utf8_encode(json_encode(array( + 'version' => ChromePHPHandler::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array( + 'test', + 'test', + ), + 'request_uri' => '', + )))), + ); + + $this->assertEquals($expected, $handler->getHeaders()); + } + + public static function agentsProvider() + { + return array( + array('Monolog Test; Chrome/1.0'), + array('Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0'), + array('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/56.0.2924.76 Chrome/56.0.2924.76 Safari/537.36'), + array('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome Safari/537.36'), + ); + } + + public function testHeadersOverflow() + { + $handler = new TestChromePHPHandler(); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::WARNING, str_repeat('a', 150 * 1024))); + + // overflow chrome headers limit + $handler->handle($this->getRecord(Logger::WARNING, str_repeat('a', 100 * 1024))); + + $expected = array( + 'X-ChromeLogger-Data' => base64_encode(utf8_encode(json_encode(array( + 'version' => ChromePHPHandler::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array( + array( + 'test', + 'test', + 'unknown', + 'log', + ), + array( + 'test', + str_repeat('a', 150 * 1024), + 'unknown', + 'warn', + ), + array( + 'monolog', + 'Incomplete logs, chrome header size limit reached', + 'unknown', + 'warn', + ), + ), + 'request_uri' => '', + )))), + ); + + $this->assertEquals($expected, $handler->getHeaders()); + } + + public function testConcurrentHandlers() + { + $handler = new TestChromePHPHandler(); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::WARNING)); + + $handler2 = new TestChromePHPHandler(); + $handler2->setFormatter($this->getIdentityFormatter()); + $handler2->handle($this->getRecord(Logger::DEBUG)); + $handler2->handle($this->getRecord(Logger::WARNING)); + + $expected = array( + 'X-ChromeLogger-Data' => base64_encode(utf8_encode(json_encode(array( + 'version' => ChromePHPHandler::VERSION, + 'columns' => array('label', 'log', 'backtrace', 'type'), + 'rows' => array( + 'test', + 'test', + 'test', + 'test', + ), + 'request_uri' => '', + )))), + ); + + $this->assertEquals($expected, $handler2->getHeaders()); + } +} + +class TestChromePHPHandler extends ChromePHPHandler +{ + protected $headers = array(); + + public static function reset() + { + self::$initialized = false; + self::$overflowed = false; + self::$sendHeaders = true; + self::$json['rows'] = array(); + } + + protected function sendHeader($header, $content) + { + $this->headers[$header] = $content; + } + + public function getHeaders() + { + return $this->headers; + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/CouchDBHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/CouchDBHandlerTest.php new file mode 100644 index 0000000..9fc4b38 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/CouchDBHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class CouchDBHandlerTest extends TestCase +{ + public function testHandle() + { + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $handler = new CouchDBHandler(); + + try { + $handler->handle($record); + } catch (\RuntimeException $e) { + $this->markTestSkipped('Could not connect to couchdb server on http://localhost:5984'); + } + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/DeduplicationHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/DeduplicationHandlerTest.php new file mode 100644 index 0000000..e2aff86 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/DeduplicationHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class DeduplicationHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\DeduplicationHandler::flush + */ + public function testFlushPassthruIfAllRecordsUnderTrigger() + { + $test = new TestHandler(); + @unlink(sys_get_temp_dir().'/monolog_dedup.log'); + $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); + + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + + $handler->flush(); + + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue($test->hasDebugRecords()); + $this->assertFalse($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\DeduplicationHandler::flush + * @covers Monolog\Handler\DeduplicationHandler::appendRecord + */ + public function testFlushPassthruIfEmptyLog() + { + $test = new TestHandler(); + @unlink(sys_get_temp_dir().'/monolog_dedup.log'); + $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); + + $handler->handle($this->getRecord(Logger::ERROR, 'Foo:bar')); + $handler->handle($this->getRecord(Logger::CRITICAL, "Foo\nbar")); + + $handler->flush(); + + $this->assertTrue($test->hasErrorRecords()); + $this->assertTrue($test->hasCriticalRecords()); + $this->assertFalse($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\DeduplicationHandler::flush + * @covers Monolog\Handler\DeduplicationHandler::appendRecord + * @covers Monolog\Handler\DeduplicationHandler::isDuplicate + * @depends testFlushPassthruIfEmptyLog + */ + public function testFlushSkipsIfLogExists() + { + $test = new TestHandler(); + $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); + + $handler->handle($this->getRecord(Logger::ERROR, 'Foo:bar')); + $handler->handle($this->getRecord(Logger::CRITICAL, "Foo\nbar")); + + $handler->flush(); + + $this->assertFalse($test->hasErrorRecords()); + $this->assertFalse($test->hasCriticalRecords()); + $this->assertFalse($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\DeduplicationHandler::flush + * @covers Monolog\Handler\DeduplicationHandler::appendRecord + * @covers Monolog\Handler\DeduplicationHandler::isDuplicate + * @depends testFlushPassthruIfEmptyLog + */ + public function testFlushPassthruIfLogTooOld() + { + $test = new TestHandler(); + $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); + + $record = $this->getRecord(Logger::ERROR); + $record['datetime']->modify('+62seconds'); + $handler->handle($record); + $record = $this->getRecord(Logger::CRITICAL); + $record['datetime']->modify('+62seconds'); + $handler->handle($record); + + $handler->flush(); + + $this->assertTrue($test->hasErrorRecords()); + $this->assertTrue($test->hasCriticalRecords()); + $this->assertFalse($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\DeduplicationHandler::flush + * @covers Monolog\Handler\DeduplicationHandler::appendRecord + * @covers Monolog\Handler\DeduplicationHandler::isDuplicate + * @covers Monolog\Handler\DeduplicationHandler::collectLogs + */ + public function testGcOldLogs() + { + $test = new TestHandler(); + @unlink(sys_get_temp_dir().'/monolog_dedup.log'); + $handler = new DeduplicationHandler($test, sys_get_temp_dir().'/monolog_dedup.log', 0); + + // handle two records from yesterday, and one recent + $record = $this->getRecord(Logger::ERROR); + $record['datetime']->modify('-1day -10seconds'); + $handler->handle($record); + $record2 = $this->getRecord(Logger::CRITICAL); + $record2['datetime']->modify('-1day -10seconds'); + $handler->handle($record2); + $record3 = $this->getRecord(Logger::CRITICAL); + $record3['datetime']->modify('-30seconds'); + $handler->handle($record3); + + // log is written as none of them are duplicate + $handler->flush(); + $this->assertSame( + $record['datetime']->getTimestamp() . ":ERROR:test\n" . + $record2['datetime']->getTimestamp() . ":CRITICAL:test\n" . + $record3['datetime']->getTimestamp() . ":CRITICAL:test\n", + file_get_contents(sys_get_temp_dir() . '/monolog_dedup.log') + ); + $this->assertTrue($test->hasErrorRecords()); + $this->assertTrue($test->hasCriticalRecords()); + $this->assertFalse($test->hasWarningRecords()); + + // clear test handler + $test->clear(); + $this->assertFalse($test->hasErrorRecords()); + $this->assertFalse($test->hasCriticalRecords()); + + // log new records, duplicate log gets GC'd at the end of this flush call + $handler->handle($record = $this->getRecord(Logger::ERROR)); + $handler->handle($record2 = $this->getRecord(Logger::CRITICAL)); + $handler->flush(); + + // log should now contain the new errors and the previous one that was recent enough + $this->assertSame( + $record3['datetime']->getTimestamp() . ":CRITICAL:test\n" . + $record['datetime']->getTimestamp() . ":ERROR:test\n" . + $record2['datetime']->getTimestamp() . ":CRITICAL:test\n", + file_get_contents(sys_get_temp_dir() . '/monolog_dedup.log') + ); + $this->assertTrue($test->hasErrorRecords()); + $this->assertTrue($test->hasCriticalRecords()); + $this->assertFalse($test->hasWarningRecords()); + } + + public static function tearDownAfterClass() + { + @unlink(sys_get_temp_dir().'/monolog_dedup.log'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/DoctrineCouchDBHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/DoctrineCouchDBHandlerTest.php new file mode 100644 index 0000000..d67da90 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/DoctrineCouchDBHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class DoctrineCouchDBHandlerTest extends TestCase +{ + protected function setup() + { + if (!class_exists('Doctrine\CouchDB\CouchDBClient')) { + $this->markTestSkipped('The "doctrine/couchdb" package is not installed'); + } + } + + public function testHandle() + { + $client = $this->getMockBuilder('Doctrine\\CouchDB\\CouchDBClient') + ->setMethods(array('postDocument')) + ->disableOriginalConstructor() + ->getMock(); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $expected = array( + 'message' => 'test', + 'context' => array('data' => '[object] (stdClass: {})', 'foo' => 34), + 'level' => Logger::WARNING, + 'level_name' => 'WARNING', + 'channel' => 'test', + 'datetime' => $record['datetime']->format('Y-m-d H:i:s'), + 'extra' => array(), + ); + + $client->expects($this->once()) + ->method('postDocument') + ->with($expected); + + $handler = new DoctrineCouchDBHandler($client); + $handler->handle($record); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/DynamoDbHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/DynamoDbHandlerTest.php new file mode 100644 index 0000000..2e6c348 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/DynamoDbHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; + +class DynamoDbHandlerTest extends TestCase +{ + private $client; + + public function setUp() + { + if (!class_exists('Aws\DynamoDb\DynamoDbClient')) { + $this->markTestSkipped('aws/aws-sdk-php not installed'); + } + + $this->client = $this->getMockBuilder('Aws\DynamoDb\DynamoDbClient') + ->setMethods(array('formatAttributes', '__call')) + ->disableOriginalConstructor()->getMock(); + } + + public function testConstruct() + { + $this->assertInstanceOf('Monolog\Handler\DynamoDbHandler', new DynamoDbHandler($this->client, 'foo')); + } + + public function testInterface() + { + $this->assertInstanceOf('Monolog\Handler\HandlerInterface', new DynamoDbHandler($this->client, 'foo')); + } + + public function testGetFormatter() + { + $handler = new DynamoDbHandler($this->client, 'foo'); + $this->assertInstanceOf('Monolog\Formatter\ScalarFormatter', $handler->getFormatter()); + } + + public function testHandle() + { + $record = $this->getRecord(); + $formatter = $this->getMock('Monolog\Formatter\FormatterInterface'); + $formatted = array('foo' => 1, 'bar' => 2); + $handler = new DynamoDbHandler($this->client, 'foo'); + $handler->setFormatter($formatter); + + $isV3 = defined('Aws\Sdk::VERSION') && version_compare(\Aws\Sdk::VERSION, '3.0', '>='); + if ($isV3) { + $expFormatted = array('foo' => array('N' => 1), 'bar' => array('N' => 2)); + } else { + $expFormatted = $formatted; + } + + $formatter + ->expects($this->once()) + ->method('format') + ->with($record) + ->will($this->returnValue($formatted)); + $this->client + ->expects($isV3 ? $this->never() : $this->once()) + ->method('formatAttributes') + ->with($this->isType('array')) + ->will($this->returnValue($formatted)); + $this->client + ->expects($this->once()) + ->method('__call') + ->with('putItem', array(array( + 'TableName' => 'foo', + 'Item' => $expFormatted, + ))); + + $handler->handle($record); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/ElasticSearchHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/ElasticSearchHandlerTest.php new file mode 100644 index 0000000..1687074 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/ElasticSearchHandlerTest.php @@ -0,0 +1,239 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\ElasticaFormatter; +use Monolog\Formatter\NormalizerFormatter; +use Monolog\TestCase; +use Monolog\Logger; +use Elastica\Client; +use Elastica\Request; +use Elastica\Response; + +class ElasticSearchHandlerTest extends TestCase +{ + /** + * @var Client mock + */ + protected $client; + + /** + * @var array Default handler options + */ + protected $options = array( + 'index' => 'my_index', + 'type' => 'doc_type', + ); + + public function setUp() + { + // Elastica lib required + if (!class_exists("Elastica\Client")) { + $this->markTestSkipped("ruflin/elastica not installed"); + } + + // base mock Elastica Client object + $this->client = $this->getMockBuilder('Elastica\Client') + ->setMethods(array('addDocuments')) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @covers Monolog\Handler\ElasticSearchHandler::write + * @covers Monolog\Handler\ElasticSearchHandler::handleBatch + * @covers Monolog\Handler\ElasticSearchHandler::bulkSend + * @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter + */ + public function testHandle() + { + // log message + $msg = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('foo' => 7, 'bar', 'class' => new \stdClass), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + // format expected result + $formatter = new ElasticaFormatter($this->options['index'], $this->options['type']); + $expected = array($formatter->format($msg)); + + // setup ES client mock + $this->client->expects($this->any()) + ->method('addDocuments') + ->with($expected); + + // perform tests + $handler = new ElasticSearchHandler($this->client, $this->options); + $handler->handle($msg); + $handler->handleBatch(array($msg)); + } + + /** + * @covers Monolog\Handler\ElasticSearchHandler::setFormatter + */ + public function testSetFormatter() + { + $handler = new ElasticSearchHandler($this->client); + $formatter = new ElasticaFormatter('index_new', 'type_new'); + $handler->setFormatter($formatter); + $this->assertInstanceOf('Monolog\Formatter\ElasticaFormatter', $handler->getFormatter()); + $this->assertEquals('index_new', $handler->getFormatter()->getIndex()); + $this->assertEquals('type_new', $handler->getFormatter()->getType()); + } + + /** + * @covers Monolog\Handler\ElasticSearchHandler::setFormatter + * @expectedException InvalidArgumentException + * @expectedExceptionMessage ElasticSearchHandler is only compatible with ElasticaFormatter + */ + public function testSetFormatterInvalid() + { + $handler = new ElasticSearchHandler($this->client); + $formatter = new NormalizerFormatter(); + $handler->setFormatter($formatter); + } + + /** + * @covers Monolog\Handler\ElasticSearchHandler::__construct + * @covers Monolog\Handler\ElasticSearchHandler::getOptions + */ + public function testOptions() + { + $expected = array( + 'index' => $this->options['index'], + 'type' => $this->options['type'], + 'ignore_error' => false, + ); + $handler = new ElasticSearchHandler($this->client, $this->options); + $this->assertEquals($expected, $handler->getOptions()); + } + + /** + * @covers Monolog\Handler\ElasticSearchHandler::bulkSend + * @dataProvider providerTestConnectionErrors + */ + public function testConnectionErrors($ignore, $expectedError) + { + $clientOpts = array('host' => '127.0.0.1', 'port' => 1); + $client = new Client($clientOpts); + $handlerOpts = array('ignore_error' => $ignore); + $handler = new ElasticSearchHandler($client, $handlerOpts); + + if ($expectedError) { + $this->setExpectedException($expectedError[0], $expectedError[1]); + $handler->handle($this->getRecord()); + } else { + $this->assertFalse($handler->handle($this->getRecord())); + } + } + + /** + * @return array + */ + public function providerTestConnectionErrors() + { + return array( + array(false, array('RuntimeException', 'Error sending messages to Elasticsearch')), + array(true, false), + ); + } + + /** + * Integration test using localhost Elastic Search server + * + * @covers Monolog\Handler\ElasticSearchHandler::__construct + * @covers Monolog\Handler\ElasticSearchHandler::handleBatch + * @covers Monolog\Handler\ElasticSearchHandler::bulkSend + * @covers Monolog\Handler\ElasticSearchHandler::getDefaultFormatter + */ + public function testHandleIntegration() + { + $msg = array( + 'level' => Logger::ERROR, + 'level_name' => 'ERROR', + 'channel' => 'meh', + 'context' => array('foo' => 7, 'bar', 'class' => new \stdClass), + 'datetime' => new \DateTime("@0"), + 'extra' => array(), + 'message' => 'log', + ); + + $expected = $msg; + $expected['datetime'] = $msg['datetime']->format(\DateTime::ISO8601); + $expected['context'] = array( + 'class' => '[object] (stdClass: {})', + 'foo' => 7, + 0 => 'bar', + ); + + $client = new Client(); + $handler = new ElasticSearchHandler($client, $this->options); + try { + $handler->handleBatch(array($msg)); + } catch (\RuntimeException $e) { + $this->markTestSkipped("Cannot connect to Elastic Search server on localhost"); + } + + // check document id from ES server response + $documentId = $this->getCreatedDocId($client->getLastResponse()); + $this->assertNotEmpty($documentId, 'No elastic document id received'); + + // retrieve document source from ES and validate + $document = $this->getDocSourceFromElastic( + $client, + $this->options['index'], + $this->options['type'], + $documentId + ); + $this->assertEquals($expected, $document); + + // remove test index from ES + $client->request("/{$this->options['index']}", Request::DELETE); + } + + /** + * Return last created document id from ES response + * @param Response $response Elastica Response object + * @return string|null + */ + protected function getCreatedDocId(Response $response) + { + $data = $response->getData(); + if (!empty($data['items'][0]['create']['_id'])) { + return $data['items'][0]['create']['_id']; + } + } + + /** + * Retrieve document by id from Elasticsearch + * @param Client $client Elastica client + * @param string $index + * @param string $type + * @param string $documentId + * @return array + */ + protected function getDocSourceFromElastic(Client $client, $index, $type, $documentId) + { + $resp = $client->request("/{$index}/{$type}/{$documentId}", Request::GET); + $data = $resp->getData(); + if (!empty($data['_source'])) { + return $data['_source']; + } + + return array(); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/ErrorLogHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/ErrorLogHandlerTest.php new file mode 100644 index 0000000..99785cb --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/ErrorLogHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +function error_log() +{ + $GLOBALS['error_log'][] = func_get_args(); +} + +class ErrorLogHandlerTest extends TestCase +{ + protected function setUp() + { + $GLOBALS['error_log'] = array(); + } + + /** + * @covers Monolog\Handler\ErrorLogHandler::__construct + * @expectedException InvalidArgumentException + * @expectedExceptionMessage The given message type "42" is not supported + */ + public function testShouldNotAcceptAnInvalidTypeOnContructor() + { + new ErrorLogHandler(42); + } + + /** + * @covers Monolog\Handler\ErrorLogHandler::write + */ + public function testShouldLogMessagesUsingErrorLogFuncion() + { + $type = ErrorLogHandler::OPERATING_SYSTEM; + $handler = new ErrorLogHandler($type); + $handler->setFormatter(new LineFormatter('%channel%.%level_name%: %message% %context% %extra%', null, true)); + $handler->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz")); + + $this->assertSame("test.ERROR: Foo\nBar\r\n\r\nBaz [] []", $GLOBALS['error_log'][0][0]); + $this->assertSame($GLOBALS['error_log'][0][1], $type); + + $handler = new ErrorLogHandler($type, Logger::DEBUG, true, true); + $handler->setFormatter(new LineFormatter(null, null, true)); + $handler->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz")); + + $this->assertStringMatchesFormat('[%s] test.ERROR: Foo', $GLOBALS['error_log'][1][0]); + $this->assertSame($GLOBALS['error_log'][1][1], $type); + + $this->assertStringMatchesFormat('Bar', $GLOBALS['error_log'][2][0]); + $this->assertSame($GLOBALS['error_log'][2][1], $type); + + $this->assertStringMatchesFormat('Baz [] []', $GLOBALS['error_log'][3][0]); + $this->assertSame($GLOBALS['error_log'][3][1], $type); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FilterHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FilterHandlerTest.php new file mode 100644 index 0000000..31b7686 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FilterHandlerTest.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\TestCase; + +class FilterHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\FilterHandler::isHandling + */ + public function testIsHandling() + { + $test = new TestHandler(); + $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::INFO))); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::NOTICE))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::WARNING))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::ERROR))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::CRITICAL))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::ALERT))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::EMERGENCY))); + } + + /** + * @covers Monolog\Handler\FilterHandler::handle + * @covers Monolog\Handler\FilterHandler::setAcceptedLevels + * @covers Monolog\Handler\FilterHandler::isHandling + */ + public function testHandleProcessOnlyNeededLevels() + { + $test = new TestHandler(); + $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE); + + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->assertFalse($test->hasDebugRecords()); + + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertTrue($test->hasInfoRecords()); + $handler->handle($this->getRecord(Logger::NOTICE)); + $this->assertTrue($test->hasNoticeRecords()); + + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertFalse($test->hasWarningRecords()); + $handler->handle($this->getRecord(Logger::ERROR)); + $this->assertFalse($test->hasErrorRecords()); + $handler->handle($this->getRecord(Logger::CRITICAL)); + $this->assertFalse($test->hasCriticalRecords()); + $handler->handle($this->getRecord(Logger::ALERT)); + $this->assertFalse($test->hasAlertRecords()); + $handler->handle($this->getRecord(Logger::EMERGENCY)); + $this->assertFalse($test->hasEmergencyRecords()); + + $test = new TestHandler(); + $handler = new FilterHandler($test, array(Logger::INFO, Logger::ERROR)); + + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->assertFalse($test->hasDebugRecords()); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertTrue($test->hasInfoRecords()); + $handler->handle($this->getRecord(Logger::NOTICE)); + $this->assertFalse($test->hasNoticeRecords()); + $handler->handle($this->getRecord(Logger::ERROR)); + $this->assertTrue($test->hasErrorRecords()); + $handler->handle($this->getRecord(Logger::CRITICAL)); + $this->assertFalse($test->hasCriticalRecords()); + } + + /** + * @covers Monolog\Handler\FilterHandler::setAcceptedLevels + * @covers Monolog\Handler\FilterHandler::getAcceptedLevels + */ + public function testAcceptedLevelApi() + { + $test = new TestHandler(); + $handler = new FilterHandler($test); + + $levels = array(Logger::INFO, Logger::ERROR); + $handler->setAcceptedLevels($levels); + $this->assertSame($levels, $handler->getAcceptedLevels()); + + $handler->setAcceptedLevels(array('info', 'error')); + $this->assertSame($levels, $handler->getAcceptedLevels()); + + $levels = array(Logger::CRITICAL, Logger::ALERT, Logger::EMERGENCY); + $handler->setAcceptedLevels(Logger::CRITICAL, Logger::EMERGENCY); + $this->assertSame($levels, $handler->getAcceptedLevels()); + + $handler->setAcceptedLevels('critical', 'emergency'); + $this->assertSame($levels, $handler->getAcceptedLevels()); + } + + /** + * @covers Monolog\Handler\FilterHandler::handle + */ + public function testHandleUsesProcessors() + { + $test = new TestHandler(); + $handler = new FilterHandler($test, Logger::DEBUG, Logger::EMERGENCY); + $handler->pushProcessor( + function ($record) { + $record['extra']['foo'] = true; + + return $record; + } + ); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasWarningRecords()); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + } + + /** + * @covers Monolog\Handler\FilterHandler::handle + */ + public function testHandleRespectsBubble() + { + $test = new TestHandler(); + + $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE, false); + $this->assertTrue($handler->handle($this->getRecord(Logger::INFO))); + $this->assertFalse($handler->handle($this->getRecord(Logger::WARNING))); + + $handler = new FilterHandler($test, Logger::INFO, Logger::NOTICE, true); + $this->assertFalse($handler->handle($this->getRecord(Logger::INFO))); + $this->assertFalse($handler->handle($this->getRecord(Logger::WARNING))); + } + + /** + * @covers Monolog\Handler\FilterHandler::handle + */ + public function testHandleWithCallback() + { + $test = new TestHandler(); + $handler = new FilterHandler( + function ($record, $handler) use ($test) { + return $test; + }, Logger::INFO, Logger::NOTICE, false + ); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertFalse($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + } + + /** + * @covers Monolog\Handler\FilterHandler::handle + * @expectedException \RuntimeException + */ + public function testHandleWithBadCallbackThrowsException() + { + $handler = new FilterHandler( + function ($record, $handler) { + return 'foo'; + } + ); + $handler->handle($this->getRecord(Logger::WARNING)); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php new file mode 100644 index 0000000..b92bf43 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FingersCrossedHandlerTest.php @@ -0,0 +1,279 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy; +use Psr\Log\LogLevel; + +class FingersCrossedHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\FingersCrossedHandler::__construct + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testHandleBuffers() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertFalse($test->hasDebugRecords()); + $this->assertFalse($test->hasInfoRecords()); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->close(); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 3); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testHandleStopsBufferingAfterTrigger() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->close(); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasDebugRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + * @covers Monolog\Handler\FingersCrossedHandler::reset + */ + public function testHandleRestartBufferingAfterReset() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->reset(); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->close(); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasDebugRecords()); + $this->assertFalse($test->hasInfoRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testHandleRestartBufferingAfterBeingTriggeredWhenStopBufferingIsDisabled() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, Logger::WARNING, 0, false, false); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->close(); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasDebugRecords()); + $this->assertFalse($test->hasInfoRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testHandleBufferLimit() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, Logger::WARNING, 2); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasWarningRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertFalse($test->hasDebugRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testHandleWithCallback() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler(function ($record, $handler) use ($test) { + return $test; + }); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertFalse($test->hasDebugRecords()); + $this->assertFalse($test->hasInfoRecords()); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 3); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + * @expectedException RuntimeException + */ + public function testHandleWithBadCallbackThrowsException() + { + $handler = new FingersCrossedHandler(function ($record, $handler) { + return 'foo'; + }); + $handler->handle($this->getRecord(Logger::WARNING)); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::isHandling + */ + public function testIsHandlingAlways() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, Logger::ERROR); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::__construct + * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::__construct + * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::isHandlerActivated + */ + public function testErrorLevelActivationStrategy() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING)); + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->assertFalse($test->hasDebugRecords()); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::__construct + * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::__construct + * @covers Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy::isHandlerActivated + */ + public function testErrorLevelActivationStrategyWithPsrLevel() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy('warning')); + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->assertFalse($test->hasDebugRecords()); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::__construct + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testOverrideActivationStrategy() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy('warning')); + $handler->handle($this->getRecord(Logger::DEBUG)); + $this->assertFalse($test->hasDebugRecords()); + $handler->activate(); + $this->assertTrue($test->hasDebugRecords()); + $handler->handle($this->getRecord(Logger::INFO)); + $this->assertTrue($test->hasInfoRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::__construct + * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::isHandlerActivated + */ + public function testChannelLevelActivationStrategy() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ChannelLevelActivationStrategy(Logger::ERROR, array('othertest' => Logger::DEBUG))); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertFalse($test->hasWarningRecords()); + $record = $this->getRecord(Logger::DEBUG); + $record['channel'] = 'othertest'; + $handler->handle($record); + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::__construct + * @covers Monolog\Handler\FingersCrossed\ChannelLevelActivationStrategy::isHandlerActivated + */ + public function testChannelLevelActivationStrategyWithPsrLevels() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ChannelLevelActivationStrategy('error', array('othertest' => 'debug'))); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertFalse($test->hasWarningRecords()); + $record = $this->getRecord(Logger::DEBUG); + $record['channel'] = 'othertest'; + $handler->handle($record); + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasWarningRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::handle + * @covers Monolog\Handler\FingersCrossedHandler::activate + */ + public function testHandleUsesProcessors() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, Logger::INFO); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasWarningRecords()); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::close + */ + public function testPassthruOnClose() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING), 0, true, true, Logger::INFO); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->close(); + $this->assertFalse($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + } + + /** + * @covers Monolog\Handler\FingersCrossedHandler::close + */ + public function testPsrLevelPassthruOnClose() + { + $test = new TestHandler(); + $handler = new FingersCrossedHandler($test, new ErrorLevelActivationStrategy(Logger::WARNING), 0, true, true, LogLevel::INFO); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + $handler->close(); + $this->assertFalse($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.php new file mode 100644 index 0000000..0eb10a6 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FirePHPHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\FirePHPHandler + */ +class FirePHPHandlerTest extends TestCase +{ + public function setUp() + { + TestFirePHPHandler::reset(); + $_SERVER['HTTP_USER_AGENT'] = 'Monolog Test; FirePHP/1.0'; + } + + public function testHeaders() + { + $handler = new TestFirePHPHandler; + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::WARNING)); + + $expected = array( + 'X-Wf-Protocol-1' => 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2', + 'X-Wf-1-Structure-1' => 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1', + 'X-Wf-1-Plugin-1' => 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3', + 'X-Wf-1-1-1-1' => 'test', + 'X-Wf-1-1-1-2' => 'test', + ); + + $this->assertEquals($expected, $handler->getHeaders()); + } + + public function testConcurrentHandlers() + { + $handler = new TestFirePHPHandler; + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::WARNING)); + + $handler2 = new TestFirePHPHandler; + $handler2->setFormatter($this->getIdentityFormatter()); + $handler2->handle($this->getRecord(Logger::DEBUG)); + $handler2->handle($this->getRecord(Logger::WARNING)); + + $expected = array( + 'X-Wf-Protocol-1' => 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2', + 'X-Wf-1-Structure-1' => 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1', + 'X-Wf-1-Plugin-1' => 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3', + 'X-Wf-1-1-1-1' => 'test', + 'X-Wf-1-1-1-2' => 'test', + ); + + $expected2 = array( + 'X-Wf-1-1-1-3' => 'test', + 'X-Wf-1-1-1-4' => 'test', + ); + + $this->assertEquals($expected, $handler->getHeaders()); + $this->assertEquals($expected2, $handler2->getHeaders()); + } +} + +class TestFirePHPHandler extends FirePHPHandler +{ + protected $headers = array(); + + public static function reset() + { + self::$initialized = false; + self::$sendHeaders = true; + self::$messageIndex = 1; + } + + protected function sendHeader($header, $content) + { + $this->headers[$header] = $content; + } + + public function getHeaders() + { + return $this->headers; + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/Fixtures/.gitkeep b/vendor/monolog/monolog/tests/Monolog/Handler/Fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FleepHookHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FleepHookHandlerTest.php new file mode 100644 index 0000000..91cdd31 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FleepHookHandlerTest.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 Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Logger; +use Monolog\TestCase; + +/** + * @coversDefaultClass \Monolog\Handler\FleepHookHandler + */ +class FleepHookHandlerTest extends TestCase +{ + /** + * Default token to use in tests + */ + const TOKEN = '123abc'; + + /** + * @var FleepHookHandler + */ + private $handler; + + public function setUp() + { + parent::setUp(); + + if (!extension_loaded('openssl')) { + $this->markTestSkipped('This test requires openssl extension to run'); + } + + // Create instances of the handler and logger for convenience + $this->handler = new FleepHookHandler(self::TOKEN); + } + + /** + * @covers ::__construct + */ + public function testConstructorSetsExpectedDefaults() + { + $this->assertEquals(Logger::DEBUG, $this->handler->getLevel()); + $this->assertEquals(true, $this->handler->getBubble()); + } + + /** + * @covers ::getDefaultFormatter + */ + public function testHandlerUsesLineFormatterWhichIgnoresEmptyArrays() + { + $record = array( + 'message' => 'msg', + 'context' => array(), + 'level' => Logger::DEBUG, + 'level_name' => Logger::getLevelName(Logger::DEBUG), + 'channel' => 'channel', + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + + $expectedFormatter = new LineFormatter(null, null, true, true); + $expected = $expectedFormatter->format($record); + + $handlerFormatter = $this->handler->getFormatter(); + $actual = $handlerFormatter->format($record); + + $this->assertEquals($expected, $actual, 'Empty context and extra arrays should not be rendered'); + } + + /** + * @covers ::__construct + */ + public function testConnectionStringisConstructedCorrectly() + { + $this->assertEquals('ssl://' . FleepHookHandler::FLEEP_HOST . ':443', $this->handler->getConnectionString()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/FlowdockHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/FlowdockHandlerTest.php new file mode 100644 index 0000000..4b120d5 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/FlowdockHandlerTest.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 Monolog\Handler; + +use Monolog\Formatter\FlowdockFormatter; +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @author Dominik Liebler + * @see https://www.hipchat.com/docs/api + */ +class FlowdockHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $res; + + /** + * @var FlowdockHandler + */ + private $handler; + + public function setUp() + { + if (!extension_loaded('openssl')) { + $this->markTestSkipped('This test requires openssl to run'); + } + } + + public function testWriteHeader() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v1\/messages\/team_inbox\/.* HTTP\/1.1\\r\\nHost: api.flowdock.com\\r\\nContent-Type: application\/json\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + /** + * @depends testWriteHeader + */ + public function testWriteContent($content) + { + $this->assertRegexp('/"source":"test_source"/', $content); + $this->assertRegexp('/"from_address":"source@test\.com"/', $content); + } + + private function createHandler($token = 'myToken') + { + $constructorArgs = array($token, Logger::DEBUG); + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\FlowdockHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $constructorArgs + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + + $this->handler->setFormatter(new FlowdockFormatter('test_source', 'source@test.com')); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerLegacyTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerLegacyTest.php new file mode 100644 index 0000000..9d007b1 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerLegacyTest.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 Monolog\Handler; + +use Gelf\Message; +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; + +class GelfHandlerLegacyTest extends TestCase +{ + public function setUp() + { + if (!class_exists('Gelf\MessagePublisher') || !class_exists('Gelf\Message')) { + $this->markTestSkipped("mlehner/gelf-php not installed"); + } + + require_once __DIR__ . '/GelfMockMessagePublisher.php'; + } + + /** + * @covers Monolog\Handler\GelfHandler::__construct + */ + public function testConstruct() + { + $handler = new GelfHandler($this->getMessagePublisher()); + $this->assertInstanceOf('Monolog\Handler\GelfHandler', $handler); + } + + protected function getHandler($messagePublisher) + { + $handler = new GelfHandler($messagePublisher); + + return $handler; + } + + protected function getMessagePublisher() + { + return new GelfMockMessagePublisher('localhost'); + } + + public function testDebug() + { + $messagePublisher = $this->getMessagePublisher(); + $handler = $this->getHandler($messagePublisher); + + $record = $this->getRecord(Logger::DEBUG, "A test debug message"); + $handler->handle($record); + + $this->assertEquals(7, $messagePublisher->lastMessage->getLevel()); + $this->assertEquals('test', $messagePublisher->lastMessage->getFacility()); + $this->assertEquals($record['message'], $messagePublisher->lastMessage->getShortMessage()); + $this->assertEquals(null, $messagePublisher->lastMessage->getFullMessage()); + } + + public function testWarning() + { + $messagePublisher = $this->getMessagePublisher(); + $handler = $this->getHandler($messagePublisher); + + $record = $this->getRecord(Logger::WARNING, "A test warning message"); + $handler->handle($record); + + $this->assertEquals(4, $messagePublisher->lastMessage->getLevel()); + $this->assertEquals('test', $messagePublisher->lastMessage->getFacility()); + $this->assertEquals($record['message'], $messagePublisher->lastMessage->getShortMessage()); + $this->assertEquals(null, $messagePublisher->lastMessage->getFullMessage()); + } + + public function testInjectedGelfMessageFormatter() + { + $messagePublisher = $this->getMessagePublisher(); + $handler = $this->getHandler($messagePublisher); + + $handler->setFormatter(new GelfMessageFormatter('mysystem', 'EXT', 'CTX')); + + $record = $this->getRecord(Logger::WARNING, "A test warning message"); + $record['extra']['blarg'] = 'yep'; + $record['context']['from'] = 'logger'; + $handler->handle($record); + + $this->assertEquals('mysystem', $messagePublisher->lastMessage->getHost()); + $this->assertArrayHasKey('_EXTblarg', $messagePublisher->lastMessage->toArray()); + $this->assertArrayHasKey('_CTXfrom', $messagePublisher->lastMessage->toArray()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerTest.php new file mode 100644 index 0000000..8cdd64f --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/GelfHandlerTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Gelf\Message; +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\GelfMessageFormatter; + +class GelfHandlerTest extends TestCase +{ + public function setUp() + { + if (!class_exists('Gelf\Publisher') || !class_exists('Gelf\Message')) { + $this->markTestSkipped("graylog2/gelf-php not installed"); + } + } + + /** + * @covers Monolog\Handler\GelfHandler::__construct + */ + public function testConstruct() + { + $handler = new GelfHandler($this->getMessagePublisher()); + $this->assertInstanceOf('Monolog\Handler\GelfHandler', $handler); + } + + protected function getHandler($messagePublisher) + { + $handler = new GelfHandler($messagePublisher); + + return $handler; + } + + protected function getMessagePublisher() + { + return $this->getMock('Gelf\Publisher', array('publish'), array(), '', false); + } + + public function testDebug() + { + $record = $this->getRecord(Logger::DEBUG, "A test debug message"); + $expectedMessage = new Message(); + $expectedMessage + ->setLevel(7) + ->setFacility("test") + ->setShortMessage($record['message']) + ->setTimestamp($record['datetime']) + ; + + $messagePublisher = $this->getMessagePublisher(); + $messagePublisher->expects($this->once()) + ->method('publish') + ->with($expectedMessage); + + $handler = $this->getHandler($messagePublisher); + + $handler->handle($record); + } + + public function testWarning() + { + $record = $this->getRecord(Logger::WARNING, "A test warning message"); + $expectedMessage = new Message(); + $expectedMessage + ->setLevel(4) + ->setFacility("test") + ->setShortMessage($record['message']) + ->setTimestamp($record['datetime']) + ; + + $messagePublisher = $this->getMessagePublisher(); + $messagePublisher->expects($this->once()) + ->method('publish') + ->with($expectedMessage); + + $handler = $this->getHandler($messagePublisher); + + $handler->handle($record); + } + + public function testInjectedGelfMessageFormatter() + { + $record = $this->getRecord(Logger::WARNING, "A test warning message"); + $record['extra']['blarg'] = 'yep'; + $record['context']['from'] = 'logger'; + + $expectedMessage = new Message(); + $expectedMessage + ->setLevel(4) + ->setFacility("test") + ->setHost("mysystem") + ->setShortMessage($record['message']) + ->setTimestamp($record['datetime']) + ->setAdditional("EXTblarg", 'yep') + ->setAdditional("CTXfrom", 'logger') + ; + + $messagePublisher = $this->getMessagePublisher(); + $messagePublisher->expects($this->once()) + ->method('publish') + ->with($expectedMessage); + + $handler = $this->getHandler($messagePublisher); + $handler->setFormatter(new GelfMessageFormatter('mysystem', 'EXT', 'CTX')); + $handler->handle($record); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/GelfMockMessagePublisher.php b/vendor/monolog/monolog/tests/Monolog/Handler/GelfMockMessagePublisher.php new file mode 100644 index 0000000..873d92f --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/GelfMockMessagePublisher.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 Monolog\Handler; + +use Gelf\MessagePublisher; +use Gelf\Message; + +class GelfMockMessagePublisher extends MessagePublisher +{ + public function publish(Message $message) + { + $this->lastMessage = $message; + } + + public $lastMessage = null; +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.php new file mode 100644 index 0000000..a1b8617 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/GroupHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class GroupHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\GroupHandler::__construct + * @expectedException InvalidArgumentException + */ + public function testConstructorOnlyTakesHandler() + { + new GroupHandler(array(new TestHandler(), "foo")); + } + + /** + * @covers Monolog\Handler\GroupHandler::__construct + * @covers Monolog\Handler\GroupHandler::handle + */ + public function testHandle() + { + $testHandlers = array(new TestHandler(), new TestHandler()); + $handler = new GroupHandler($testHandlers); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + foreach ($testHandlers as $test) { + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + } + } + + /** + * @covers Monolog\Handler\GroupHandler::handleBatch + */ + public function testHandleBatch() + { + $testHandlers = array(new TestHandler(), new TestHandler()); + $handler = new GroupHandler($testHandlers); + $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); + foreach ($testHandlers as $test) { + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + } + } + + /** + * @covers Monolog\Handler\GroupHandler::isHandling + */ + public function testIsHandling() + { + $testHandlers = array(new TestHandler(Logger::ERROR), new TestHandler(Logger::WARNING)); + $handler = new GroupHandler($testHandlers); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::ERROR))); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::WARNING))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\GroupHandler::handle + */ + public function testHandleUsesProcessors() + { + $test = new TestHandler(); + $handler = new GroupHandler(array($test)); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasWarningRecords()); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + } + + /** + * @covers Monolog\Handler\GroupHandler::handle + */ + public function testHandleBatchUsesProcessors() + { + $testHandlers = array(new TestHandler(), new TestHandler()); + $handler = new GroupHandler($testHandlers); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); + foreach ($testHandlers as $test) { + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + $this->assertTrue($records[1]['extra']['foo']); + } + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/HandlerWrapperTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/HandlerWrapperTest.php new file mode 100644 index 0000000..d8d0452 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/HandlerWrapperTest.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; + +/** + * @author Alexey Karapetov + */ +class HandlerWrapperTest extends TestCase +{ + /** + * @var HandlerWrapper + */ + private $wrapper; + + private $handler; + + public function setUp() + { + parent::setUp(); + $this->handler = $this->getMock('Monolog\\Handler\\HandlerInterface'); + $this->wrapper = new HandlerWrapper($this->handler); + } + + /** + * @return array + */ + public function trueFalseDataProvider() + { + return array( + array(true), + array(false), + ); + } + + /** + * @param $result + * @dataProvider trueFalseDataProvider + */ + public function testIsHandling($result) + { + $record = $this->getRecord(); + $this->handler->expects($this->once()) + ->method('isHandling') + ->with($record) + ->willReturn($result); + + $this->assertEquals($result, $this->wrapper->isHandling($record)); + } + + /** + * @param $result + * @dataProvider trueFalseDataProvider + */ + public function testHandle($result) + { + $record = $this->getRecord(); + $this->handler->expects($this->once()) + ->method('handle') + ->with($record) + ->willReturn($result); + + $this->assertEquals($result, $this->wrapper->handle($record)); + } + + /** + * @param $result + * @dataProvider trueFalseDataProvider + */ + public function testHandleBatch($result) + { + $records = $this->getMultipleRecords(); + $this->handler->expects($this->once()) + ->method('handleBatch') + ->with($records) + ->willReturn($result); + + $this->assertEquals($result, $this->wrapper->handleBatch($records)); + } + + public function testPushProcessor() + { + $processor = function () {}; + $this->handler->expects($this->once()) + ->method('pushProcessor') + ->with($processor); + + $this->assertEquals($this->wrapper, $this->wrapper->pushProcessor($processor)); + } + + public function testPopProcessor() + { + $processor = function () {}; + $this->handler->expects($this->once()) + ->method('popProcessor') + ->willReturn($processor); + + $this->assertEquals($processor, $this->wrapper->popProcessor()); + } + + public function testSetFormatter() + { + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $this->handler->expects($this->once()) + ->method('setFormatter') + ->with($formatter); + + $this->assertEquals($this->wrapper, $this->wrapper->setFormatter($formatter)); + } + + public function testGetFormatter() + { + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $this->handler->expects($this->once()) + ->method('getFormatter') + ->willReturn($formatter); + + $this->assertEquals($formatter, $this->wrapper->getFormatter()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/HipChatHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/HipChatHandlerTest.php new file mode 100644 index 0000000..52dc9da --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/HipChatHandlerTest.php @@ -0,0 +1,279 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @author Rafael Dohms + * @see https://www.hipchat.com/docs/api + */ +class HipChatHandlerTest extends TestCase +{ + private $res; + /** @var HipChatHandler */ + private $handler; + + public function testWriteHeader() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v1\/rooms\/message\?format=json&auth_token=.* HTTP\/1.1\\r\\nHost: api.hipchat.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + public function testWriteCustomHostHeader() + { + $this->createHandler('myToken', 'room1', 'Monolog', true, 'hipchat.foo.bar'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v1\/rooms\/message\?format=json&auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + public function testWriteV2() + { + $this->createHandler('myToken', 'room1', 'Monolog', false, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + public function testWriteV2Notify() + { + $this->createHandler('myToken', 'room1', 'Monolog', true, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room1\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + public function testRoomSpaces() + { + $this->createHandler('myToken', 'room name', 'Monolog', false, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/v2\/room\/room%20name\/notification\?auth_token=.* HTTP\/1.1\\r\\nHost: hipchat.foo.bar\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + /** + * @depends testWriteHeader + */ + public function testWriteContent($content) + { + $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); + } + + public function testWriteContentV1WithoutName() + { + $this->createHandler('myToken', 'room1', null, false, 'hipchat.foo.bar', 'v1'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/notify=0&message=test1&message_format=text&color=red&room_id=room1&from=$/', $content); + + return $content; + } + + /** + * @depends testWriteCustomHostHeader + */ + public function testWriteContentNotify($content) + { + $this->assertRegexp('/notify=1&message=test1&message_format=text&color=red&room_id=room1&from=Monolog$/', $content); + } + + /** + * @depends testWriteV2 + */ + public function testWriteContentV2($content) + { + $this->assertRegexp('/notify=false&message=test1&message_format=text&color=red&from=Monolog$/', $content); + } + + /** + * @depends testWriteV2Notify + */ + public function testWriteContentV2Notify($content) + { + $this->assertRegexp('/notify=true&message=test1&message_format=text&color=red&from=Monolog$/', $content); + } + + public function testWriteContentV2WithoutName() + { + $this->createHandler('myToken', 'room1', null, false, 'hipchat.foo.bar', 'v2'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/notify=false&message=test1&message_format=text&color=red$/', $content); + + return $content; + } + + public function testWriteWithComplexMessage() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Backup of database "example" finished in 16 minutes.')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/message=Backup\+of\+database\+%22example%22\+finished\+in\+16\+minutes\./', $content); + } + + public function testWriteTruncatesLongMessage() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, str_repeat('abcde', 2000))); + fseek($this->res, 0); + $content = fread($this->res, 12000); + + $this->assertRegexp('/message='.str_repeat('abcde', 1900).'\+%5Btruncated%5D/', $content); + } + + /** + * @dataProvider provideLevelColors + */ + public function testWriteWithErrorLevelsAndColors($level, $expectedColor) + { + $this->createHandler(); + $this->handler->handle($this->getRecord($level, 'Backup of database "example" finished in 16 minutes.')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/color='.$expectedColor.'/', $content); + } + + public function provideLevelColors() + { + return array( + array(Logger::DEBUG, 'gray'), + array(Logger::INFO, 'green'), + array(Logger::WARNING, 'yellow'), + array(Logger::ERROR, 'red'), + array(Logger::CRITICAL, 'red'), + array(Logger::ALERT, 'red'), + array(Logger::EMERGENCY,'red'), + array(Logger::NOTICE, 'green'), + ); + } + + /** + * @dataProvider provideBatchRecords + */ + public function testHandleBatch($records, $expectedColor) + { + $this->createHandler(); + + $this->handler->handleBatch($records); + + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/color='.$expectedColor.'/', $content); + } + + public function provideBatchRecords() + { + return array( + array( + array( + array('level' => Logger::WARNING, 'message' => 'Oh bugger!', 'level_name' => 'warning', 'datetime' => new \DateTime()), + array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()), + array('level' => Logger::CRITICAL, 'message' => 'Everything is broken!', 'level_name' => 'critical', 'datetime' => new \DateTime()), + ), + 'red', + ), + array( + array( + array('level' => Logger::WARNING, 'message' => 'Oh bugger!', 'level_name' => 'warning', 'datetime' => new \DateTime()), + array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()), + ), + 'yellow', + ), + array( + array( + array('level' => Logger::DEBUG, 'message' => 'Just debugging.', 'level_name' => 'debug', 'datetime' => new \DateTime()), + array('level' => Logger::NOTICE, 'message' => 'Something noticeable happened.', 'level_name' => 'notice', 'datetime' => new \DateTime()), + ), + 'green', + ), + array( + array( + array('level' => Logger::DEBUG, 'message' => 'Just debugging.', 'level_name' => 'debug', 'datetime' => new \DateTime()), + ), + 'gray', + ), + ); + } + + private function createHandler($token = 'myToken', $room = 'room1', $name = 'Monolog', $notify = false, $host = 'api.hipchat.com', $version = 'v1') + { + $constructorArgs = array($token, $room, $name, $notify, Logger::DEBUG, true, true, 'text', $host, $version); + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\HipChatHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $constructorArgs + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + + $this->handler->setFormatter($this->getIdentityFormatter()); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testCreateWithTooLongName() + { + $hipChatHandler = new HipChatHandler('token', 'room', 'SixteenCharsHere'); + } + + public function testCreateWithTooLongNameV2() + { + // creating a handler with too long of a name but using the v2 api doesn't matter. + $hipChatHandler = new HipChatHandler('token', 'room', 'SixteenCharsHere', false, Logger::CRITICAL, true, true, 'test', 'api.hipchat.com', 'v2'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/LogEntriesHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/LogEntriesHandlerTest.php new file mode 100644 index 0000000..b2deb40 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/LogEntriesHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @author Robert Kaufmann III + */ +class LogEntriesHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $res; + + /** + * @var LogEntriesHandler + */ + private $handler; + + public function testWriteContent() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Critical write test')); + + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] test.CRITICAL: Critical write test/', $content); + } + + public function testWriteBatchContent() + { + $records = array( + $this->getRecord(), + $this->getRecord(), + $this->getRecord(), + ); + $this->createHandler(); + $this->handler->handleBatch($records); + + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/(testToken \[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] .* \[\] \[\]\n){3}/', $content); + } + + private function createHandler() + { + $useSSL = extension_loaded('openssl'); + $args = array('testToken', $useSSL, Logger::DEBUG, true); + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\LogEntriesHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $args + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/MailHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/MailHandlerTest.php new file mode 100644 index 0000000..6754f3d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/MailHandlerTest.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\TestCase; + +class MailHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\MailHandler::handleBatch + */ + public function testHandleBatch() + { + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $formatter->expects($this->once()) + ->method('formatBatch'); // Each record is formatted + + $handler = $this->getMockForAbstractClass('Monolog\\Handler\\MailHandler'); + $handler->expects($this->once()) + ->method('send'); + $handler->expects($this->never()) + ->method('write'); // write is for individual records + + $handler->setFormatter($formatter); + + $handler->handleBatch($this->getMultipleRecords()); + } + + /** + * @covers Monolog\Handler\MailHandler::handleBatch + */ + public function testHandleBatchNotSendsMailIfMessagesAreBelowLevel() + { + $records = array( + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information'), + ); + + $handler = $this->getMockForAbstractClass('Monolog\\Handler\\MailHandler'); + $handler->expects($this->never()) + ->method('send'); + $handler->setLevel(Logger::ERROR); + + $handler->handleBatch($records); + } + + /** + * @covers Monolog\Handler\MailHandler::write + */ + public function testHandle() + { + $handler = $this->getMockForAbstractClass('Monolog\\Handler\\MailHandler'); + + $record = $this->getRecord(); + $records = array($record); + $records[0]['formatted'] = '['.$record['datetime']->format('Y-m-d H:i:s').'] test.WARNING: test [] []'."\n"; + + $handler->expects($this->once()) + ->method('send') + ->with($records[0]['formatted'], $records); + + $handler->handle($record); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/MockRavenClient.php b/vendor/monolog/monolog/tests/Monolog/Handler/MockRavenClient.php new file mode 100644 index 0000000..a083322 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/MockRavenClient.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 Monolog\Handler; + +use Raven_Client; + +class MockRavenClient extends Raven_Client +{ + public function capture($data, $stack, $vars = null) + { + $data = array_merge($this->get_user_data(), $data); + $this->lastData = $data; + $this->lastStack = $stack; + } + + public $lastData; + public $lastStack; +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/MongoDBHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/MongoDBHandlerTest.php new file mode 100644 index 0000000..0fdef63 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/MongoDBHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class MongoDBHandlerTest extends TestCase +{ + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorShouldThrowExceptionForInvalidMongo() + { + new MongoDBHandler(new \stdClass(), 'DB', 'Collection'); + } + + public function testHandle() + { + $mongo = $this->getMock('Mongo', array('selectCollection'), array(), '', false); + $collection = $this->getMock('stdClass', array('save')); + + $mongo->expects($this->once()) + ->method('selectCollection') + ->with('DB', 'Collection') + ->will($this->returnValue($collection)); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $expected = array( + 'message' => 'test', + 'context' => array('data' => '[object] (stdClass: {})', 'foo' => 34), + 'level' => Logger::WARNING, + 'level_name' => 'WARNING', + 'channel' => 'test', + 'datetime' => $record['datetime']->format('Y-m-d H:i:s'), + 'extra' => array(), + ); + + $collection->expects($this->once()) + ->method('save') + ->with($expected); + + $handler = new MongoDBHandler($mongo, 'DB', 'Collection'); + $handler->handle($record); + } +} + +if (!class_exists('Mongo')) { + class Mongo + { + public function selectCollection() + { + } + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.php new file mode 100644 index 0000000..ddf545d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/NativeMailerHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use InvalidArgumentException; + +function mail($to, $subject, $message, $additional_headers = null, $additional_parameters = null) +{ + $GLOBALS['mail'][] = func_get_args(); +} + +class NativeMailerHandlerTest extends TestCase +{ + protected function setUp() + { + $GLOBALS['mail'] = array(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorHeaderInjection() + { + $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', "receiver@example.org\r\nFrom: faked@attacker.org"); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetterHeaderInjection() + { + $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); + $mailer->addHeader("Content-Type: text/html\r\nFrom: faked@attacker.org"); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetterArrayHeaderInjection() + { + $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); + $mailer->addHeader(array("Content-Type: text/html\r\nFrom: faked@attacker.org")); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetterContentTypeInjection() + { + $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); + $mailer->setContentType("text/html\r\nFrom: faked@attacker.org"); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testSetterEncodingInjection() + { + $mailer = new NativeMailerHandler('spammer@example.org', 'dear victim', 'receiver@example.org'); + $mailer->setEncoding("utf-8\r\nFrom: faked@attacker.org"); + } + + public function testSend() + { + $to = 'spammer@example.org'; + $subject = 'dear victim'; + $from = 'receiver@example.org'; + + $mailer = new NativeMailerHandler($to, $subject, $from); + $mailer->handleBatch(array()); + + // batch is empty, nothing sent + $this->assertEmpty($GLOBALS['mail']); + + // non-empty batch + $mailer->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz")); + $this->assertNotEmpty($GLOBALS['mail']); + $this->assertInternalType('array', $GLOBALS['mail']); + $this->assertArrayHasKey('0', $GLOBALS['mail']); + $params = $GLOBALS['mail'][0]; + $this->assertCount(5, $params); + $this->assertSame($to, $params[0]); + $this->assertSame($subject, $params[1]); + $this->assertStringEndsWith(" test.ERROR: Foo Bar Baz [] []\n", $params[2]); + $this->assertSame("From: $from\r\nContent-type: text/plain; charset=utf-8\r\n", $params[3]); + $this->assertSame('', $params[4]); + } + + public function testMessageSubjectFormatting() + { + $mailer = new NativeMailerHandler('to@example.org', 'Alert: %level_name% %message%', 'from@example.org'); + $mailer->handle($this->getRecord(Logger::ERROR, "Foo\nBar\r\n\r\nBaz")); + $this->assertNotEmpty($GLOBALS['mail']); + $this->assertInternalType('array', $GLOBALS['mail']); + $this->assertArrayHasKey('0', $GLOBALS['mail']); + $params = $GLOBALS['mail'][0]; + $this->assertCount(5, $params); + $this->assertSame('Alert: ERROR Foo Bar Baz', $params[1]); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/NewRelicHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/NewRelicHandlerTest.php new file mode 100644 index 0000000..4d3a615 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/NewRelicHandlerTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\TestCase; +use Monolog\Logger; + +class NewRelicHandlerTest extends TestCase +{ + public static $appname; + public static $customParameters; + public static $transactionName; + + public function setUp() + { + self::$appname = null; + self::$customParameters = array(); + self::$transactionName = null; + } + + /** + * @expectedException Monolog\Handler\MissingExtensionException + */ + public function testThehandlerThrowsAnExceptionIfTheNRExtensionIsNotLoaded() + { + $handler = new StubNewRelicHandlerWithoutExtension(); + $handler->handle($this->getRecord(Logger::ERROR)); + } + + public function testThehandlerCanHandleTheRecord() + { + $handler = new StubNewRelicHandler(); + $handler->handle($this->getRecord(Logger::ERROR)); + } + + public function testThehandlerCanAddContextParamsToTheNewRelicTrace() + { + $handler = new StubNewRelicHandler(); + $handler->handle($this->getRecord(Logger::ERROR, 'log message', array('a' => 'b'))); + $this->assertEquals(array('context_a' => 'b'), self::$customParameters); + } + + public function testThehandlerCanAddExplodedContextParamsToTheNewRelicTrace() + { + $handler = new StubNewRelicHandler(Logger::ERROR, true, self::$appname, true); + $handler->handle($this->getRecord( + Logger::ERROR, + 'log message', + array('a' => array('key1' => 'value1', 'key2' => 'value2')) + )); + $this->assertEquals( + array('context_a_key1' => 'value1', 'context_a_key2' => 'value2'), + self::$customParameters + ); + } + + public function testThehandlerCanAddExtraParamsToTheNewRelicTrace() + { + $record = $this->getRecord(Logger::ERROR, 'log message'); + $record['extra'] = array('c' => 'd'); + + $handler = new StubNewRelicHandler(); + $handler->handle($record); + + $this->assertEquals(array('extra_c' => 'd'), self::$customParameters); + } + + public function testThehandlerCanAddExplodedExtraParamsToTheNewRelicTrace() + { + $record = $this->getRecord(Logger::ERROR, 'log message'); + $record['extra'] = array('c' => array('key1' => 'value1', 'key2' => 'value2')); + + $handler = new StubNewRelicHandler(Logger::ERROR, true, self::$appname, true); + $handler->handle($record); + + $this->assertEquals( + array('extra_c_key1' => 'value1', 'extra_c_key2' => 'value2'), + self::$customParameters + ); + } + + public function testThehandlerCanAddExtraContextAndParamsToTheNewRelicTrace() + { + $record = $this->getRecord(Logger::ERROR, 'log message', array('a' => 'b')); + $record['extra'] = array('c' => 'd'); + + $handler = new StubNewRelicHandler(); + $handler->handle($record); + + $expected = array( + 'context_a' => 'b', + 'extra_c' => 'd', + ); + + $this->assertEquals($expected, self::$customParameters); + } + + public function testThehandlerCanHandleTheRecordsFormattedUsingTheLineFormatter() + { + $handler = new StubNewRelicHandler(); + $handler->setFormatter(new LineFormatter()); + $handler->handle($this->getRecord(Logger::ERROR)); + } + + public function testTheAppNameIsNullByDefault() + { + $handler = new StubNewRelicHandler(); + $handler->handle($this->getRecord(Logger::ERROR, 'log message')); + + $this->assertEquals(null, self::$appname); + } + + public function testTheAppNameCanBeInjectedFromtheConstructor() + { + $handler = new StubNewRelicHandler(Logger::DEBUG, false, 'myAppName'); + $handler->handle($this->getRecord(Logger::ERROR, 'log message')); + + $this->assertEquals('myAppName', self::$appname); + } + + public function testTheAppNameCanBeOverriddenFromEachLog() + { + $handler = new StubNewRelicHandler(Logger::DEBUG, false, 'myAppName'); + $handler->handle($this->getRecord(Logger::ERROR, 'log message', array('appname' => 'logAppName'))); + + $this->assertEquals('logAppName', self::$appname); + } + + public function testTheTransactionNameIsNullByDefault() + { + $handler = new StubNewRelicHandler(); + $handler->handle($this->getRecord(Logger::ERROR, 'log message')); + + $this->assertEquals(null, self::$transactionName); + } + + public function testTheTransactionNameCanBeInjectedFromTheConstructor() + { + $handler = new StubNewRelicHandler(Logger::DEBUG, false, null, false, 'myTransaction'); + $handler->handle($this->getRecord(Logger::ERROR, 'log message')); + + $this->assertEquals('myTransaction', self::$transactionName); + } + + public function testTheTransactionNameCanBeOverriddenFromEachLog() + { + $handler = new StubNewRelicHandler(Logger::DEBUG, false, null, false, 'myTransaction'); + $handler->handle($this->getRecord(Logger::ERROR, 'log message', array('transaction_name' => 'logTransactName'))); + + $this->assertEquals('logTransactName', self::$transactionName); + } +} + +class StubNewRelicHandlerWithoutExtension extends NewRelicHandler +{ + protected function isNewRelicEnabled() + { + return false; + } +} + +class StubNewRelicHandler extends NewRelicHandler +{ + protected function isNewRelicEnabled() + { + return true; + } +} + +function newrelic_notice_error() +{ + return true; +} + +function newrelic_set_appname($appname) +{ + return NewRelicHandlerTest::$appname = $appname; +} + +function newrelic_name_transaction($transactionName) +{ + return NewRelicHandlerTest::$transactionName = $transactionName; +} + +function newrelic_add_custom_parameter($key, $value) +{ + NewRelicHandlerTest::$customParameters[$key] = $value; + + return true; +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/NullHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/NullHandlerTest.php new file mode 100644 index 0000000..292df78 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/NullHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\NullHandler::handle + */ +class NullHandlerTest extends TestCase +{ + public function testHandle() + { + $handler = new NullHandler(); + $this->assertTrue($handler->handle($this->getRecord())); + } + + public function testHandleLowerLevelRecord() + { + $handler = new NullHandler(Logger::WARNING); + $this->assertFalse($handler->handle($this->getRecord(Logger::DEBUG))); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/PHPConsoleHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/PHPConsoleHandlerTest.php new file mode 100644 index 0000000..152573e --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/PHPConsoleHandlerTest.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 Monolog\Handler; + +use Exception; +use Monolog\ErrorHandler; +use Monolog\Logger; +use Monolog\TestCase; +use PhpConsole\Connector; +use PhpConsole\Dispatcher\Debug as DebugDispatcher; +use PhpConsole\Dispatcher\Errors as ErrorDispatcher; +use PhpConsole\Handler; +use PHPUnit_Framework_MockObject_MockObject; + +/** + * @covers Monolog\Handler\PHPConsoleHandler + * @author Sergey Barbushin https://www.linkedin.com/in/barbushin + */ +class PHPConsoleHandlerTest extends TestCase +{ + /** @var Connector|PHPUnit_Framework_MockObject_MockObject */ + protected $connector; + /** @var DebugDispatcher|PHPUnit_Framework_MockObject_MockObject */ + protected $debugDispatcher; + /** @var ErrorDispatcher|PHPUnit_Framework_MockObject_MockObject */ + protected $errorDispatcher; + + protected function setUp() + { + if (!class_exists('PhpConsole\Connector')) { + $this->markTestSkipped('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); + } + $this->connector = $this->initConnectorMock(); + + $this->debugDispatcher = $this->initDebugDispatcherMock($this->connector); + $this->connector->setDebugDispatcher($this->debugDispatcher); + + $this->errorDispatcher = $this->initErrorDispatcherMock($this->connector); + $this->connector->setErrorsDispatcher($this->errorDispatcher); + } + + protected function initDebugDispatcherMock(Connector $connector) + { + return $this->getMockBuilder('PhpConsole\Dispatcher\Debug') + ->disableOriginalConstructor() + ->setMethods(array('dispatchDebug')) + ->setConstructorArgs(array($connector, $connector->getDumper())) + ->getMock(); + } + + protected function initErrorDispatcherMock(Connector $connector) + { + return $this->getMockBuilder('PhpConsole\Dispatcher\Errors') + ->disableOriginalConstructor() + ->setMethods(array('dispatchError', 'dispatchException')) + ->setConstructorArgs(array($connector, $connector->getDumper())) + ->getMock(); + } + + protected function initConnectorMock() + { + $connector = $this->getMockBuilder('PhpConsole\Connector') + ->disableOriginalConstructor() + ->setMethods(array( + 'sendMessage', + 'onShutDown', + 'isActiveClient', + 'setSourcesBasePath', + 'setServerEncoding', + 'setPassword', + 'enableSslOnlyMode', + 'setAllowedIpMasks', + 'setHeadersLimit', + 'startEvalRequestsListener', + )) + ->getMock(); + + $connector->expects($this->any()) + ->method('isActiveClient') + ->will($this->returnValue(true)); + + return $connector; + } + + protected function getHandlerDefaultOption($name) + { + $handler = new PHPConsoleHandler(array(), $this->connector); + $options = $handler->getOptions(); + + return $options[$name]; + } + + protected function initLogger($handlerOptions = array(), $level = Logger::DEBUG) + { + return new Logger('test', array( + new PHPConsoleHandler($handlerOptions, $this->connector, $level), + )); + } + + public function testInitWithDefaultConnector() + { + $handler = new PHPConsoleHandler(); + $this->assertEquals(spl_object_hash(Connector::getInstance()), spl_object_hash($handler->getConnector())); + } + + public function testInitWithCustomConnector() + { + $handler = new PHPConsoleHandler(array(), $this->connector); + $this->assertEquals(spl_object_hash($this->connector), spl_object_hash($handler->getConnector())); + } + + public function testDebug() + { + $this->debugDispatcher->expects($this->once())->method('dispatchDebug')->with($this->equalTo('test')); + $this->initLogger()->addDebug('test'); + } + + public function testDebugContextInMessage() + { + $message = 'test'; + $tag = 'tag'; + $context = array($tag, 'custom' => mt_rand()); + $expectedMessage = $message . ' ' . json_encode(array_slice($context, 1)); + $this->debugDispatcher->expects($this->once())->method('dispatchDebug')->with( + $this->equalTo($expectedMessage), + $this->equalTo($tag) + ); + $this->initLogger()->addDebug($message, $context); + } + + public function testDebugTags($tagsContextKeys = null) + { + $expectedTags = mt_rand(); + $logger = $this->initLogger($tagsContextKeys ? array('debugTagsKeysInContext' => $tagsContextKeys) : array()); + if (!$tagsContextKeys) { + $tagsContextKeys = $this->getHandlerDefaultOption('debugTagsKeysInContext'); + } + foreach ($tagsContextKeys as $key) { + $debugDispatcher = $this->initDebugDispatcherMock($this->connector); + $debugDispatcher->expects($this->once())->method('dispatchDebug')->with( + $this->anything(), + $this->equalTo($expectedTags) + ); + $this->connector->setDebugDispatcher($debugDispatcher); + $logger->addDebug('test', array($key => $expectedTags)); + } + } + + public function testError($classesPartialsTraceIgnore = null) + { + $code = E_USER_NOTICE; + $message = 'message'; + $file = __FILE__; + $line = __LINE__; + $this->errorDispatcher->expects($this->once())->method('dispatchError')->with( + $this->equalTo($code), + $this->equalTo($message), + $this->equalTo($file), + $this->equalTo($line), + $classesPartialsTraceIgnore ?: $this->equalTo($this->getHandlerDefaultOption('classesPartialsTraceIgnore')) + ); + $errorHandler = ErrorHandler::register($this->initLogger($classesPartialsTraceIgnore ? array('classesPartialsTraceIgnore' => $classesPartialsTraceIgnore) : array()), false); + $errorHandler->registerErrorHandler(array(), false, E_USER_WARNING); + $errorHandler->handleError($code, $message, $file, $line); + } + + public function testException() + { + $e = new Exception(); + $this->errorDispatcher->expects($this->once())->method('dispatchException')->with( + $this->equalTo($e) + ); + $handler = $this->initLogger(); + $handler->log( + \Psr\Log\LogLevel::ERROR, + sprintf('Uncaught Exception %s: "%s" at %s line %s', get_class($e), $e->getMessage(), $e->getFile(), $e->getLine()), + array('exception' => $e) + ); + } + + /** + * @expectedException Exception + */ + public function testWrongOptionsThrowsException() + { + new PHPConsoleHandler(array('xxx' => 1)); + } + + public function testOptionEnabled() + { + $this->debugDispatcher->expects($this->never())->method('dispatchDebug'); + $this->initLogger(array('enabled' => false))->addDebug('test'); + } + + public function testOptionClassesPartialsTraceIgnore() + { + $this->testError(array('Class', 'Namespace\\')); + } + + public function testOptionDebugTagsKeysInContext() + { + $this->testDebugTags(array('key1', 'key2')); + } + + public function testOptionUseOwnErrorsAndExceptionsHandler() + { + $this->initLogger(array('useOwnErrorsHandler' => true, 'useOwnExceptionsHandler' => true)); + $this->assertEquals(array(Handler::getInstance(), 'handleError'), set_error_handler(function () { + })); + $this->assertEquals(array(Handler::getInstance(), 'handleException'), set_exception_handler(function () { + })); + } + + public static function provideConnectorMethodsOptionsSets() + { + return array( + array('sourcesBasePath', 'setSourcesBasePath', __DIR__), + array('serverEncoding', 'setServerEncoding', 'cp1251'), + array('password', 'setPassword', '******'), + array('enableSslOnlyMode', 'enableSslOnlyMode', true, false), + array('ipMasks', 'setAllowedIpMasks', array('127.0.0.*')), + array('headersLimit', 'setHeadersLimit', 2500), + array('enableEvalListener', 'startEvalRequestsListener', true, false), + ); + } + + /** + * @dataProvider provideConnectorMethodsOptionsSets + */ + public function testOptionCallsConnectorMethod($option, $method, $value, $isArgument = true) + { + $expectCall = $this->connector->expects($this->once())->method($method); + if ($isArgument) { + $expectCall->with($value); + } + new PHPConsoleHandler(array($option => $value), $this->connector); + } + + public function testOptionDetectDumpTraceAndSource() + { + new PHPConsoleHandler(array('detectDumpTraceAndSource' => true), $this->connector); + $this->assertTrue($this->connector->getDebugDispatcher()->detectTraceAndSource); + } + + public static function provideDumperOptionsValues() + { + return array( + array('dumperLevelLimit', 'levelLimit', 1001), + array('dumperItemsCountLimit', 'itemsCountLimit', 1002), + array('dumperItemSizeLimit', 'itemSizeLimit', 1003), + array('dumperDumpSizeLimit', 'dumpSizeLimit', 1004), + array('dumperDetectCallbacks', 'detectCallbacks', true), + ); + } + + /** + * @dataProvider provideDumperOptionsValues + */ + public function testDumperOptions($option, $dumperProperty, $value) + { + new PHPConsoleHandler(array($option => $value), $this->connector); + $this->assertEquals($value, $this->connector->getDumper()->$dumperProperty); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/PsrHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/PsrHandlerTest.php new file mode 100644 index 0000000..64eaab1 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/PsrHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\PsrHandler::handle + */ +class PsrHandlerTest extends TestCase +{ + public function logLevelProvider() + { + $levels = array(); + $monologLogger = new Logger(''); + + foreach ($monologLogger->getLevels() as $levelName => $level) { + $levels[] = array($levelName, $level); + } + + return $levels; + } + + /** + * @dataProvider logLevelProvider + */ + public function testHandlesAllLevels($levelName, $level) + { + $message = 'Hello, world! ' . $level; + $context = array('foo' => 'bar', 'level' => $level); + + $psrLogger = $this->getMock('Psr\Log\NullLogger'); + $psrLogger->expects($this->once()) + ->method('log') + ->with(strtolower($levelName), $message, $context); + + $handler = new PsrHandler($psrLogger); + $handler->handle(array('level' => $level, 'level_name' => $levelName, 'message' => $message, 'context' => $context)); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.php new file mode 100644 index 0000000..56df474 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/PushoverHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * Almost all examples (expected header, titles, messages) taken from + * https://www.pushover.net/api + * @author Sebastian Göttschkes + * @see https://www.pushover.net/api + */ +class PushoverHandlerTest extends TestCase +{ + private $res; + private $handler; + + public function testWriteHeader() + { + $this->createHandler(); + $this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/1\/messages.json HTTP\/1.1\\r\\nHost: api.pushover.net\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + + return $content; + } + + /** + * @depends testWriteHeader + */ + public function testWriteContent($content) + { + $this->assertRegexp('/token=myToken&user=myUser&message=test1&title=Monolog×tamp=\d{10}$/', $content); + } + + public function testWriteWithComplexTitle() + { + $this->createHandler('myToken', 'myUser', 'Backup finished - SQL1'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/title=Backup\+finished\+-\+SQL1/', $content); + } + + public function testWriteWithComplexMessage() + { + $this->createHandler(); + $this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'Backup of database "example" finished in 16 minutes.')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/message=Backup\+of\+database\+%22example%22\+finished\+in\+16\+minutes\./', $content); + } + + public function testWriteWithTooLongMessage() + { + $message = str_pad('test', 520, 'a'); + $this->createHandler(); + $this->handler->setHighPriorityLevel(Logger::EMERGENCY); // skip priority notifications + $this->handler->handle($this->getRecord(Logger::CRITICAL, $message)); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $expectedMessage = substr($message, 0, 505); + + $this->assertRegexp('/message=' . $expectedMessage . '&title/', $content); + } + + public function testWriteWithHighPriority() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/token=myToken&user=myUser&message=test1&title=Monolog×tamp=\d{10}&priority=1$/', $content); + } + + public function testWriteWithEmergencyPriority() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::EMERGENCY, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/token=myToken&user=myUser&message=test1&title=Monolog×tamp=\d{10}&priority=2&retry=30&expire=25200$/', $content); + } + + public function testWriteToMultipleUsers() + { + $this->createHandler('myToken', array('userA', 'userB')); + $this->handler->handle($this->getRecord(Logger::EMERGENCY, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/token=myToken&user=userA&message=test1&title=Monolog×tamp=\d{10}&priority=2&retry=30&expire=25200POST/', $content); + $this->assertRegexp('/token=myToken&user=userB&message=test1&title=Monolog×tamp=\d{10}&priority=2&retry=30&expire=25200$/', $content); + } + + private function createHandler($token = 'myToken', $user = 'myUser', $title = 'Monolog') + { + $constructorArgs = array($token, $user, $title); + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\PushoverHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $constructorArgs + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + + $this->handler->setFormatter($this->getIdentityFormatter()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.php new file mode 100644 index 0000000..26d212b --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/RavenHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +class RavenHandlerTest extends TestCase +{ + public function setUp() + { + if (!class_exists('Raven_Client')) { + $this->markTestSkipped('raven/raven not installed'); + } + + require_once __DIR__ . '/MockRavenClient.php'; + } + + /** + * @covers Monolog\Handler\RavenHandler::__construct + */ + public function testConstruct() + { + $handler = new RavenHandler($this->getRavenClient()); + $this->assertInstanceOf('Monolog\Handler\RavenHandler', $handler); + } + + protected function getHandler($ravenClient) + { + $handler = new RavenHandler($ravenClient); + + return $handler; + } + + protected function getRavenClient() + { + $dsn = 'http://43f6017361224d098402974103bfc53d:a6a0538fc2934ba2bed32e08741b2cd3@marca.python.live.cheggnet.com:9000/1'; + + return new MockRavenClient($dsn); + } + + public function testDebug() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $record = $this->getRecord(Logger::DEBUG, 'A test debug message'); + $handler->handle($record); + + $this->assertEquals($ravenClient::DEBUG, $ravenClient->lastData['level']); + $this->assertContains($record['message'], $ravenClient->lastData['message']); + } + + public function testWarning() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $record = $this->getRecord(Logger::WARNING, 'A test warning message'); + $handler->handle($record); + + $this->assertEquals($ravenClient::WARNING, $ravenClient->lastData['level']); + $this->assertContains($record['message'], $ravenClient->lastData['message']); + } + + public function testTag() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $tags = array(1, 2, 'foo'); + $record = $this->getRecord(Logger::INFO, 'test', array('tags' => $tags)); + $handler->handle($record); + + $this->assertEquals($tags, $ravenClient->lastData['tags']); + } + + public function testExtraParameters() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $checksum = '098f6bcd4621d373cade4e832627b4f6'; + $release = '05a671c66aefea124cc08b76ea6d30bb'; + $eventId = '31423'; + $record = $this->getRecord(Logger::INFO, 'test', array('checksum' => $checksum, 'release' => $release, 'event_id' => $eventId)); + $handler->handle($record); + + $this->assertEquals($checksum, $ravenClient->lastData['checksum']); + $this->assertEquals($release, $ravenClient->lastData['release']); + $this->assertEquals($eventId, $ravenClient->lastData['event_id']); + } + + public function testFingerprint() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $fingerprint = array('{{ default }}', 'other value'); + $record = $this->getRecord(Logger::INFO, 'test', array('fingerprint' => $fingerprint)); + $handler->handle($record); + + $this->assertEquals($fingerprint, $ravenClient->lastData['fingerprint']); + } + + public function testUserContext() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $recordWithNoContext = $this->getRecord(Logger::INFO, 'test with default user context'); + // set user context 'externally' + + $user = array( + 'id' => '123', + 'email' => 'test@test.com', + ); + + $recordWithContext = $this->getRecord(Logger::INFO, 'test', array('user' => $user)); + + $ravenClient->user_context(array('id' => 'test_user_id')); + // handle context + $handler->handle($recordWithContext); + $this->assertEquals($user, $ravenClient->lastData['user']); + + // check to see if its reset + $handler->handle($recordWithNoContext); + $this->assertInternalType('array', $ravenClient->context->user); + $this->assertSame('test_user_id', $ravenClient->context->user['id']); + + // handle with null context + $ravenClient->user_context(null); + $handler->handle($recordWithContext); + $this->assertEquals($user, $ravenClient->lastData['user']); + + // check to see if its reset + $handler->handle($recordWithNoContext); + $this->assertNull($ravenClient->context->user); + } + + public function testException() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + try { + $this->methodThatThrowsAnException(); + } catch (\Exception $e) { + $record = $this->getRecord(Logger::ERROR, $e->getMessage(), array('exception' => $e)); + $handler->handle($record); + } + + $this->assertEquals($record['message'], $ravenClient->lastData['message']); + } + + public function testHandleBatch() + { + $records = $this->getMultipleRecords(); + $records[] = $this->getRecord(Logger::WARNING, 'warning'); + $records[] = $this->getRecord(Logger::WARNING, 'warning'); + + $logFormatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $logFormatter->expects($this->once())->method('formatBatch'); + + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $formatter->expects($this->once())->method('format')->with($this->callback(function ($record) { + return $record['level'] == 400; + })); + + $handler = $this->getHandler($this->getRavenClient()); + $handler->setBatchFormatter($logFormatter); + $handler->setFormatter($formatter); + $handler->handleBatch($records); + } + + public function testHandleBatchDoNothingIfRecordsAreBelowLevel() + { + $records = array( + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information'), + ); + + $handler = $this->getMock('Monolog\Handler\RavenHandler', null, array($this->getRavenClient())); + $handler->expects($this->never())->method('handle'); + $handler->setLevel(Logger::ERROR); + $handler->handleBatch($records); + } + + public function testHandleBatchPicksProperMessage() + { + $records = array( + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information 1'), + $this->getRecord(Logger::ERROR, 'error 1'), + $this->getRecord(Logger::WARNING, 'warning'), + $this->getRecord(Logger::ERROR, 'error 2'), + $this->getRecord(Logger::INFO, 'information 2'), + ); + + $logFormatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $logFormatter->expects($this->once())->method('formatBatch'); + + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $formatter->expects($this->once())->method('format')->with($this->callback(function ($record) use ($records) { + return $record['message'] == 'error 1'; + })); + + $handler = $this->getHandler($this->getRavenClient()); + $handler->setBatchFormatter($logFormatter); + $handler->setFormatter($formatter); + $handler->handleBatch($records); + } + + public function testGetSetBatchFormatter() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + + $handler->setBatchFormatter($formatter = new LineFormatter()); + $this->assertSame($formatter, $handler->getBatchFormatter()); + } + + public function testRelease() + { + $ravenClient = $this->getRavenClient(); + $handler = $this->getHandler($ravenClient); + $release = 'v42.42.42'; + $handler->setRelease($release); + $record = $this->getRecord(Logger::INFO, 'test'); + $handler->handle($record); + $this->assertEquals($release, $ravenClient->lastData['release']); + + $localRelease = 'v41.41.41'; + $record = $this->getRecord(Logger::INFO, 'test', array('release' => $localRelease)); + $handler->handle($record); + $this->assertEquals($localRelease, $ravenClient->lastData['release']); + } + + private function methodThatThrowsAnException() + { + throw new \Exception('This is an exception'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.php new file mode 100644 index 0000000..689d527 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/RedisHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; + +class RedisHandlerTest extends TestCase +{ + /** + * @expectedException InvalidArgumentException + */ + public function testConstructorShouldThrowExceptionForInvalidRedis() + { + new RedisHandler(new \stdClass(), 'key'); + } + + public function testConstructorShouldWorkWithPredis() + { + $redis = $this->getMock('Predis\Client'); + $this->assertInstanceof('Monolog\Handler\RedisHandler', new RedisHandler($redis, 'key')); + } + + public function testConstructorShouldWorkWithRedis() + { + $redis = $this->getMock('Redis'); + $this->assertInstanceof('Monolog\Handler\RedisHandler', new RedisHandler($redis, 'key')); + } + + public function testPredisHandle() + { + $redis = $this->getMock('Predis\Client', array('rpush')); + + // Predis\Client uses rpush + $redis->expects($this->once()) + ->method('rpush') + ->with('key', 'test'); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $handler = new RedisHandler($redis, 'key'); + $handler->setFormatter(new LineFormatter("%message%")); + $handler->handle($record); + } + + public function testRedisHandle() + { + $redis = $this->getMock('Redis', array('rpush')); + + // Redis uses rPush + $redis->expects($this->once()) + ->method('rPush') + ->with('key', 'test'); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $handler = new RedisHandler($redis, 'key'); + $handler->setFormatter(new LineFormatter("%message%")); + $handler->handle($record); + } + + public function testRedisHandleCapped() + { + $redis = $this->getMock('Redis', array('multi', 'rpush', 'ltrim', 'exec')); + + // Redis uses multi + $redis->expects($this->once()) + ->method('multi') + ->will($this->returnSelf()); + + $redis->expects($this->once()) + ->method('rpush') + ->will($this->returnSelf()); + + $redis->expects($this->once()) + ->method('ltrim') + ->will($this->returnSelf()); + + $redis->expects($this->once()) + ->method('exec') + ->will($this->returnSelf()); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $handler = new RedisHandler($redis, 'key', Logger::DEBUG, true, 10); + $handler->setFormatter(new LineFormatter("%message%")); + $handler->handle($record); + } + + public function testPredisHandleCapped() + { + $redis = $this->getMock('Predis\Client', array('transaction')); + + $redisTransaction = $this->getMock('Predis\Client', array('rpush', 'ltrim')); + + $redisTransaction->expects($this->once()) + ->method('rpush') + ->will($this->returnSelf()); + + $redisTransaction->expects($this->once()) + ->method('ltrim') + ->will($this->returnSelf()); + + // Redis uses multi + $redis->expects($this->once()) + ->method('transaction') + ->will($this->returnCallback(function ($cb) use ($redisTransaction) { + $cb($redisTransaction); + })); + + $record = $this->getRecord(Logger::WARNING, 'test', array('data' => new \stdClass, 'foo' => 34)); + + $handler = new RedisHandler($redis, 'key', Logger::DEBUG, true, 10); + $handler->setFormatter(new LineFormatter("%message%")); + $handler->handle($record); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/RollbarHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/RollbarHandlerTest.php new file mode 100644 index 0000000..f302e91 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/RollbarHandlerTest.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 Monolog\Handler; + +use Exception; +use Monolog\TestCase; +use Monolog\Logger; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @author Erik Johansson + * @see https://rollbar.com/docs/notifier/rollbar-php/ + * + * @coversDefaultClass Monolog\Handler\RollbarHandler + */ +class RollbarHandlerTest extends TestCase +{ + /** + * @var MockObject + */ + private $rollbarNotifier; + + /** + * @var array + */ + public $reportedExceptionArguments = null; + + protected function setUp() + { + parent::setUp(); + + $this->setupRollbarNotifierMock(); + } + + /** + * When reporting exceptions to Rollbar the + * level has to be set in the payload data + */ + public function testExceptionLogLevel() + { + $handler = $this->createHandler(); + + $handler->handle($this->createExceptionRecord(Logger::DEBUG)); + + $this->assertEquals('debug', $this->reportedExceptionArguments['payload']['level']); + } + + private function setupRollbarNotifierMock() + { + $this->rollbarNotifier = $this->getMockBuilder('RollbarNotifier') + ->setMethods(array('report_message', 'report_exception', 'flush')) + ->getMock(); + + $that = $this; + + $this->rollbarNotifier + ->expects($this->any()) + ->method('report_exception') + ->willReturnCallback(function ($exception, $context, $payload) use ($that) { + $that->reportedExceptionArguments = compact('exception', 'context', 'payload'); + }); + } + + private function createHandler() + { + return new RollbarHandler($this->rollbarNotifier, Logger::DEBUG); + } + + private function createExceptionRecord($level = Logger::DEBUG, $message = 'test', $exception = null) + { + return $this->getRecord($level, $message, array( + 'exception' => $exception ?: new Exception() + )); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php new file mode 100644 index 0000000..f1feb22 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/RotatingFileHandlerTest.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use PHPUnit_Framework_Error_Deprecated; + +/** + * @covers Monolog\Handler\RotatingFileHandler + */ +class RotatingFileHandlerTest extends TestCase +{ + /** + * This var should be private but then the anonymous function + * in the `setUp` method won't be able to set it. `$this` cant't + * be used in the anonymous function in `setUp` because PHP 5.3 + * does not support it. + */ + public $lastError; + + public function setUp() + { + $dir = __DIR__.'/Fixtures'; + chmod($dir, 0777); + if (!is_writable($dir)) { + $this->markTestSkipped($dir.' must be writable to test the RotatingFileHandler.'); + } + $this->lastError = null; + $self = $this; + // workaround with &$self used for PHP 5.3 + set_error_handler(function($code, $message) use (&$self) { + $self->lastError = array( + 'code' => $code, + 'message' => $message, + ); + }); + } + + private function assertErrorWasTriggered($code, $message) + { + if (empty($this->lastError)) { + $this->fail( + sprintf( + 'Failed asserting that error with code `%d` and message `%s` was triggered', + $code, + $message + ) + ); + } + $this->assertEquals($code, $this->lastError['code'], sprintf('Expected an error with code %d to be triggered, got `%s` instead', $code, $this->lastError['code'])); + $this->assertEquals($message, $this->lastError['message'], sprintf('Expected an error with message `%d` to be triggered, got `%s` instead', $message, $this->lastError['message'])); + } + + public function testRotationCreatesNewFile() + { + touch(__DIR__.'/Fixtures/foo-'.date('Y-m-d', time() - 86400).'.rot'); + + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot'); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord()); + + $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; + $this->assertTrue(file_exists($log)); + $this->assertEquals('test', file_get_contents($log)); + } + + /** + * @dataProvider rotationTests + */ + public function testRotation($createFile, $dateFormat, $timeCallback) + { + touch($old1 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-1)).'.rot'); + touch($old2 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-2)).'.rot'); + touch($old3 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-3)).'.rot'); + touch($old4 = __DIR__.'/Fixtures/foo-'.date($dateFormat, $timeCallback(-4)).'.rot'); + + $log = __DIR__.'/Fixtures/foo-'.date($dateFormat).'.rot'; + + if ($createFile) { + touch($log); + } + + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->setFilenameFormat('{filename}-{date}', $dateFormat); + $handler->handle($this->getRecord()); + + $handler->close(); + + $this->assertTrue(file_exists($log)); + $this->assertTrue(file_exists($old1)); + $this->assertEquals($createFile, file_exists($old2)); + $this->assertEquals($createFile, file_exists($old3)); + $this->assertEquals($createFile, file_exists($old4)); + $this->assertEquals('test', file_get_contents($log)); + } + + public function rotationTests() + { + $now = time(); + $dayCallback = function($ago) use ($now) { + return $now + 86400 * $ago; + }; + $monthCallback = function($ago) { + return gmmktime(0, 0, 0, date('n') + $ago, 1, date('Y')); + }; + $yearCallback = function($ago) { + return gmmktime(0, 0, 0, 1, 1, date('Y') + $ago); + }; + + return array( + 'Rotation is triggered when the file of the current day is not present' + => array(true, RotatingFileHandler::FILE_PER_DAY, $dayCallback), + 'Rotation is not triggered when the file of the current day is already present' + => array(false, RotatingFileHandler::FILE_PER_DAY, $dayCallback), + + 'Rotation is triggered when the file of the current month is not present' + => array(true, RotatingFileHandler::FILE_PER_MONTH, $monthCallback), + 'Rotation is not triggered when the file of the current month is already present' + => array(false, RotatingFileHandler::FILE_PER_MONTH, $monthCallback), + + 'Rotation is triggered when the file of the current year is not present' + => array(true, RotatingFileHandler::FILE_PER_YEAR, $yearCallback), + 'Rotation is not triggered when the file of the current year is already present' + => array(false, RotatingFileHandler::FILE_PER_YEAR, $yearCallback), + ); + } + + /** + * @dataProvider dateFormatProvider + */ + public function testAllowOnlyFixedDefinedDateFormats($dateFormat, $valid) + { + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); + $handler->setFilenameFormat('{filename}-{date}', $dateFormat); + if (!$valid) { + $this->assertErrorWasTriggered( + E_USER_DEPRECATED, + 'Invalid date format - format must be one of RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), '. + 'RotatingFileHandler::FILE_PER_MONTH ("Y-m") or RotatingFileHandler::FILE_PER_YEAR ("Y"), '. + 'or you can set one of the date formats using slashes, underscores and/or dots instead of dashes.' + ); + } + } + + public function dateFormatProvider() + { + return array( + array(RotatingFileHandler::FILE_PER_DAY, true), + array(RotatingFileHandler::FILE_PER_MONTH, true), + array(RotatingFileHandler::FILE_PER_YEAR, true), + array('m-d-Y', false), + array('Y-m-d-h-i', false) + ); + } + + /** + * @dataProvider filenameFormatProvider + */ + public function testDisallowFilenameFormatsWithoutDate($filenameFormat, $valid) + { + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot', 2); + $handler->setFilenameFormat($filenameFormat, RotatingFileHandler::FILE_PER_DAY); + if (!$valid) { + $this->assertErrorWasTriggered( + E_USER_DEPRECATED, + 'Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.' + ); + } + } + + public function filenameFormatProvider() + { + return array( + array('{filename}', false), + array('{filename}-{date}', true), + array('{date}', true), + array('foobar-{date}', true), + array('foo-{date}-bar', true), + array('{date}-foobar', true), + array('foobar', false), + ); + } + + public function testReuseCurrentFile() + { + $log = __DIR__.'/Fixtures/foo-'.date('Y-m-d').'.rot'; + file_put_contents($log, "foo"); + $handler = new RotatingFileHandler(__DIR__.'/Fixtures/foo.rot'); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord()); + $this->assertEquals('footest', file_get_contents($log)); + } + + public function tearDown() + { + foreach (glob(__DIR__.'/Fixtures/*.rot') as $file) { + unlink($file); + } + restore_error_handler(); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SamplingHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SamplingHandlerTest.php new file mode 100644 index 0000000..b354cee --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SamplingHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; + +/** + * @covers Monolog\Handler\SamplingHandler::handle + */ +class SamplingHandlerTest extends TestCase +{ + public function testHandle() + { + $testHandler = new TestHandler(); + $handler = new SamplingHandler($testHandler, 2); + for ($i = 0; $i < 10000; $i++) { + $handler->handle($this->getRecord()); + } + $count = count($testHandler->getRecords()); + // $count should be half of 10k, so between 4k and 6k + $this->assertLessThan(6000, $count); + $this->assertGreaterThan(4000, $count); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php new file mode 100644 index 0000000..e1aa96d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/Slack/SlackRecordTest.php @@ -0,0 +1,387 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler\Slack; + +use Monolog\Logger; +use Monolog\TestCase; + +/** + * @coversDefaultClass Monolog\Handler\Slack\SlackRecord + */ +class SlackRecordTest extends TestCase +{ + private $jsonPrettyPrintFlag; + + protected function setUp() + { + $this->jsonPrettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128; + } + + public function dataGetAttachmentColor() + { + return array( + array(Logger::DEBUG, SlackRecord::COLOR_DEFAULT), + array(Logger::INFO, SlackRecord::COLOR_GOOD), + array(Logger::NOTICE, SlackRecord::COLOR_GOOD), + array(Logger::WARNING, SlackRecord::COLOR_WARNING), + array(Logger::ERROR, SlackRecord::COLOR_DANGER), + array(Logger::CRITICAL, SlackRecord::COLOR_DANGER), + array(Logger::ALERT, SlackRecord::COLOR_DANGER), + array(Logger::EMERGENCY, SlackRecord::COLOR_DANGER), + ); + } + + /** + * @dataProvider dataGetAttachmentColor + * @param int $logLevel + * @param string $expectedColour RGB hex color or name of Slack color + * @covers ::getAttachmentColor + */ + public function testGetAttachmentColor($logLevel, $expectedColour) + { + $slackRecord = new SlackRecord(); + $this->assertSame( + $expectedColour, + $slackRecord->getAttachmentColor($logLevel) + ); + } + + public function testAddsChannel() + { + $channel = '#test'; + $record = new SlackRecord($channel); + $data = $record->getSlackData($this->getRecord()); + + $this->assertArrayHasKey('channel', $data); + $this->assertSame($channel, $data['channel']); + } + + public function testNoUsernameByDefault() + { + $record = new SlackRecord(); + $data = $record->getSlackData($this->getRecord()); + + $this->assertArrayNotHasKey('username', $data); + } + + /** + * @return array + */ + public function dataStringify() + { + $jsonPrettyPrintFlag = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 128; + + $multipleDimensions = array(array(1, 2)); + $numericKeys = array('library' => 'monolog'); + $singleDimension = array(1, 'Hello', 'Jordi'); + + return array( + array(array(), '[]'), + array($multipleDimensions, json_encode($multipleDimensions, $jsonPrettyPrintFlag)), + array($numericKeys, json_encode($numericKeys, $jsonPrettyPrintFlag)), + array($singleDimension, json_encode($singleDimension)) + ); + } + + /** + * @dataProvider dataStringify + */ + public function testStringify($fields, $expectedResult) + { + $slackRecord = new SlackRecord( + '#test', + 'test', + true, + null, + true, + true + ); + + $this->assertSame($expectedResult, $slackRecord->stringify($fields)); + } + + public function testAddsCustomUsername() + { + $username = 'Monolog bot'; + $record = new SlackRecord(null, $username); + $data = $record->getSlackData($this->getRecord()); + + $this->assertArrayHasKey('username', $data); + $this->assertSame($username, $data['username']); + } + + public function testNoIcon() + { + $record = new SlackRecord(); + $data = $record->getSlackData($this->getRecord()); + + $this->assertArrayNotHasKey('icon_emoji', $data); + } + + public function testAddsIcon() + { + $record = $this->getRecord(); + $slackRecord = new SlackRecord(null, null, false, 'ghost'); + $data = $slackRecord->getSlackData($record); + + $slackRecord2 = new SlackRecord(null, null, false, 'http://github.com/Seldaek/monolog'); + $data2 = $slackRecord2->getSlackData($record); + + $this->assertArrayHasKey('icon_emoji', $data); + $this->assertSame(':ghost:', $data['icon_emoji']); + $this->assertArrayHasKey('icon_url', $data2); + $this->assertSame('http://github.com/Seldaek/monolog', $data2['icon_url']); + } + + public function testAttachmentsNotPresentIfNoAttachment() + { + $record = new SlackRecord(null, null, false); + $data = $record->getSlackData($this->getRecord()); + + $this->assertArrayNotHasKey('attachments', $data); + } + + public function testAddsOneAttachment() + { + $record = new SlackRecord(); + $data = $record->getSlackData($this->getRecord()); + + $this->assertArrayHasKey('attachments', $data); + $this->assertArrayHasKey(0, $data['attachments']); + $this->assertInternalType('array', $data['attachments'][0]); + } + + public function testTextEqualsMessageIfNoAttachment() + { + $message = 'Test message'; + $record = new SlackRecord(null, null, false); + $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); + + $this->assertArrayHasKey('text', $data); + $this->assertSame($message, $data['text']); + } + + public function testTextEqualsFormatterOutput() + { + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $formatter + ->expects($this->any()) + ->method('format') + ->will($this->returnCallback(function ($record) { return $record['message'] . 'test'; })); + + $formatter2 = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $formatter2 + ->expects($this->any()) + ->method('format') + ->will($this->returnCallback(function ($record) { return $record['message'] . 'test1'; })); + + $message = 'Test message'; + $record = new SlackRecord(null, null, false, null, false, false, array(), $formatter); + $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); + + $this->assertArrayHasKey('text', $data); + $this->assertSame($message . 'test', $data['text']); + + $record->setFormatter($formatter2); + $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); + + $this->assertArrayHasKey('text', $data); + $this->assertSame($message . 'test1', $data['text']); + } + + public function testAddsFallbackAndTextToAttachment() + { + $message = 'Test message'; + $record = new SlackRecord(null); + $data = $record->getSlackData($this->getRecord(Logger::WARNING, $message)); + + $this->assertSame($message, $data['attachments'][0]['text']); + $this->assertSame($message, $data['attachments'][0]['fallback']); + } + + public function testMapsLevelToColorAttachmentColor() + { + $record = new SlackRecord(null); + $errorLoggerRecord = $this->getRecord(Logger::ERROR); + $emergencyLoggerRecord = $this->getRecord(Logger::EMERGENCY); + $warningLoggerRecord = $this->getRecord(Logger::WARNING); + $infoLoggerRecord = $this->getRecord(Logger::INFO); + $debugLoggerRecord = $this->getRecord(Logger::DEBUG); + + $data = $record->getSlackData($errorLoggerRecord); + $this->assertSame(SlackRecord::COLOR_DANGER, $data['attachments'][0]['color']); + + $data = $record->getSlackData($emergencyLoggerRecord); + $this->assertSame(SlackRecord::COLOR_DANGER, $data['attachments'][0]['color']); + + $data = $record->getSlackData($warningLoggerRecord); + $this->assertSame(SlackRecord::COLOR_WARNING, $data['attachments'][0]['color']); + + $data = $record->getSlackData($infoLoggerRecord); + $this->assertSame(SlackRecord::COLOR_GOOD, $data['attachments'][0]['color']); + + $data = $record->getSlackData($debugLoggerRecord); + $this->assertSame(SlackRecord::COLOR_DEFAULT, $data['attachments'][0]['color']); + } + + public function testAddsShortAttachmentWithoutContextAndExtra() + { + $level = Logger::ERROR; + $levelName = Logger::getLevelName($level); + $record = new SlackRecord(null, null, true, null, true); + $data = $record->getSlackData($this->getRecord($level, 'test', array('test' => 1))); + + $attachment = $data['attachments'][0]; + $this->assertArrayHasKey('title', $attachment); + $this->assertArrayHasKey('fields', $attachment); + $this->assertSame($levelName, $attachment['title']); + $this->assertSame(array(), $attachment['fields']); + } + + public function testAddsShortAttachmentWithContextAndExtra() + { + $level = Logger::ERROR; + $levelName = Logger::getLevelName($level); + $context = array('test' => 1); + $extra = array('tags' => array('web')); + $record = new SlackRecord(null, null, true, null, true, true); + $loggerRecord = $this->getRecord($level, 'test', $context); + $loggerRecord['extra'] = $extra; + $data = $record->getSlackData($loggerRecord); + + $attachment = $data['attachments'][0]; + $this->assertArrayHasKey('title', $attachment); + $this->assertArrayHasKey('fields', $attachment); + $this->assertCount(2, $attachment['fields']); + $this->assertSame($levelName, $attachment['title']); + $this->assertSame( + array( + array( + 'title' => 'Extra', + 'value' => sprintf('```%s```', json_encode($extra, $this->jsonPrettyPrintFlag)), + 'short' => false + ), + array( + 'title' => 'Context', + 'value' => sprintf('```%s```', json_encode($context, $this->jsonPrettyPrintFlag)), + 'short' => false + ) + ), + $attachment['fields'] + ); + } + + public function testAddsLongAttachmentWithoutContextAndExtra() + { + $level = Logger::ERROR; + $levelName = Logger::getLevelName($level); + $record = new SlackRecord(null, null, true, null); + $data = $record->getSlackData($this->getRecord($level, 'test', array('test' => 1))); + + $attachment = $data['attachments'][0]; + $this->assertArrayHasKey('title', $attachment); + $this->assertArrayHasKey('fields', $attachment); + $this->assertCount(1, $attachment['fields']); + $this->assertSame('Message', $attachment['title']); + $this->assertSame( + array(array( + 'title' => 'Level', + 'value' => $levelName, + 'short' => false + )), + $attachment['fields'] + ); + } + + public function testAddsLongAttachmentWithContextAndExtra() + { + $level = Logger::ERROR; + $levelName = Logger::getLevelName($level); + $context = array('test' => 1); + $extra = array('tags' => array('web')); + $record = new SlackRecord(null, null, true, null, false, true); + $loggerRecord = $this->getRecord($level, 'test', $context); + $loggerRecord['extra'] = $extra; + $data = $record->getSlackData($loggerRecord); + + $expectedFields = array( + array( + 'title' => 'Level', + 'value' => $levelName, + 'short' => false, + ), + array( + 'title' => 'tags', + 'value' => sprintf('```%s```', json_encode($extra['tags'])), + 'short' => false + ), + array( + 'title' => 'test', + 'value' => $context['test'], + 'short' => false + ) + ); + + $attachment = $data['attachments'][0]; + $this->assertArrayHasKey('title', $attachment); + $this->assertArrayHasKey('fields', $attachment); + $this->assertCount(3, $attachment['fields']); + $this->assertSame('Message', $attachment['title']); + $this->assertSame( + $expectedFields, + $attachment['fields'] + ); + } + + public function testAddsTimestampToAttachment() + { + $record = $this->getRecord(); + $slackRecord = new SlackRecord(); + $data = $slackRecord->getSlackData($this->getRecord()); + + $attachment = $data['attachments'][0]; + $this->assertArrayHasKey('ts', $attachment); + $this->assertSame($record['datetime']->getTimestamp(), $attachment['ts']); + } + + public function testExcludeExtraAndContextFields() + { + $record = $this->getRecord( + Logger::WARNING, + 'test', + array('info' => array('library' => 'monolog', 'author' => 'Jordi')) + ); + $record['extra'] = array('tags' => array('web', 'cli')); + + $slackRecord = new SlackRecord(null, null, true, null, false, true, array('context.info.library', 'extra.tags.1')); + $data = $slackRecord->getSlackData($record); + $attachment = $data['attachments'][0]; + + $expected = array( + array( + 'title' => 'info', + 'value' => sprintf('```%s```', json_encode(array('author' => 'Jordi'), $this->jsonPrettyPrintFlag)), + 'short' => false + ), + array( + 'title' => 'tags', + 'value' => sprintf('```%s```', json_encode(array('web'))), + 'short' => false + ), + ); + + foreach ($expected as $field) { + $this->assertNotFalse(array_search($field, $attachment['fields'])); + break; + } + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SlackHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SlackHandlerTest.php new file mode 100644 index 0000000..b12b01f --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SlackHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; +use Monolog\Handler\Slack\SlackRecord; + +/** + * @author Greg Kedzierski + * @see https://api.slack.com/ + */ +class SlackHandlerTest extends TestCase +{ + /** + * @var resource + */ + private $res; + + /** + * @var SlackHandler + */ + private $handler; + + public function setUp() + { + if (!extension_loaded('openssl')) { + $this->markTestSkipped('This test requires openssl to run'); + } + } + + public function testWriteHeader() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/POST \/api\/chat.postMessage HTTP\/1.1\\r\\nHost: slack.com\\r\\nContent-Type: application\/x-www-form-urlencoded\\r\\nContent-Length: \d{2,4}\\r\\n\\r\\n/', $content); + } + + public function testWriteContent() + { + $this->createHandler(); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegExp('/username=Monolog/', $content); + $this->assertRegExp('/channel=channel1/', $content); + $this->assertRegExp('/token=myToken/', $content); + $this->assertRegExp('/attachments/', $content); + } + + public function testWriteContentUsesFormatterIfProvided() + { + $this->createHandler('myToken', 'channel1', 'Monolog', false); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->createHandler('myToken', 'channel1', 'Monolog', false); + $this->handler->setFormatter(new LineFormatter('foo--%message%')); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test2')); + fseek($this->res, 0); + $content2 = fread($this->res, 1024); + + $this->assertRegexp('/text=test1/', $content); + $this->assertRegexp('/text=foo--test2/', $content2); + } + + public function testWriteContentWithEmoji() + { + $this->createHandler('myToken', 'channel1', 'Monolog', true, 'alien'); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/icon_emoji=%3Aalien%3A/', $content); + } + + /** + * @dataProvider provideLevelColors + */ + public function testWriteContentWithColors($level, $expectedColor) + { + $this->createHandler(); + $this->handler->handle($this->getRecord($level, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/%22color%22%3A%22'.$expectedColor.'/', $content); + } + + public function testWriteContentWithPlainTextMessage() + { + $this->createHandler('myToken', 'channel1', 'Monolog', false); + $this->handler->handle($this->getRecord(Logger::CRITICAL, 'test1')); + fseek($this->res, 0); + $content = fread($this->res, 1024); + + $this->assertRegexp('/text=test1/', $content); + } + + public function provideLevelColors() + { + return array( + array(Logger::DEBUG, urlencode(SlackRecord::COLOR_DEFAULT)), + array(Logger::INFO, SlackRecord::COLOR_GOOD), + array(Logger::NOTICE, SlackRecord::COLOR_GOOD), + array(Logger::WARNING, SlackRecord::COLOR_WARNING), + array(Logger::ERROR, SlackRecord::COLOR_DANGER), + array(Logger::CRITICAL, SlackRecord::COLOR_DANGER), + array(Logger::ALERT, SlackRecord::COLOR_DANGER), + array(Logger::EMERGENCY,SlackRecord::COLOR_DANGER), + ); + } + + private function createHandler($token = 'myToken', $channel = 'channel1', $username = 'Monolog', $useAttachment = true, $iconEmoji = null, $useShortAttachment = false, $includeExtra = false) + { + $constructorArgs = array($token, $channel, $username, $useAttachment, $iconEmoji, Logger::DEBUG, true, $useShortAttachment, $includeExtra); + $this->res = fopen('php://memory', 'a'); + $this->handler = $this->getMock( + '\Monolog\Handler\SlackHandler', + array('fsockopen', 'streamSetTimeout', 'closeSocket'), + $constructorArgs + ); + + $reflectionProperty = new \ReflectionProperty('\Monolog\Handler\SocketHandler', 'connectionString'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($this->handler, 'localhost:1234'); + + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + $this->handler->expects($this->any()) + ->method('closeSocket') + ->will($this->returnValue(true)); + + $this->handler->setFormatter($this->getIdentityFormatter()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SlackWebhookHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SlackWebhookHandlerTest.php new file mode 100644 index 0000000..c9229e2 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SlackWebhookHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; +use Monolog\Formatter\LineFormatter; +use Monolog\Handler\Slack\SlackRecord; + +/** + * @author Haralan Dobrev + * @see https://api.slack.com/incoming-webhooks + * @coversDefaultClass Monolog\Handler\SlackWebhookHandler + */ +class SlackWebhookHandlerTest extends TestCase +{ + const WEBHOOK_URL = 'https://hooks.slack.com/services/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E'; + + /** + * @covers ::__construct + * @covers ::getSlackRecord + */ + public function testConstructorMinimal() + { + $handler = new SlackWebhookHandler(self::WEBHOOK_URL); + $record = $this->getRecord(); + $slackRecord = $handler->getSlackRecord(); + $this->assertInstanceOf('Monolog\Handler\Slack\SlackRecord', $slackRecord); + $this->assertEquals(array( + 'attachments' => array( + array( + 'fallback' => 'test', + 'text' => 'test', + 'color' => SlackRecord::COLOR_WARNING, + 'fields' => array( + array( + 'title' => 'Level', + 'value' => 'WARNING', + 'short' => false, + ), + ), + 'title' => 'Message', + 'mrkdwn_in' => array('fields'), + 'ts' => $record['datetime']->getTimestamp(), + ), + ), + ), $slackRecord->getSlackData($record)); + } + + /** + * @covers ::__construct + * @covers ::getSlackRecord + */ + public function testConstructorFull() + { + $handler = new SlackWebhookHandler( + self::WEBHOOK_URL, + 'test-channel', + 'test-username', + false, + ':ghost:', + false, + false, + Logger::DEBUG, + false + ); + + $slackRecord = $handler->getSlackRecord(); + $this->assertInstanceOf('Monolog\Handler\Slack\SlackRecord', $slackRecord); + $this->assertEquals(array( + 'username' => 'test-username', + 'text' => 'test', + 'channel' => 'test-channel', + 'icon_emoji' => ':ghost:', + ), $slackRecord->getSlackData($this->getRecord())); + } + + /** + * @covers ::getFormatter + */ + public function testGetFormatter() + { + $handler = new SlackWebhookHandler(self::WEBHOOK_URL); + $formatter = $handler->getFormatter(); + $this->assertInstanceOf('Monolog\Formatter\FormatterInterface', $formatter); + } + + /** + * @covers ::setFormatter + */ + public function testSetFormatter() + { + $handler = new SlackWebhookHandler(self::WEBHOOK_URL); + $formatter = new LineFormatter(); + $handler->setFormatter($formatter); + $this->assertSame($formatter, $handler->getFormatter()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SlackbotHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SlackbotHandlerTest.php new file mode 100644 index 0000000..b1b02bd --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SlackbotHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @author Haralan Dobrev + * @see https://slack.com/apps/A0F81R8ET-slackbot + * @coversDefaultClass Monolog\Handler\SlackbotHandler + */ +class SlackbotHandlerTest extends TestCase +{ + /** + * @covers ::__construct + */ + public function testConstructorMinimal() + { + $handler = new SlackbotHandler('test-team', 'test-token', 'test-channel'); + $this->assertInstanceOf('Monolog\Handler\AbstractProcessingHandler', $handler); + } + + /** + * @covers ::__construct + */ + public function testConstructorFull() + { + $handler = new SlackbotHandler( + 'test-team', + 'test-token', + 'test-channel', + Logger::DEBUG, + false + ); + $this->assertInstanceOf('Monolog\Handler\AbstractProcessingHandler', $handler); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php new file mode 100644 index 0000000..1f9c1f2 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SocketHandlerTest.php @@ -0,0 +1,309 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @author Pablo de Leon Belloc + */ +class SocketHandlerTest extends TestCase +{ + /** + * @var Monolog\Handler\SocketHandler + */ + private $handler; + + /** + * @var resource + */ + private $res; + + /** + * @expectedException UnexpectedValueException + */ + public function testInvalidHostname() + { + $this->createHandler('garbage://here'); + $this->writeRecord('data'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testBadConnectionTimeout() + { + $this->createHandler('localhost:1234'); + $this->handler->setConnectionTimeout(-1); + } + + public function testSetConnectionTimeout() + { + $this->createHandler('localhost:1234'); + $this->handler->setConnectionTimeout(10.1); + $this->assertEquals(10.1, $this->handler->getConnectionTimeout()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testBadTimeout() + { + $this->createHandler('localhost:1234'); + $this->handler->setTimeout(-1); + } + + public function testSetTimeout() + { + $this->createHandler('localhost:1234'); + $this->handler->setTimeout(10.25); + $this->assertEquals(10.25, $this->handler->getTimeout()); + } + + public function testSetWritingTimeout() + { + $this->createHandler('localhost:1234'); + $this->handler->setWritingTimeout(10.25); + $this->assertEquals(10.25, $this->handler->getWritingTimeout()); + } + + public function testSetConnectionString() + { + $this->createHandler('tcp://localhost:9090'); + $this->assertEquals('tcp://localhost:9090', $this->handler->getConnectionString()); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testExceptionIsThrownOnFsockopenError() + { + $this->setMockHandler(array('fsockopen')); + $this->handler->expects($this->once()) + ->method('fsockopen') + ->will($this->returnValue(false)); + $this->writeRecord('Hello world'); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testExceptionIsThrownOnPfsockopenError() + { + $this->setMockHandler(array('pfsockopen')); + $this->handler->expects($this->once()) + ->method('pfsockopen') + ->will($this->returnValue(false)); + $this->handler->setPersistent(true); + $this->writeRecord('Hello world'); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testExceptionIsThrownIfCannotSetTimeout() + { + $this->setMockHandler(array('streamSetTimeout')); + $this->handler->expects($this->once()) + ->method('streamSetTimeout') + ->will($this->returnValue(false)); + $this->writeRecord('Hello world'); + } + + /** + * @expectedException RuntimeException + */ + public function testWriteFailsOnIfFwriteReturnsFalse() + { + $this->setMockHandler(array('fwrite')); + + $callback = function ($arg) { + $map = array( + 'Hello world' => 6, + 'world' => false, + ); + + return $map[$arg]; + }; + + $this->handler->expects($this->exactly(2)) + ->method('fwrite') + ->will($this->returnCallback($callback)); + + $this->writeRecord('Hello world'); + } + + /** + * @expectedException RuntimeException + */ + public function testWriteFailsIfStreamTimesOut() + { + $this->setMockHandler(array('fwrite', 'streamGetMetadata')); + + $callback = function ($arg) { + $map = array( + 'Hello world' => 6, + 'world' => 5, + ); + + return $map[$arg]; + }; + + $this->handler->expects($this->exactly(1)) + ->method('fwrite') + ->will($this->returnCallback($callback)); + $this->handler->expects($this->exactly(1)) + ->method('streamGetMetadata') + ->will($this->returnValue(array('timed_out' => true))); + + $this->writeRecord('Hello world'); + } + + /** + * @expectedException RuntimeException + */ + public function testWriteFailsOnIncompleteWrite() + { + $this->setMockHandler(array('fwrite', 'streamGetMetadata')); + + $res = $this->res; + $callback = function ($string) use ($res) { + fclose($res); + + return strlen('Hello'); + }; + + $this->handler->expects($this->exactly(1)) + ->method('fwrite') + ->will($this->returnCallback($callback)); + $this->handler->expects($this->exactly(1)) + ->method('streamGetMetadata') + ->will($this->returnValue(array('timed_out' => false))); + + $this->writeRecord('Hello world'); + } + + public function testWriteWithMemoryFile() + { + $this->setMockHandler(); + $this->writeRecord('test1'); + $this->writeRecord('test2'); + $this->writeRecord('test3'); + fseek($this->res, 0); + $this->assertEquals('test1test2test3', fread($this->res, 1024)); + } + + public function testWriteWithMock() + { + $this->setMockHandler(array('fwrite')); + + $callback = function ($arg) { + $map = array( + 'Hello world' => 6, + 'world' => 5, + ); + + return $map[$arg]; + }; + + $this->handler->expects($this->exactly(2)) + ->method('fwrite') + ->will($this->returnCallback($callback)); + + $this->writeRecord('Hello world'); + } + + public function testClose() + { + $this->setMockHandler(); + $this->writeRecord('Hello world'); + $this->assertInternalType('resource', $this->res); + $this->handler->close(); + $this->assertFalse(is_resource($this->res), "Expected resource to be closed after closing handler"); + } + + public function testCloseDoesNotClosePersistentSocket() + { + $this->setMockHandler(); + $this->handler->setPersistent(true); + $this->writeRecord('Hello world'); + $this->assertTrue(is_resource($this->res)); + $this->handler->close(); + $this->assertTrue(is_resource($this->res)); + } + + /** + * @expectedException \RuntimeException + */ + public function testAvoidInfiniteLoopWhenNoDataIsWrittenForAWritingTimeoutSeconds() + { + $this->setMockHandler(array('fwrite', 'streamGetMetadata')); + + $this->handler->expects($this->any()) + ->method('fwrite') + ->will($this->returnValue(0)); + + $this->handler->expects($this->any()) + ->method('streamGetMetadata') + ->will($this->returnValue(array('timed_out' => false))); + + $this->handler->setWritingTimeout(1); + + $this->writeRecord('Hello world'); + } + + private function createHandler($connectionString) + { + $this->handler = new SocketHandler($connectionString); + $this->handler->setFormatter($this->getIdentityFormatter()); + } + + private function writeRecord($string) + { + $this->handler->handle($this->getRecord(Logger::WARNING, $string)); + } + + private function setMockHandler(array $methods = array()) + { + $this->res = fopen('php://memory', 'a'); + + $defaultMethods = array('fsockopen', 'pfsockopen', 'streamSetTimeout'); + $newMethods = array_diff($methods, $defaultMethods); + + $finalMethods = array_merge($defaultMethods, $newMethods); + + $this->handler = $this->getMock( + '\Monolog\Handler\SocketHandler', $finalMethods, array('localhost:1234') + ); + + if (!in_array('fsockopen', $methods)) { + $this->handler->expects($this->any()) + ->method('fsockopen') + ->will($this->returnValue($this->res)); + } + + if (!in_array('pfsockopen', $methods)) { + $this->handler->expects($this->any()) + ->method('pfsockopen') + ->will($this->returnValue($this->res)); + } + + if (!in_array('streamSetTimeout', $methods)) { + $this->handler->expects($this->any()) + ->method('streamSetTimeout') + ->will($this->returnValue(true)); + } + + $this->handler->setFormatter($this->getIdentityFormatter()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php new file mode 100644 index 0000000..487030f --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/StreamHandlerTest.php @@ -0,0 +1,184 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class StreamHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWrite() + { + $handle = fopen('php://memory', 'a+'); + $handler = new StreamHandler($handle); + $handler->setFormatter($this->getIdentityFormatter()); + $handler->handle($this->getRecord(Logger::WARNING, 'test')); + $handler->handle($this->getRecord(Logger::WARNING, 'test2')); + $handler->handle($this->getRecord(Logger::WARNING, 'test3')); + fseek($handle, 0); + $this->assertEquals('testtest2test3', fread($handle, 100)); + } + + /** + * @covers Monolog\Handler\StreamHandler::close + */ + public function testCloseKeepsExternalHandlersOpen() + { + $handle = fopen('php://memory', 'a+'); + $handler = new StreamHandler($handle); + $this->assertTrue(is_resource($handle)); + $handler->close(); + $this->assertTrue(is_resource($handle)); + } + + /** + * @covers Monolog\Handler\StreamHandler::close + */ + public function testClose() + { + $handler = new StreamHandler('php://memory'); + $handler->handle($this->getRecord(Logger::WARNING, 'test')); + $streamProp = new \ReflectionProperty('Monolog\Handler\StreamHandler', 'stream'); + $streamProp->setAccessible(true); + $handle = $streamProp->getValue($handler); + + $this->assertTrue(is_resource($handle)); + $handler->close(); + $this->assertFalse(is_resource($handle)); + } + + /** + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteCreatesTheStreamResource() + { + $handler = new StreamHandler('php://memory'); + $handler->handle($this->getRecord()); + } + + /** + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteLocking() + { + $temp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'monolog_locked_log'; + $handler = new StreamHandler($temp, Logger::DEBUG, true, null, true); + $handler->handle($this->getRecord()); + } + + /** + * @expectedException LogicException + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteMissingResource() + { + $handler = new StreamHandler(null); + $handler->handle($this->getRecord()); + } + + public function invalidArgumentProvider() + { + return array( + array(1), + array(array()), + array(array('bogus://url')), + ); + } + + /** + * @dataProvider invalidArgumentProvider + * @expectedException InvalidArgumentException + * @covers Monolog\Handler\StreamHandler::__construct + */ + public function testWriteInvalidArgument($invalidArgument) + { + $handler = new StreamHandler($invalidArgument); + } + + /** + * @expectedException UnexpectedValueException + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteInvalidResource() + { + $handler = new StreamHandler('bogus://url'); + $handler->handle($this->getRecord()); + } + + /** + * @expectedException UnexpectedValueException + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteNonExistingResource() + { + $handler = new StreamHandler('ftp://foo/bar/baz/'.rand(0, 10000)); + $handler->handle($this->getRecord()); + } + + /** + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteNonExistingPath() + { + $handler = new StreamHandler(sys_get_temp_dir().'/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); + $handler->handle($this->getRecord()); + } + + /** + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteNonExistingFileResource() + { + $handler = new StreamHandler('file://'.sys_get_temp_dir().'/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); + $handler->handle($this->getRecord()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessageRegExp /There is no existing directory at/ + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteNonExistingAndNotCreatablePath() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Permissions checks can not run on windows'); + } + $handler = new StreamHandler('/foo/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); + $handler->handle($this->getRecord()); + } + + /** + * @expectedException Exception + * @expectedExceptionMessageRegExp /There is no existing directory at/ + * @covers Monolog\Handler\StreamHandler::__construct + * @covers Monolog\Handler\StreamHandler::write + */ + public function testWriteNonExistingAndNotCreatableFileResource() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Permissions checks can not run on windows'); + } + $handler = new StreamHandler('file:///foo/bar/'.rand(0, 10000).DIRECTORY_SEPARATOR.rand(0, 10000)); + $handler->handle($this->getRecord()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SwiftMailerHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SwiftMailerHandlerTest.php new file mode 100644 index 0000000..1d62940 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SwiftMailerHandlerTest.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 Monolog\Handler; + +use Monolog\Logger; +use Monolog\TestCase; + +class SwiftMailerHandlerTest extends TestCase +{ + /** @var \Swift_Mailer|\PHPUnit_Framework_MockObject_MockObject */ + private $mailer; + + public function setUp() + { + $this->mailer = $this + ->getMockBuilder('Swift_Mailer') + ->disableOriginalConstructor() + ->getMock(); + } + + public function testMessageCreationIsLazyWhenUsingCallback() + { + $this->mailer->expects($this->never()) + ->method('send'); + + $callback = function () { + throw new \RuntimeException('Swift_Message creation callback should not have been called in this test'); + }; + $handler = new SwiftMailerHandler($this->mailer, $callback); + + $records = array( + $this->getRecord(Logger::DEBUG), + $this->getRecord(Logger::INFO), + ); + $handler->handleBatch($records); + } + + public function testMessageCanBeCustomizedGivenLoggedData() + { + // Wire Mailer to expect a specific Swift_Message with a customized Subject + $expectedMessage = new \Swift_Message(); + $this->mailer->expects($this->once()) + ->method('send') + ->with($this->callback(function ($value) use ($expectedMessage) { + return $value instanceof \Swift_Message + && $value->getSubject() === 'Emergency' + && $value === $expectedMessage; + })); + + // Callback dynamically changes subject based on number of logged records + $callback = function ($content, array $records) use ($expectedMessage) { + $subject = count($records) > 0 ? 'Emergency' : 'Normal'; + $expectedMessage->setSubject($subject); + + return $expectedMessage; + }; + $handler = new SwiftMailerHandler($this->mailer, $callback); + + // Logging 1 record makes this an Emergency + $records = array( + $this->getRecord(Logger::EMERGENCY), + ); + $handler->handleBatch($records); + } + + public function testMessageSubjectFormatting() + { + // Wire Mailer to expect a specific Swift_Message with a customized Subject + $messageTemplate = new \Swift_Message(); + $messageTemplate->setSubject('Alert: %level_name% %message%'); + $receivedMessage = null; + + $this->mailer->expects($this->once()) + ->method('send') + ->with($this->callback(function ($value) use (&$receivedMessage) { + $receivedMessage = $value; + return true; + })); + + $handler = new SwiftMailerHandler($this->mailer, $messageTemplate); + + $records = array( + $this->getRecord(Logger::EMERGENCY), + ); + $handler->handleBatch($records); + + $this->assertEquals('Alert: EMERGENCY test', $receivedMessage->getSubject()); + } + + public function testMessageHaveUniqueId() + { + $messageTemplate = new \Swift_Message(); + $handler = new SwiftMailerHandler($this->mailer, $messageTemplate); + + $method = new \ReflectionMethod('Monolog\Handler\SwiftMailerHandler', 'buildMessage'); + $method->setAccessible(true); + $method->invokeArgs($handler, array($messageTemplate, array())); + + $builtMessage1 = $method->invoke($handler, $messageTemplate, array()); + $builtMessage2 = $method->invoke($handler, $messageTemplate, array()); + + $this->assertFalse($builtMessage1->getId() === $builtMessage2->getId(), 'Two different messages have the same id'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SyslogHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SyslogHandlerTest.php new file mode 100644 index 0000000..8f9e46b --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SyslogHandlerTest.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 Monolog\Handler; + +use Monolog\Logger; + +class SyslogHandlerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Monolog\Handler\SyslogHandler::__construct + */ + public function testConstruct() + { + $handler = new SyslogHandler('test'); + $this->assertInstanceOf('Monolog\Handler\SyslogHandler', $handler); + + $handler = new SyslogHandler('test', LOG_USER); + $this->assertInstanceOf('Monolog\Handler\SyslogHandler', $handler); + + $handler = new SyslogHandler('test', 'user'); + $this->assertInstanceOf('Monolog\Handler\SyslogHandler', $handler); + + $handler = new SyslogHandler('test', LOG_USER, Logger::DEBUG, true, LOG_PERROR); + $this->assertInstanceOf('Monolog\Handler\SyslogHandler', $handler); + } + + /** + * @covers Monolog\Handler\SyslogHandler::__construct + */ + public function testConstructInvalidFacility() + { + $this->setExpectedException('UnexpectedValueException'); + $handler = new SyslogHandler('test', 'unknown'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/SyslogUdpHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/SyslogUdpHandlerTest.php new file mode 100644 index 0000000..7ee8a98 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/SyslogUdpHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; + +/** + * @requires extension sockets + */ +class SyslogUdpHandlerTest extends TestCase +{ + /** + * @expectedException UnexpectedValueException + */ + public function testWeValidateFacilities() + { + $handler = new SyslogUdpHandler("ip", null, "invalidFacility"); + } + + public function testWeSplitIntoLines() + { + $time = '2014-01-07T12:34'; + $pid = getmypid(); + $host = gethostname(); + + $handler = $this->getMockBuilder('\Monolog\Handler\SyslogUdpHandler') + ->setConstructorArgs(array("127.0.0.1", 514, "authpriv")) + ->setMethods(array('getDateTime')) + ->getMock(); + + $handler->method('getDateTime') + ->willReturn($time); + + $handler->setFormatter(new \Monolog\Formatter\ChromePHPFormatter()); + + $socket = $this->getMock('\Monolog\Handler\SyslogUdp\UdpSocket', array('write'), array('lol', 'lol')); + $socket->expects($this->at(0)) + ->method('write') + ->with("lol", "<".(LOG_AUTHPRIV + LOG_WARNING).">1 $time $host php $pid - - "); + $socket->expects($this->at(1)) + ->method('write') + ->with("hej", "<".(LOG_AUTHPRIV + LOG_WARNING).">1 $time $host php $pid - - "); + + $handler->setSocket($socket); + + $handler->handle($this->getRecordWithMessage("hej\nlol")); + } + + public function testSplitWorksOnEmptyMsg() + { + $handler = new SyslogUdpHandler("127.0.0.1", 514, "authpriv"); + $handler->setFormatter($this->getIdentityFormatter()); + + $socket = $this->getMock('\Monolog\Handler\SyslogUdp\UdpSocket', array('write'), array('lol', 'lol')); + $socket->expects($this->never()) + ->method('write'); + + $handler->setSocket($socket); + + $handler->handle($this->getRecordWithMessage(null)); + } + + protected function getRecordWithMessage($msg) + { + return array('message' => $msg, 'level' => \Monolog\Logger::WARNING, 'context' => null, 'extra' => array(), 'channel' => 'lol'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.php new file mode 100644 index 0000000..bfb8d3d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/TestHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +/** + * @covers Monolog\Handler\TestHandler + */ +class TestHandlerTest extends TestCase +{ + /** + * @dataProvider methodProvider + */ + public function testHandler($method, $level) + { + $handler = new TestHandler; + $record = $this->getRecord($level, 'test'.$method); + $this->assertFalse($handler->hasRecords($level)); + $this->assertFalse($handler->hasRecord($record, $level)); + $this->assertFalse($handler->{'has'.$method}($record), 'has'.$method); + $this->assertFalse($handler->{'has'.$method.'ThatContains'}('test'), 'has'.$method.'ThatContains'); + $this->assertFalse($handler->{'has'.$method.'ThatPasses'}(function ($rec) { + return true; + }), 'has'.$method.'ThatPasses'); + $this->assertFalse($handler->{'has'.$method.'ThatMatches'}('/test\w+/')); + $this->assertFalse($handler->{'has'.$method.'Records'}(), 'has'.$method.'Records'); + $handler->handle($record); + + $this->assertFalse($handler->{'has'.$method}('bar'), 'has'.$method); + $this->assertTrue($handler->hasRecords($level)); + $this->assertTrue($handler->hasRecord($record, $level)); + $this->assertTrue($handler->{'has'.$method}($record), 'has'.$method); + $this->assertTrue($handler->{'has'.$method}('test'.$method), 'has'.$method); + $this->assertTrue($handler->{'has'.$method.'ThatContains'}('test'), 'has'.$method.'ThatContains'); + $this->assertTrue($handler->{'has'.$method.'ThatPasses'}(function ($rec) { + return true; + }), 'has'.$method.'ThatPasses'); + $this->assertTrue($handler->{'has'.$method.'ThatMatches'}('/test\w+/')); + $this->assertTrue($handler->{'has'.$method.'Records'}(), 'has'.$method.'Records'); + + $records = $handler->getRecords(); + unset($records[0]['formatted']); + $this->assertEquals(array($record), $records); + } + + public function methodProvider() + { + return array( + array('Emergency', Logger::EMERGENCY), + array('Alert' , Logger::ALERT), + array('Critical' , Logger::CRITICAL), + array('Error' , Logger::ERROR), + array('Warning' , Logger::WARNING), + array('Info' , Logger::INFO), + array('Notice' , Logger::NOTICE), + array('Debug' , Logger::DEBUG), + ); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/UdpSocketTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/UdpSocketTest.php new file mode 100644 index 0000000..fa524d0 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/UdpSocketTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Handler\SyslogUdp\UdpSocket; + +/** + * @requires extension sockets + */ +class UdpSocketTest extends TestCase +{ + public function testWeDoNotTruncateShortMessages() + { + $socket = $this->getMock('\Monolog\Handler\SyslogUdp\UdpSocket', array('send'), array('lol', 'lol')); + + $socket->expects($this->at(0)) + ->method('send') + ->with("HEADER: The quick brown fox jumps over the lazy dog"); + + $socket->write("The quick brown fox jumps over the lazy dog", "HEADER: "); + } + + public function testLongMessagesAreTruncated() + { + $socket = $this->getMock('\Monolog\Handler\SyslogUdp\UdpSocket', array('send'), array('lol', 'lol')); + + $truncatedString = str_repeat("derp", 16254).'d'; + + $socket->expects($this->exactly(1)) + ->method('send') + ->with("HEADER" . $truncatedString); + + $longString = str_repeat("derp", 20000); + + $socket->write($longString, "HEADER"); + } + + public function testDoubleCloseDoesNotError() + { + $socket = new UdpSocket('127.0.0.1', 514); + $socket->close(); + $socket->close(); + } + + /** + * @expectedException LogicException + */ + public function testWriteAfterCloseErrors() + { + $socket = new UdpSocket('127.0.0.1', 514); + $socket->close(); + $socket->write('foo', "HEADER"); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.php new file mode 100644 index 0000000..8d37a1f --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/WhatFailureGroupHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; +use Monolog\Logger; + +class WhatFailureGroupHandlerTest extends TestCase +{ + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::__construct + * @expectedException InvalidArgumentException + */ + public function testConstructorOnlyTakesHandler() + { + new WhatFailureGroupHandler(array(new TestHandler(), "foo")); + } + + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::__construct + * @covers Monolog\Handler\WhatFailureGroupHandler::handle + */ + public function testHandle() + { + $testHandlers = array(new TestHandler(), new TestHandler()); + $handler = new WhatFailureGroupHandler($testHandlers); + $handler->handle($this->getRecord(Logger::DEBUG)); + $handler->handle($this->getRecord(Logger::INFO)); + foreach ($testHandlers as $test) { + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + } + } + + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::handleBatch + */ + public function testHandleBatch() + { + $testHandlers = array(new TestHandler(), new TestHandler()); + $handler = new WhatFailureGroupHandler($testHandlers); + $handler->handleBatch(array($this->getRecord(Logger::DEBUG), $this->getRecord(Logger::INFO))); + foreach ($testHandlers as $test) { + $this->assertTrue($test->hasDebugRecords()); + $this->assertTrue($test->hasInfoRecords()); + $this->assertTrue(count($test->getRecords()) === 2); + } + } + + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::isHandling + */ + public function testIsHandling() + { + $testHandlers = array(new TestHandler(Logger::ERROR), new TestHandler(Logger::WARNING)); + $handler = new WhatFailureGroupHandler($testHandlers); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::ERROR))); + $this->assertTrue($handler->isHandling($this->getRecord(Logger::WARNING))); + $this->assertFalse($handler->isHandling($this->getRecord(Logger::DEBUG))); + } + + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::handle + */ + public function testHandleUsesProcessors() + { + $test = new TestHandler(); + $handler = new WhatFailureGroupHandler(array($test)); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasWarningRecords()); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + } + + /** + * @covers Monolog\Handler\WhatFailureGroupHandler::handle + */ + public function testHandleException() + { + $test = new TestHandler(); + $exception = new ExceptionTestHandler(); + $handler = new WhatFailureGroupHandler(array($exception, $test, $exception)); + $handler->pushProcessor(function ($record) { + $record['extra']['foo'] = true; + + return $record; + }); + $handler->handle($this->getRecord(Logger::WARNING)); + $this->assertTrue($test->hasWarningRecords()); + $records = $test->getRecords(); + $this->assertTrue($records[0]['extra']['foo']); + } +} + +class ExceptionTestHandler extends TestHandler +{ + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + parent::handle($record); + + throw new \Exception("ExceptionTestHandler::handle"); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Handler/ZendMonitorHandlerTest.php b/vendor/monolog/monolog/tests/Monolog/Handler/ZendMonitorHandlerTest.php new file mode 100644 index 0000000..69b001e --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Handler/ZendMonitorHandlerTest.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 Monolog\Handler; + +use Monolog\TestCase; + +class ZendMonitorHandlerTest extends TestCase +{ + protected $zendMonitorHandler; + + public function setUp() + { + if (!function_exists('zend_monitor_custom_event')) { + $this->markTestSkipped('ZendServer is not installed'); + } + } + + /** + * @covers Monolog\Handler\ZendMonitorHandler::write + */ + public function testWrite() + { + $record = $this->getRecord(); + $formatterResult = array( + 'message' => $record['message'], + ); + + $zendMonitor = $this->getMockBuilder('Monolog\Handler\ZendMonitorHandler') + ->setMethods(array('writeZendMonitorCustomEvent', 'getDefaultFormatter')) + ->getMock(); + + $formatterMock = $this->getMockBuilder('Monolog\Formatter\NormalizerFormatter') + ->disableOriginalConstructor() + ->getMock(); + + $formatterMock->expects($this->once()) + ->method('format') + ->will($this->returnValue($formatterResult)); + + $zendMonitor->expects($this->once()) + ->method('getDefaultFormatter') + ->will($this->returnValue($formatterMock)); + + $levelMap = $zendMonitor->getLevelMap(); + + $zendMonitor->expects($this->once()) + ->method('writeZendMonitorCustomEvent') + ->with($levelMap[$record['level']], $record['message'], $formatterResult); + + $zendMonitor->handle($record); + } + + /** + * @covers Monolog\Handler\ZendMonitorHandler::getDefaultFormatter + */ + public function testGetDefaultFormatterReturnsNormalizerFormatter() + { + $zendMonitor = new ZendMonitorHandler(); + $this->assertInstanceOf('Monolog\Formatter\NormalizerFormatter', $zendMonitor->getDefaultFormatter()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/LoggerTest.php b/vendor/monolog/monolog/tests/Monolog/LoggerTest.php new file mode 100644 index 0000000..1ecc34a --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/LoggerTest.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 Monolog; + +use Monolog\Processor\WebProcessor; +use Monolog\Handler\TestHandler; + +class LoggerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @covers Monolog\Logger::getName + */ + public function testGetName() + { + $logger = new Logger('foo'); + $this->assertEquals('foo', $logger->getName()); + } + + /** + * @covers Monolog\Logger::getLevelName + */ + public function testGetLevelName() + { + $this->assertEquals('ERROR', Logger::getLevelName(Logger::ERROR)); + } + + /** + * @covers Monolog\Logger::withName + */ + public function testWithName() + { + $first = new Logger('first', array($handler = new TestHandler())); + $second = $first->withName('second'); + + $this->assertSame('first', $first->getName()); + $this->assertSame('second', $second->getName()); + $this->assertSame($handler, $second->popHandler()); + } + + /** + * @covers Monolog\Logger::toMonologLevel + */ + public function testConvertPSR3ToMonologLevel() + { + $this->assertEquals(Logger::toMonologLevel('debug'), 100); + $this->assertEquals(Logger::toMonologLevel('info'), 200); + $this->assertEquals(Logger::toMonologLevel('notice'), 250); + $this->assertEquals(Logger::toMonologLevel('warning'), 300); + $this->assertEquals(Logger::toMonologLevel('error'), 400); + $this->assertEquals(Logger::toMonologLevel('critical'), 500); + $this->assertEquals(Logger::toMonologLevel('alert'), 550); + $this->assertEquals(Logger::toMonologLevel('emergency'), 600); + } + + /** + * @covers Monolog\Logger::getLevelName + * @expectedException InvalidArgumentException + */ + public function testGetLevelNameThrows() + { + Logger::getLevelName(5); + } + + /** + * @covers Monolog\Logger::__construct + */ + public function testChannel() + { + $logger = new Logger('foo'); + $handler = new TestHandler; + $logger->pushHandler($handler); + $logger->addWarning('test'); + list($record) = $handler->getRecords(); + $this->assertEquals('foo', $record['channel']); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testLog() + { + $logger = new Logger(__METHOD__); + + $handler = $this->getMock('Monolog\Handler\NullHandler', array('handle')); + $handler->expects($this->once()) + ->method('handle'); + $logger->pushHandler($handler); + + $this->assertTrue($logger->addWarning('test')); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testLogNotHandled() + { + $logger = new Logger(__METHOD__); + + $handler = $this->getMock('Monolog\Handler\NullHandler', array('handle'), array(Logger::ERROR)); + $handler->expects($this->never()) + ->method('handle'); + $logger->pushHandler($handler); + + $this->assertFalse($logger->addWarning('test')); + } + + public function testHandlersInCtor() + { + $handler1 = new TestHandler; + $handler2 = new TestHandler; + $logger = new Logger(__METHOD__, array($handler1, $handler2)); + + $this->assertEquals($handler1, $logger->popHandler()); + $this->assertEquals($handler2, $logger->popHandler()); + } + + public function testProcessorsInCtor() + { + $processor1 = new WebProcessor; + $processor2 = new WebProcessor; + $logger = new Logger(__METHOD__, array(), array($processor1, $processor2)); + + $this->assertEquals($processor1, $logger->popProcessor()); + $this->assertEquals($processor2, $logger->popProcessor()); + } + + /** + * @covers Monolog\Logger::pushHandler + * @covers Monolog\Logger::popHandler + * @expectedException LogicException + */ + public function testPushPopHandler() + { + $logger = new Logger(__METHOD__); + $handler1 = new TestHandler; + $handler2 = new TestHandler; + + $logger->pushHandler($handler1); + $logger->pushHandler($handler2); + + $this->assertEquals($handler2, $logger->popHandler()); + $this->assertEquals($handler1, $logger->popHandler()); + $logger->popHandler(); + } + + /** + * @covers Monolog\Logger::setHandlers + */ + public function testSetHandlers() + { + $logger = new Logger(__METHOD__); + $handler1 = new TestHandler; + $handler2 = new TestHandler; + + $logger->pushHandler($handler1); + $logger->setHandlers(array($handler2)); + + // handler1 has been removed + $this->assertEquals(array($handler2), $logger->getHandlers()); + + $logger->setHandlers(array( + "AMapKey" => $handler1, + "Woop" => $handler2, + )); + + // Keys have been scrubbed + $this->assertEquals(array($handler1, $handler2), $logger->getHandlers()); + } + + /** + * @covers Monolog\Logger::pushProcessor + * @covers Monolog\Logger::popProcessor + * @expectedException LogicException + */ + public function testPushPopProcessor() + { + $logger = new Logger(__METHOD__); + $processor1 = new WebProcessor; + $processor2 = new WebProcessor; + + $logger->pushProcessor($processor1); + $logger->pushProcessor($processor2); + + $this->assertEquals($processor2, $logger->popProcessor()); + $this->assertEquals($processor1, $logger->popProcessor()); + $logger->popProcessor(); + } + + /** + * @covers Monolog\Logger::pushProcessor + * @expectedException InvalidArgumentException + */ + public function testPushProcessorWithNonCallable() + { + $logger = new Logger(__METHOD__); + + $logger->pushProcessor(new \stdClass()); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testProcessorsAreExecuted() + { + $logger = new Logger(__METHOD__); + $handler = new TestHandler; + $logger->pushHandler($handler); + $logger->pushProcessor(function ($record) { + $record['extra']['win'] = true; + + return $record; + }); + $logger->addError('test'); + list($record) = $handler->getRecords(); + $this->assertTrue($record['extra']['win']); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testProcessorsAreCalledOnlyOnce() + { + $logger = new Logger(__METHOD__); + $handler = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler->expects($this->any()) + ->method('handle') + ->will($this->returnValue(true)) + ; + $logger->pushHandler($handler); + + $processor = $this->getMockBuilder('Monolog\Processor\WebProcessor') + ->disableOriginalConstructor() + ->setMethods(array('__invoke')) + ->getMock() + ; + $processor->expects($this->once()) + ->method('__invoke') + ->will($this->returnArgument(0)) + ; + $logger->pushProcessor($processor); + + $logger->addError('test'); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testProcessorsNotCalledWhenNotHandled() + { + $logger = new Logger(__METHOD__); + $handler = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler->expects($this->once()) + ->method('isHandling') + ->will($this->returnValue(false)) + ; + $logger->pushHandler($handler); + $that = $this; + $logger->pushProcessor(function ($record) use ($that) { + $that->fail('The processor should not be called'); + }); + $logger->addAlert('test'); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testHandlersNotCalledBeforeFirstHandling() + { + $logger = new Logger(__METHOD__); + + $handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler1->expects($this->never()) + ->method('isHandling') + ->will($this->returnValue(false)) + ; + $handler1->expects($this->once()) + ->method('handle') + ->will($this->returnValue(false)) + ; + $logger->pushHandler($handler1); + + $handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler2->expects($this->once()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler2->expects($this->once()) + ->method('handle') + ->will($this->returnValue(false)) + ; + $logger->pushHandler($handler2); + + $handler3 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler3->expects($this->once()) + ->method('isHandling') + ->will($this->returnValue(false)) + ; + $handler3->expects($this->never()) + ->method('handle') + ; + $logger->pushHandler($handler3); + + $logger->debug('test'); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testHandlersNotCalledBeforeFirstHandlingWithAssocArray() + { + $handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler1->expects($this->never()) + ->method('isHandling') + ->will($this->returnValue(false)) + ; + $handler1->expects($this->once()) + ->method('handle') + ->will($this->returnValue(false)) + ; + + $handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler2->expects($this->once()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler2->expects($this->once()) + ->method('handle') + ->will($this->returnValue(false)) + ; + + $handler3 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler3->expects($this->once()) + ->method('isHandling') + ->will($this->returnValue(false)) + ; + $handler3->expects($this->never()) + ->method('handle') + ; + + $logger = new Logger(__METHOD__, array('last' => $handler3, 'second' => $handler2, 'first' => $handler1)); + + $logger->debug('test'); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testBubblingWhenTheHandlerReturnsFalse() + { + $logger = new Logger(__METHOD__); + + $handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler1->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler1->expects($this->once()) + ->method('handle') + ->will($this->returnValue(false)) + ; + $logger->pushHandler($handler1); + + $handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler2->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler2->expects($this->once()) + ->method('handle') + ->will($this->returnValue(false)) + ; + $logger->pushHandler($handler2); + + $logger->debug('test'); + } + + /** + * @covers Monolog\Logger::addRecord + */ + public function testNotBubblingWhenTheHandlerReturnsTrue() + { + $logger = new Logger(__METHOD__); + + $handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler1->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler1->expects($this->never()) + ->method('handle') + ; + $logger->pushHandler($handler1); + + $handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler2->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + $handler2->expects($this->once()) + ->method('handle') + ->will($this->returnValue(true)) + ; + $logger->pushHandler($handler2); + + $logger->debug('test'); + } + + /** + * @covers Monolog\Logger::isHandling + */ + public function testIsHandling() + { + $logger = new Logger(__METHOD__); + + $handler1 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler1->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(false)) + ; + + $logger->pushHandler($handler1); + $this->assertFalse($logger->isHandling(Logger::DEBUG)); + + $handler2 = $this->getMock('Monolog\Handler\HandlerInterface'); + $handler2->expects($this->any()) + ->method('isHandling') + ->will($this->returnValue(true)) + ; + + $logger->pushHandler($handler2); + $this->assertTrue($logger->isHandling(Logger::DEBUG)); + } + + /** + * @dataProvider logMethodProvider + * @covers Monolog\Logger::addDebug + * @covers Monolog\Logger::addInfo + * @covers Monolog\Logger::addNotice + * @covers Monolog\Logger::addWarning + * @covers Monolog\Logger::addError + * @covers Monolog\Logger::addCritical + * @covers Monolog\Logger::addAlert + * @covers Monolog\Logger::addEmergency + * @covers Monolog\Logger::debug + * @covers Monolog\Logger::info + * @covers Monolog\Logger::notice + * @covers Monolog\Logger::warn + * @covers Monolog\Logger::err + * @covers Monolog\Logger::crit + * @covers Monolog\Logger::alert + * @covers Monolog\Logger::emerg + */ + public function testLogMethods($method, $expectedLevel) + { + $logger = new Logger('foo'); + $handler = new TestHandler; + $logger->pushHandler($handler); + $logger->{$method}('test'); + list($record) = $handler->getRecords(); + $this->assertEquals($expectedLevel, $record['level']); + } + + public function logMethodProvider() + { + return array( + // monolog methods + array('addDebug', Logger::DEBUG), + array('addInfo', Logger::INFO), + array('addNotice', Logger::NOTICE), + array('addWarning', Logger::WARNING), + array('addError', Logger::ERROR), + array('addCritical', Logger::CRITICAL), + array('addAlert', Logger::ALERT), + array('addEmergency', Logger::EMERGENCY), + + // ZF/Sf2 compat methods + array('debug', Logger::DEBUG), + array('info', Logger::INFO), + array('notice', Logger::NOTICE), + array('warn', Logger::WARNING), + array('err', Logger::ERROR), + array('crit', Logger::CRITICAL), + array('alert', Logger::ALERT), + array('emerg', Logger::EMERGENCY), + ); + } + + /** + * @dataProvider setTimezoneProvider + * @covers Monolog\Logger::setTimezone + */ + public function testSetTimezone($tz) + { + Logger::setTimezone($tz); + $logger = new Logger('foo'); + $handler = new TestHandler; + $logger->pushHandler($handler); + $logger->info('test'); + list($record) = $handler->getRecords(); + $this->assertEquals($tz, $record['datetime']->getTimezone()); + } + + public function setTimezoneProvider() + { + return array_map( + function ($tz) { return array(new \DateTimeZone($tz)); }, + \DateTimeZone::listIdentifiers() + ); + } + + /** + * @dataProvider useMicrosecondTimestampsProvider + * @covers Monolog\Logger::useMicrosecondTimestamps + * @covers Monolog\Logger::addRecord + */ + public function testUseMicrosecondTimestamps($micro, $assert) + { + $logger = new Logger('foo'); + $logger->useMicrosecondTimestamps($micro); + $handler = new TestHandler; + $logger->pushHandler($handler); + $logger->info('test'); + list($record) = $handler->getRecords(); + $this->{$assert}('000000', $record['datetime']->format('u')); + } + + public function useMicrosecondTimestampsProvider() + { + return array( + // this has a very small chance of a false negative (1/10^6) + 'with microseconds' => array(true, 'assertNotSame'), + 'without microseconds' => array(false, PHP_VERSION_ID >= 70100 ? 'assertNotSame' : 'assertSame'), + ); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/GitProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/GitProcessorTest.php new file mode 100644 index 0000000..5adb505 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/GitProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class GitProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\GitProcessor::__invoke + */ + public function testProcessor() + { + $processor = new GitProcessor(); + $record = $processor($this->getRecord()); + + $this->assertArrayHasKey('git', $record['extra']); + $this->assertTrue(!is_array($record['extra']['git']['branch'])); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/IntrospectionProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/IntrospectionProcessorTest.php new file mode 100644 index 0000000..0dd411d --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/IntrospectionProcessorTest.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 Acme; + +class Tester +{ + public function test($handler, $record) + { + $handler->handle($record); + } +} + +function tester($handler, $record) +{ + $handler->handle($record); +} + +namespace Monolog\Processor; + +use Monolog\Logger; +use Monolog\TestCase; +use Monolog\Handler\TestHandler; + +class IntrospectionProcessorTest extends TestCase +{ + public function getHandler() + { + $processor = new IntrospectionProcessor(); + $handler = new TestHandler(); + $handler->pushProcessor($processor); + + return $handler; + } + + public function testProcessorFromClass() + { + $handler = $this->getHandler(); + $tester = new \Acme\Tester; + $tester->test($handler, $this->getRecord()); + list($record) = $handler->getRecords(); + $this->assertEquals(__FILE__, $record['extra']['file']); + $this->assertEquals(18, $record['extra']['line']); + $this->assertEquals('Acme\Tester', $record['extra']['class']); + $this->assertEquals('test', $record['extra']['function']); + } + + public function testProcessorFromFunc() + { + $handler = $this->getHandler(); + \Acme\tester($handler, $this->getRecord()); + list($record) = $handler->getRecords(); + $this->assertEquals(__FILE__, $record['extra']['file']); + $this->assertEquals(24, $record['extra']['line']); + $this->assertEquals(null, $record['extra']['class']); + $this->assertEquals('Acme\tester', $record['extra']['function']); + } + + public function testLevelTooLow() + { + $input = array( + 'level' => Logger::DEBUG, + 'extra' => array(), + ); + + $expected = $input; + + $processor = new IntrospectionProcessor(Logger::CRITICAL); + $actual = $processor($input); + + $this->assertEquals($expected, $actual); + } + + public function testLevelEqual() + { + $input = array( + 'level' => Logger::CRITICAL, + 'extra' => array(), + ); + + $expected = $input; + $expected['extra'] = array( + 'file' => null, + 'line' => null, + 'class' => 'ReflectionMethod', + 'function' => 'invokeArgs', + ); + + $processor = new IntrospectionProcessor(Logger::CRITICAL); + $actual = $processor($input); + + $this->assertEquals($expected, $actual); + } + + public function testLevelHigher() + { + $input = array( + 'level' => Logger::EMERGENCY, + 'extra' => array(), + ); + + $expected = $input; + $expected['extra'] = array( + 'file' => null, + 'line' => null, + 'class' => 'ReflectionMethod', + 'function' => 'invokeArgs', + ); + + $processor = new IntrospectionProcessor(Logger::CRITICAL); + $actual = $processor($input); + + $this->assertEquals($expected, $actual); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/MemoryPeakUsageProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/MemoryPeakUsageProcessorTest.php new file mode 100644 index 0000000..eb66614 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/MemoryPeakUsageProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class MemoryPeakUsageProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\MemoryPeakUsageProcessor::__invoke + * @covers Monolog\Processor\MemoryProcessor::formatBytes + */ + public function testProcessor() + { + $processor = new MemoryPeakUsageProcessor(); + $record = $processor($this->getRecord()); + $this->assertArrayHasKey('memory_peak_usage', $record['extra']); + $this->assertRegExp('#[0-9.]+ (M|K)?B$#', $record['extra']['memory_peak_usage']); + } + + /** + * @covers Monolog\Processor\MemoryPeakUsageProcessor::__invoke + * @covers Monolog\Processor\MemoryProcessor::formatBytes + */ + public function testProcessorWithoutFormatting() + { + $processor = new MemoryPeakUsageProcessor(true, false); + $record = $processor($this->getRecord()); + $this->assertArrayHasKey('memory_peak_usage', $record['extra']); + $this->assertInternalType('int', $record['extra']['memory_peak_usage']); + $this->assertGreaterThan(0, $record['extra']['memory_peak_usage']); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/MemoryUsageProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/MemoryUsageProcessorTest.php new file mode 100644 index 0000000..4692dbf --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/MemoryUsageProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class MemoryUsageProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\MemoryUsageProcessor::__invoke + * @covers Monolog\Processor\MemoryProcessor::formatBytes + */ + public function testProcessor() + { + $processor = new MemoryUsageProcessor(); + $record = $processor($this->getRecord()); + $this->assertArrayHasKey('memory_usage', $record['extra']); + $this->assertRegExp('#[0-9.]+ (M|K)?B$#', $record['extra']['memory_usage']); + } + + /** + * @covers Monolog\Processor\MemoryUsageProcessor::__invoke + * @covers Monolog\Processor\MemoryProcessor::formatBytes + */ + public function testProcessorWithoutFormatting() + { + $processor = new MemoryUsageProcessor(true, false); + $record = $processor($this->getRecord()); + $this->assertArrayHasKey('memory_usage', $record['extra']); + $this->assertInternalType('int', $record['extra']['memory_usage']); + $this->assertGreaterThan(0, $record['extra']['memory_usage']); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/MercurialProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/MercurialProcessorTest.php new file mode 100644 index 0000000..11f2b35 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/MercurialProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class MercurialProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\MercurialProcessor::__invoke + */ + public function testProcessor() + { + if (defined('PHP_WINDOWS_VERSION_BUILD')) { + exec("where hg 2>NUL", $output, $result); + } else { + exec("which hg 2>/dev/null >/dev/null", $output, $result); + } + if ($result != 0) { + $this->markTestSkipped('hg is missing'); + return; + } + + `hg init`; + $processor = new MercurialProcessor(); + $record = $processor($this->getRecord()); + + $this->assertArrayHasKey('hg', $record['extra']); + $this->assertTrue(!is_array($record['extra']['hg']['branch'])); + $this->assertTrue(!is_array($record['extra']['hg']['revision'])); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/ProcessIdProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/ProcessIdProcessorTest.php new file mode 100644 index 0000000..458d2a3 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/ProcessIdProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class ProcessIdProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\ProcessIdProcessor::__invoke + */ + public function testProcessor() + { + $processor = new ProcessIdProcessor(); + $record = $processor($this->getRecord()); + $this->assertArrayHasKey('process_id', $record['extra']); + $this->assertInternalType('int', $record['extra']['process_id']); + $this->assertGreaterThan(0, $record['extra']['process_id']); + $this->assertEquals(getmypid(), $record['extra']['process_id']); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/PsrLogMessageProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/PsrLogMessageProcessorTest.php new file mode 100644 index 0000000..029a0c0 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/PsrLogMessageProcessorTest.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 Monolog\Processor; + +class PsrLogMessageProcessorTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider getPairs + */ + public function testReplacement($val, $expected) + { + $proc = new PsrLogMessageProcessor; + + $message = $proc(array( + 'message' => '{foo}', + 'context' => array('foo' => $val), + )); + $this->assertEquals($expected, $message['message']); + } + + public function getPairs() + { + return array( + array('foo', 'foo'), + array('3', '3'), + array(3, '3'), + array(null, ''), + array(true, '1'), + array(false, ''), + array(new \stdClass, '[object stdClass]'), + array(array(), '[array]'), + ); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/TagProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/TagProcessorTest.php new file mode 100644 index 0000000..0d860c6 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/TagProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class TagProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\TagProcessor::__invoke + */ + public function testProcessor() + { + $tags = array(1, 2, 3); + $processor = new TagProcessor($tags); + $record = $processor($this->getRecord()); + + $this->assertEquals($tags, $record['extra']['tags']); + } + + /** + * @covers Monolog\Processor\TagProcessor::__invoke + */ + public function testProcessorTagModification() + { + $tags = array(1, 2, 3); + $processor = new TagProcessor($tags); + + $record = $processor($this->getRecord()); + $this->assertEquals($tags, $record['extra']['tags']); + + $processor->setTags(array('a', 'b')); + $record = $processor($this->getRecord()); + $this->assertEquals(array('a', 'b'), $record['extra']['tags']); + + $processor->addTags(array('a', 'c', 'foo' => 'bar')); + $record = $processor($this->getRecord()); + $this->assertEquals(array('a', 'b', 'a', 'c', 'foo' => 'bar'), $record['extra']['tags']); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/UidProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/UidProcessorTest.php new file mode 100644 index 0000000..5d13058 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/UidProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class UidProcessorTest extends TestCase +{ + /** + * @covers Monolog\Processor\UidProcessor::__invoke + */ + public function testProcessor() + { + $processor = new UidProcessor(); + $record = $processor($this->getRecord()); + $this->assertArrayHasKey('uid', $record['extra']); + } + + public function testGetUid() + { + $processor = new UidProcessor(10); + $this->assertEquals(10, strlen($processor->getUid())); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/Processor/WebProcessorTest.php b/vendor/monolog/monolog/tests/Monolog/Processor/WebProcessorTest.php new file mode 100644 index 0000000..4105baf --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/Processor/WebProcessorTest.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 Monolog\Processor; + +use Monolog\TestCase; + +class WebProcessorTest extends TestCase +{ + public function testProcessor() + { + $server = array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + 'HTTP_REFERER' => 'D', + 'SERVER_NAME' => 'F', + 'UNIQUE_ID' => 'G', + ); + + $processor = new WebProcessor($server); + $record = $processor($this->getRecord()); + $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']); + $this->assertEquals($server['REMOTE_ADDR'], $record['extra']['ip']); + $this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']); + $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); + $this->assertEquals($server['SERVER_NAME'], $record['extra']['server']); + $this->assertEquals($server['UNIQUE_ID'], $record['extra']['unique_id']); + } + + public function testProcessorDoNothingIfNoRequestUri() + { + $server = array( + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + ); + $processor = new WebProcessor($server); + $record = $processor($this->getRecord()); + $this->assertEmpty($record['extra']); + } + + public function testProcessorReturnNullIfNoHttpReferer() + { + $server = array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + 'SERVER_NAME' => 'F', + ); + $processor = new WebProcessor($server); + $record = $processor($this->getRecord()); + $this->assertNull($record['extra']['referrer']); + } + + public function testProcessorDoesNotAddUniqueIdIfNotPresent() + { + $server = array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + 'SERVER_NAME' => 'F', + ); + $processor = new WebProcessor($server); + $record = $processor($this->getRecord()); + $this->assertFalse(isset($record['extra']['unique_id'])); + } + + public function testProcessorAddsOnlyRequestedExtraFields() + { + $server = array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + 'SERVER_NAME' => 'F', + ); + + $processor = new WebProcessor($server, array('url', 'http_method')); + $record = $processor($this->getRecord()); + + $this->assertSame(array('url' => 'A', 'http_method' => 'C'), $record['extra']); + } + + public function testProcessorConfiguringOfExtraFields() + { + $server = array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => 'B', + 'REQUEST_METHOD' => 'C', + 'SERVER_NAME' => 'F', + ); + + $processor = new WebProcessor($server, array('url' => 'REMOTE_ADDR')); + $record = $processor($this->getRecord()); + + $this->assertSame(array('url' => 'B'), $record['extra']); + } + + /** + * @expectedException UnexpectedValueException + */ + public function testInvalidData() + { + new WebProcessor(new \stdClass); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/PsrLogCompatTest.php b/vendor/monolog/monolog/tests/Monolog/PsrLogCompatTest.php new file mode 100644 index 0000000..ab89944 --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/PsrLogCompatTest.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 Monolog; + +use Monolog\Handler\TestHandler; +use Monolog\Formatter\LineFormatter; +use Monolog\Processor\PsrLogMessageProcessor; +use Psr\Log\Test\LoggerInterfaceTest; + +class PsrLogCompatTest extends LoggerInterfaceTest +{ + private $handler; + + public function getLogger() + { + $logger = new Logger('foo'); + $logger->pushHandler($handler = new TestHandler); + $logger->pushProcessor(new PsrLogMessageProcessor); + $handler->setFormatter(new LineFormatter('%level_name% %message%')); + + $this->handler = $handler; + + return $logger; + } + + public function getLogs() + { + $convert = function ($record) { + $lower = function ($match) { + return strtolower($match[0]); + }; + + return preg_replace_callback('{^[A-Z]+}', $lower, $record['formatted']); + }; + + return array_map($convert, $this->handler->getRecords()); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/RegistryTest.php b/vendor/monolog/monolog/tests/Monolog/RegistryTest.php new file mode 100644 index 0000000..15fdfbd --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/RegistryTest.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 Monolog; + +class RegistryTest extends \PHPUnit_Framework_TestCase +{ + protected function setUp() + { + Registry::clear(); + } + + /** + * @dataProvider hasLoggerProvider + * @covers Monolog\Registry::hasLogger + */ + public function testHasLogger(array $loggersToAdd, array $loggersToCheck, array $expectedResult) + { + foreach ($loggersToAdd as $loggerToAdd) { + Registry::addLogger($loggerToAdd); + } + foreach ($loggersToCheck as $index => $loggerToCheck) { + $this->assertSame($expectedResult[$index], Registry::hasLogger($loggerToCheck)); + } + } + + public function hasLoggerProvider() + { + $logger1 = new Logger('test1'); + $logger2 = new Logger('test2'); + $logger3 = new Logger('test3'); + + return array( + // only instances + array( + array($logger1), + array($logger1, $logger2), + array(true, false), + ), + // only names + array( + array($logger1), + array('test1', 'test2'), + array(true, false), + ), + // mixed case + array( + array($logger1, $logger2), + array('test1', $logger2, 'test3', $logger3), + array(true, true, false, false), + ), + ); + } + + /** + * @covers Monolog\Registry::clear + */ + public function testClearClears() + { + Registry::addLogger(new Logger('test1'), 'log'); + Registry::clear(); + + $this->setExpectedException('\InvalidArgumentException'); + Registry::getInstance('log'); + } + + /** + * @dataProvider removedLoggerProvider + * @covers Monolog\Registry::addLogger + * @covers Monolog\Registry::removeLogger + */ + public function testRemovesLogger($loggerToAdd, $remove) + { + Registry::addLogger($loggerToAdd); + Registry::removeLogger($remove); + + $this->setExpectedException('\InvalidArgumentException'); + Registry::getInstance($loggerToAdd->getName()); + } + + public function removedLoggerProvider() + { + $logger1 = new Logger('test1'); + + return array( + array($logger1, $logger1), + array($logger1, 'test1'), + ); + } + + /** + * @covers Monolog\Registry::addLogger + * @covers Monolog\Registry::getInstance + * @covers Monolog\Registry::__callStatic + */ + public function testGetsSameLogger() + { + $logger1 = new Logger('test1'); + $logger2 = new Logger('test2'); + + Registry::addLogger($logger1, 'test1'); + Registry::addLogger($logger2); + + $this->assertSame($logger1, Registry::getInstance('test1')); + $this->assertSame($logger2, Registry::test2()); + } + + /** + * @expectedException \InvalidArgumentException + * @covers Monolog\Registry::getInstance + */ + public function testFailsOnNonExistantLogger() + { + Registry::getInstance('test1'); + } + + /** + * @covers Monolog\Registry::addLogger + */ + public function testReplacesLogger() + { + $log1 = new Logger('test1'); + $log2 = new Logger('test2'); + + Registry::addLogger($log1, 'log'); + + Registry::addLogger($log2, 'log', true); + + $this->assertSame($log2, Registry::getInstance('log')); + } + + /** + * @expectedException \InvalidArgumentException + * @covers Monolog\Registry::addLogger + */ + public function testFailsOnUnspecifiedReplacement() + { + $log1 = new Logger('test1'); + $log2 = new Logger('test2'); + + Registry::addLogger($log1, 'log'); + + Registry::addLogger($log2, 'log'); + } +} diff --git a/vendor/monolog/monolog/tests/Monolog/TestCase.php b/vendor/monolog/monolog/tests/Monolog/TestCase.php new file mode 100644 index 0000000..4eb7b4c --- /dev/null +++ b/vendor/monolog/monolog/tests/Monolog/TestCase.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 Monolog; + +class TestCase extends \PHPUnit_Framework_TestCase +{ + /** + * @return array Record + */ + protected function getRecord($level = Logger::WARNING, $message = 'test', $context = array()) + { + return array( + 'message' => $message, + 'context' => $context, + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true))), + 'extra' => array(), + ); + } + + /** + * @return array + */ + protected function getMultipleRecords() + { + return array( + $this->getRecord(Logger::DEBUG, 'debug message 1'), + $this->getRecord(Logger::DEBUG, 'debug message 2'), + $this->getRecord(Logger::INFO, 'information'), + $this->getRecord(Logger::WARNING, 'warning'), + $this->getRecord(Logger::ERROR, 'error'), + ); + } + + /** + * @return Monolog\Formatter\FormatterInterface + */ + protected function getIdentityFormatter() + { + $formatter = $this->getMock('Monolog\\Formatter\\FormatterInterface'); + $formatter->expects($this->any()) + ->method('format') + ->will($this->returnCallback(function ($record) { return $record['message']; })); + + return $formatter; + } +} diff --git a/vendor/pagerfanta/pagerfanta/CHANGELOG.md b/vendor/pagerfanta/pagerfanta/CHANGELOG.md new file mode 100644 index 0000000..ffe89e2 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/CHANGELOG.md @@ -0,0 +1,17 @@ +### 1.0.2 (-) + + * Added `DoctrineODMPhpcrAdapter` + * Added endpoint to `SolariumAdapter` + * Added $useOutputWalkers mode into the DoctrineORM adapter + +### 1.0.1 (2013-09-23) + + * Added `TwitterBootstrap3View` + * Made `getResultSet` public in the `SolariumAdapter` + * Fixed the `last` method in the `DefaultView` to call the last method of the template + * Added `currentPageOffsetStart` and `currentPageOffsetEnd` to `Pagerfanta` + * Fixed the minimum number of pages to 1 + +### 1.0.0 (2013-04-24) + + * Initial release diff --git a/vendor/pagerfanta/pagerfanta/LICENSE b/vendor/pagerfanta/pagerfanta/LICENSE new file mode 100644 index 0000000..72eeba3 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2011 White October Ltd +http://www.whiteoctober.co.uk/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/pagerfanta/pagerfanta/README.md b/vendor/pagerfanta/pagerfanta/README.md new file mode 100644 index 0000000..4a513a1 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/README.md @@ -0,0 +1,610 @@ +# Pagerfanta + +[![Build Status](https://travis-ci.org/whiteoctober/Pagerfanta.png?branch=master)](https://travis-ci.org/whiteoctober/Pagerfanta) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/whiteoctober/Pagerfanta/badges/quality-score.png?s=1ee480491644c07812b5206cf07d33a5035d0118)](https://scrutinizer-ci.com/g/whiteoctober/Pagerfanta/) [![Code Coverage](https://scrutinizer-ci.com/g/whiteoctober/Pagerfanta/badges/coverage.png?s=284be0616a9ba0439ee1123bcaf5fb3f6bfb0e50)](https://scrutinizer-ci.com/g/whiteoctober/Pagerfanta/) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/9e710230-b088-4904-baef-5f5e2d62e681/mini.png)](https://insight.sensiolabs.com/projects/9e710230-b088-4904-baef-5f5e2d62e681) [![Latest Stable Version](https://poser.pugx.org/pagerfanta/pagerfanta/v/stable.png)](https://packagist.org/packages/pagerfanta/pagerfanta) [![Total Downloads](https://poser.pugx.org/pagerfanta/pagerfanta/downloads.png)](https://packagist.org/packages/pagerfanta/pagerfanta) + +This project is for PHP >= 7.0. +If you need support for PHP < 7, use [Release v1.1.0](https://github.com/whiteoctober/Pagerfanta/releases/tag/v1.1.0). + +## Usage + +```php +setMaxPerPage($maxPerPage); // 10 by default +$maxPerPage = $pagerfanta->getMaxPerPage(); + +$pagerfanta->setCurrentPage($currentPage); // 1 by default +$currentPage = $pagerfanta->getCurrentPage(); + +$nbResults = $pagerfanta->getNbResults(); +$currentPageResults = $pagerfanta->getCurrentPageResults(); + +$pagerfanta->getNbPages(); + +$pagerfanta->haveToPaginate(); // whether the number of results is higher than the max per page + +$pagerfanta->hasPreviousPage(); +$pagerfanta->getPreviousPage(); +$pagerfanta->hasNextPage(); +$pagerfanta->getNextPage(); +$pagerfanta->getCurrentPageOffsetStart(); +$pagerfanta->getCurrentPageOffsetEnd(); +``` + +The `->setMaxPerPage()` and `->setCurrentPage()` methods implement +a fluent interface: + +```php +setMaxPerPage($maxPerPage) + ->setCurrentPage($currentPage); +``` + +The `->setMaxPerPage()` method throws an exception if the max per page +is not valid: + + * `Pagerfanta\Exception\NotIntegerMaxPerPageException` + * `Pagerfanta\Exception\LessThan1MaxPerPageException` + +Both extend from `Pagerfanta\Exception\NotValidMaxPerPageException`. + +The `->setCurrentPage()` method throws an exception if the page is not valid: + + * `Pagerfanta\Exception\NotIntegerCurrentPageException` + * `Pagerfanta\Exception\LessThan1CurrentPageException` + * `Pagerfanta\Exception\OutOfRangeCurrentPageException` + +All of them extend from `Pagerfanta\Exception\NotValidCurrentPageException`. + +`->setCurrentPage()` throws an out ot range exception depending on the +max per page, so if you are going to modify the max per page, you should do it +before setting the current page. + +(If you want to use Pagerfanta in a Symfony project, see +[https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle](https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle).) + +## Adapters + +The adapter's concept is very simple. An adapter just returns the number +of results and an slice for a offset and length. This way you can adapt +a pagerfanta to paginate any kind results simply by creating an adapter. + +An adapter must implement the `Pagerfanta\Adapter\AdapterInterface` +interface, which has these two methods: + +```php +find(); +$adapter = new MongoAdapter($cursor); +``` + +### MandangoAdapter + +To paginate [Mandango](http://mandango.org) Queries. + +```php +getRepository('Model\Article')->createQuery(); +$adapter = new MandangoAdapter($query); +``` + +### DoctrineDbalAdapter + +To paginate [DoctrineDbal](http://www.doctrine-project.org/projects/dbal.html) +query builders. + +```php +select('p.*')->from('posts', 'p'); + +$countQueryBuilderModifier = function ($queryBuilder) { + $queryBuilder->select('COUNT(DISTINCT p.id) AS total_results') + ->setMaxResults(1); +}; + +$adapter = new DoctrineDbalAdapter($queryBuilder, $countQueryBuilderModifier); +``` + +### DoctrineDbalSingleTableAdapter + +To simplify the pagination of single table +[DoctrineDbal](http://www.doctrine-project.org/projects/dbal.html) +query builders. + +This adapter only paginates single table query builders, without joins. + +```php +select('p.*')->from('posts', 'p'); + +$countField = 'p.id'; + +$adapter = new DoctrineDbalSingleTableAdapter($queryBuilder, $countField); +``` + +### DoctrineORMAdapter + +To paginate [DoctrineORM](http://www.doctrine-project.org/projects/orm) query objects. + +```php +createQueryBuilder() + ->select('u') + ->from('Model\Article', 'u'); +$adapter = new DoctrineORMAdapter($queryBuilder); +``` + +### DoctrineODMMongoDBAdapter + +To paginate [DoctrineODMMongoDB](http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/) query builders. + +```php +createQueryBuilder('Model\Article'); +$adapter = new DoctrineODMMongoDBAdapter($queryBuilder); +``` + +### DoctrineODMPhpcrAdapter + +To paginate [Doctrine PHPCR-ODM](http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/) query builders. + +```php +createQueryBuilder(); +$queryBuilder->from('Model\Article'); +$adapter = new DoctrineODMPhpcrAdapter($queryBuilder); +``` + +### DoctrineCollectionAdapter + +To paginate a `Doctrine\Common\Collection\Collections` interface +you can use the `DoctrineCollectionAdapter`. It proxies to the +count() and slice() methods on the Collections interface for +pagination. This makes sense if you are using Doctrine ORMs Extra +Lazy association features: + +```php +find("Pagerfanta\Tests\Adapter\DoctrineORM\User", 1); + +$adapter = new DoctrineCollectionAdapter($user->getGroups()); +``` + +### DoctrineSelectableAdapter + +To paginate a `Doctrine\Common\Collection\Selectable` interface +you can use the `DoctrineSelectableAdapter`. It uses the matching() +method on the Selectable interface for pagination. This is +especially usefull when using the Doctrine Criteria object to +filter a PersistentCollection: + +```php +find("Pagerfanta\Tests\Adapter\DoctrineORM\User", 1); +$comments = $user->getComments(); +$criteria = Criteria::create()->andWhere(Criteria::expr()->in('id', array(1,2,3)); + +$adapter = new DoctrineSelectableAdapter($comments, $criteria); +``` + +Note that you should never use this adapter with a +PersistentCollection which is not set to use the EXTRA_LAZY fetch mode. + +*Be careful when using the `count()` method, currently Doctrine2 +needs to fetch all the records to count the number of elements.* + +### ElasticaAdapter + +To paginate an Elastica Query query: + +```php + 'Fred' +)); + +$adapter = new ElasticaAdapter($searchable, $query); +``` + +*Be careful when paginating a huge set of documents. By default, offset + limit +can't exceed 10000. You can mitigate this by setting the `$maxResults` +parameter when constructing the `ElasticaAdapter`. For more information, see: +[#213](https://github.com/whiteoctober/Pagerfanta/pull/213#issue-87631892).* + +### PropelAdapter + +To paginate a propel 1 query: + +```php +createSelect(); +$query->setQuery('search term'); + +$adapter = new SolariumAdapter($solarium, $query); +``` + +### FixedAdapter + +Best used when you need to do a custom paging solution and +don't want to implement a full adapter for a one-off use case. + +It returns always the same data no matter what page you query: + +```php + 3); +$html = $view->render($pagerfanta, $routeGenerator, $options); +``` + +Options (default): + + * proximity (3) + * prev_message (Previous) + * next_message (Next) + * css_disabled_class (disabled) + * css_dots_class (dots) + * css_current_class (current) + * dots_text (...) + * container_template () + * page_template (%text%) + * span_template (%text%) + +CSS: + +```css +.pagerfanta { +} + +.pagerfanta a, +.pagerfanta span { + display: inline-block; + border: 1px solid blue; + color: blue; + margin-right: .2em; + padding: .25em .35em; +} + +.pagerfanta a { + text-decoration: none; +} + +.pagerfanta a:hover { + background: #ccf; +} + +.pagerfanta .dots { + border-width: 0; +} + +.pagerfanta .current { + background: #ccf; + font-weight: bold; +} + +.pagerfanta .disabled { + border-color: #ccf; + color: #ccf; +} + +COLORS: + +.pagerfanta a, +.pagerfanta span { + border-color: blue; + color: blue; +} + +.pagerfanta a:hover { + background: #ccf; +} + +.pagerfanta .current { + background: #ccf; +} + +.pagerfanta .disabled { + border-color: #ccf; + color: #cf; +} +``` + +### TwitterBootstrapView, TwitterBootstrap3View and TwitterBootstrap4View + +These views generate paginators designed for use with +[Twitter Bootstrap](https://github.com/twitter/bootstrap). + +`TwitterBootstrapView` is for Bootstrap 2; `TwitterBootstrap3View` is for Bootstrap 3; `TwitterBootstrap4View` is for Bootstrap 4 (alpha). + +```php + 3); +$html = $view->render($pagerfanta, $routeGenerator, $options); +``` + +Options (default): + + * proximity (3) + * prev_message (← Previous) + * prev_disabled_href () + * next_message (Next →) + * next_disabled_href () + * dots_message (…) + * dots_href () + * css_container_class (pagination) + * css_prev_class (prev) + * css_next_class (next) + * css_disabled_class (disabled) + * css_dots_class (disabled) + * css_active_class (active) + +### SemanticUiView + +This view generates a pagination for +[Semantic UI](https://github.com/Semantic-Org/Semantic-UI). + +```php + 3); +$html = $view->render($pagerfanta, $routeGenerator, $options); +``` + +Options (default): + + * proximity (3) + * prev_message (← Previous) + * prev_disabled_href () + * next_message (Next →) + * next_disabled_href () + * dots_message (…) + * dots_href () + * css_container_class (pagination) + * css_item_class (item) + * css_prev_class (prev) + * css_next_class (next) + * css_disabled_class (disabled) + * css_dots_class (disabled) + * css_active_class (active) + +### OptionableView + +This view is to reuse options in different views. + +```php + 3)); + +$myView2 = new OptionableView($defaultView, array('prev_message' => 'Anterior', 'next_message' => 'Siguiente')); + +// using in a normal way +$pagerfantaHtml = $myView2->render($pagerfanta, $routeGenerator); + +// overwriting default options +$pagerfantaHtml = $myView2->render($pagerfanta, $routeGenerator, array('next_message' => 'Siguiente!!')); +``` + +## Contributing + +We welcome contributions to this project, including pull requests and issues (and discussions on existing issues). + +If you'd like to contribute code but aren't sure what, the [issues list](https://github.com/whiteoctober/pagerfanta/issues) is a good place to start. +If you're a first-time code contributor, you may find Github's guide to [forking projects](https://guides.github.com/activities/forking/) helpful. + +All contributors (whether contributing code, involved in issue discussions, or involved in any other way) must abide by our [code of conduct](code_of_conduct.md). + +## Acknowledgements + +Pagerfanta is inspired by [Zend Paginator](https://github.com/zendframework/zf2). + +Thanks also to Pablo Díez (pablodip@gmail.com) for most of the work on the first versions of Pagerfanta. + +## Licence + +Pagerfanta is licensed under the [MIT License](LICENSE). diff --git a/vendor/pagerfanta/pagerfanta/code_of_conduct.md b/vendor/pagerfanta/pagerfanta/code_of_conduct.md new file mode 100644 index 0000000..59c7d9f --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/code_of_conduct.md @@ -0,0 +1 @@ +This project's code-of-conduct can be found at [https://github.com/whiteoctober/open-source-code-of-conduct/blob/master/code_of_conduct.md](https://github.com/whiteoctober/open-source-code-of-conduct/blob/master/code_of_conduct.md). diff --git a/vendor/pagerfanta/pagerfanta/composer.json b/vendor/pagerfanta/pagerfanta/composer.json new file mode 100644 index 0000000..9d66724 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/composer.json @@ -0,0 +1,46 @@ +{ + "name": "pagerfanta/pagerfanta", + "description": "Pagination for PHP 5.3", + "keywords": ["page","paging", "paginator", "pagination"], + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Pablo Díez", + "email": "pablodip@gmail.com" + } + ], + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7|^6", + "doctrine/orm": "~2.3", + "mandango/mandango": "~1.0@dev", + "mandango/mondator": "~1.0@dev", + "jmikola/geojson": "~1.0", + "doctrine/phpcr-odm": "1.*", + "propel/propel": "~2.0@dev", + "propel/propel1": "~1.6", + "ruflin/Elastica": "~1.3", + "solarium/solarium": "~3.1", + "jackalope/jackalope-doctrine-dbal": "1.*" + }, + "suggest": { + "doctrine/orm": "To use the DoctrineORMAdapter.", + "mandango/mandango": "To use the MandangoAdapter.", + "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.", + "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0", + "propel/propel1": "To use the PropelAdapter", + "propel/propel": "To use the Propel2Adapter", + "solarium/solarium": "To use the SolariumAdapter." + }, + "autoload": { + "psr-0": { "Pagerfanta\\": "src/" } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/AdapterInterface.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/AdapterInterface.php new file mode 100644 index 0000000..f7cbdb8 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/AdapterInterface.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 Pagerfanta\Adapter; + +/** + * AdapterInterface. + * + * @author Pablo Díez + */ +interface AdapterInterface +{ + /** + * Returns the number of results. + * + * @return integer The number of results. + */ + public function getNbResults(); + + /** + * Returns an slice of the results. + * + * @param integer $offset The offset. + * @param integer $length The length. + * + * @return array|\Traversable The slice. + */ + public function getSlice($offset, $length); +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ArrayAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ArrayAdapter.php new file mode 100644 index 0000000..cffaba3 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ArrayAdapter.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 Pagerfanta\Adapter; + +/** + * ArrayAdapter. + * + * @author Pablo Díez + */ +class ArrayAdapter implements AdapterInterface +{ + private $array; + + /** + * Constructor. + * + * @param array $array The array. + */ + public function __construct(array $array) + { + $this->array = $array; + } + + /** + * Returns the array. + * + * @return array The array. + */ + public function getArray() + { + return $this->array; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return count($this->array); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return array_slice($this->array, $offset, $length); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/CallbackAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/CallbackAdapter.php new file mode 100644 index 0000000..637e40d --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/CallbackAdapter.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 Pagerfanta\Adapter; + +use Pagerfanta\Exception\InvalidArgumentException; + +/** + * @author Adrien Brault + */ +class CallbackAdapter implements AdapterInterface +{ + private $getNbResultsCallback; + private $getSliceCallback; + + /** + * @param callable $getNbResultsCallback + * @param callable $getSliceCallback + */ + public function __construct($getNbResultsCallback, $getSliceCallback) + { + if (!is_callable($getNbResultsCallback)) { + throw new InvalidArgumentException('$getNbResultsCallback should be a callable'); + } + if (!is_callable($getSliceCallback)) { + throw new InvalidArgumentException('$getSliceCallback should be a callable'); + } + + $this->getNbResultsCallback = $getNbResultsCallback; + $this->getSliceCallback = $getSliceCallback; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return call_user_func($this->getNbResultsCallback); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return call_user_func($this->getSliceCallback, $offset, $length); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ConcatenationAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ConcatenationAdapter.php new file mode 100644 index 0000000..1de27ef --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ConcatenationAdapter.php @@ -0,0 +1,126 @@ + + */ +class ConcatenationAdapter implements AdapterInterface +{ + /** + * @var AdapterInterface[] List of adapters + */ + protected $adapters; + + /** + * @var int[]|null Cache of the numbers of results of the adapters. The indexes correspond the indexes of the + * `adapters` property. + */ + protected $adaptersNbResultsCache; + + /** + * @param AdapterInterface[] $adapters + * @throws InvalidArgumentException + */ + public function __construct(array $adapters) + { + foreach ($adapters as $index => $adapter) { + if (!($adapter instanceof AdapterInterface)) { + throw new InvalidArgumentException(sprintf( + 'Argument $adapters[%s] expected to be a \Pagerfanta\Adapter\AdapterInterface instance, a %s given', + $index, + is_object($adapter) ? sprintf('%s instance', get_class($adapter)) : gettype($adapter) + )); + } + } + + $this->adapters = $adapters; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + if (!isset($this->adaptersNbResultsCache)) { + $this->refreshAdaptersNbResults(); + } + + return array_sum($this->adaptersNbResultsCache); + } + + /** + * {@inheritdoc} + * @return array + */ + public function getSlice($offset, $length) + { + if (!isset($this->adaptersNbResultsCache)) { + $this->refreshAdaptersNbResults(); + } + + $slice = array(); + $previousAdaptersNbResultsSum = 0; + $requestFirstIndex = $offset; + $requestLastIndex = $offset + $length - 1; + + foreach ($this->adapters as $index => $adapter) { + $adapterNbResults = $this->adaptersNbResultsCache[$index]; + $adapterFirstIndex = $previousAdaptersNbResultsSum; + $adapterLastIndex = $adapterFirstIndex + $adapterNbResults - 1; + + $previousAdaptersNbResultsSum += $adapterNbResults; + + // The adapter is fully below the requested slice range — skip it + if ($adapterLastIndex < $requestFirstIndex) { + continue; + } + + // The adapter is fully above the requested slice range — finish the gathering + if ($adapterFirstIndex > $requestLastIndex) { + break; + } + + // Else the adapter range definitely intersects with the requested range + $fetchOffset = $requestFirstIndex - $adapterFirstIndex; + $fetchLength = $length; + + // The requested range start is below the adapter range start + if ($fetchOffset < 0) { + $fetchLength += $fetchOffset; + $fetchOffset = 0; + } + + // The requested range end is above the adapter range end + if ($fetchOffset + $fetchLength > $adapterNbResults) { + $fetchLength = $adapterNbResults - $fetchOffset; + } + + // Getting the subslice from the adapter and adding it to the result slice + $fetchSlice = $adapter->getSlice($fetchOffset, $fetchLength); + foreach ($fetchSlice as $item) { + $slice[] = $item; + } + } + + return $slice; + } + + /** + * Refreshes the cache of the numbers of results of the adapters. + */ + protected function refreshAdaptersNbResults() + { + if (!isset($this->adaptersNbResultsCache)) { + $this->adaptersNbResultsCache = array(); + } + + foreach ($this->adapters as $index => $adapter) { + $this->adaptersNbResultsCache[$index] = $adapter->getNbResults(); + } + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineCollectionAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineCollectionAdapter.php new file mode 100644 index 0000000..86a0a40 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineCollectionAdapter.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta\Adapter; + +use Doctrine\Common\Collections\Collection; + +/** + * DoctrineCollectionAdapter. + * + * @author Pablo Díez + */ +class DoctrineCollectionAdapter implements AdapterInterface +{ + private $collection; + + /** + * Constructor. + * + * @param Collection $collection A Doctrine collection. + */ + public function __construct(Collection $collection) + { + $this->collection = $collection; + } + + /** + * Returns the collection. + * + * @return Collection The collection. + */ + public function getCollection() + { + return $this->collection; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->collection->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return $this->collection->slice($offset, $length); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalAdapter.php new file mode 100644 index 0000000..7fad396 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalAdapter.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 Pagerfanta\Adapter; + +use Doctrine\DBAL\Query\QueryBuilder; +use Pagerfanta\Exception\InvalidArgumentException; + +/** + * @author Michael Williams + * @author Pablo Díez + */ +class DoctrineDbalAdapter implements AdapterInterface +{ + private $queryBuilder; + private $countQueryBuilderModifier; + + /** + * Constructor. + * + * @param QueryBuilder $queryBuilder A DBAL query builder. + * @param callable $countQueryBuilderModifier A callable to modifier the query builder to count. + */ + public function __construct(QueryBuilder $queryBuilder, $countQueryBuilderModifier) + { + if ($queryBuilder->getType() !== QueryBuilder::SELECT) { + throw new InvalidArgumentException('Only SELECT queries can be paginated.'); + } + + if (!is_callable($countQueryBuilderModifier)) { + throw new InvalidArgumentException('The count query builder modifier must be a callable.'); + } + + $this->queryBuilder = clone $queryBuilder; + $this->countQueryBuilderModifier = $countQueryBuilderModifier; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + $qb = $this->prepareCountQueryBuilder(); + $result = $qb->execute()->fetchColumn(); + + return (int) $result; + } + + private function prepareCountQueryBuilder() + { + $qb = clone $this->queryBuilder; + call_user_func($this->countQueryBuilderModifier, $qb); + + return $qb; + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + $qb = clone $this->queryBuilder; + $result = $qb->setMaxResults($length) + ->setFirstResult($offset) + ->execute(); + + return $result->fetchAll(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalSingleTableAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalSingleTableAdapter.php new file mode 100644 index 0000000..86b440e --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineDbalSingleTableAdapter.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 Pagerfanta\Adapter; + +use Doctrine\DBAL\Query\QueryBuilder; +use Pagerfanta\Exception\InvalidArgumentException; + +/** + * @author Michael Williams + * @author Pablo Díez + */ +class DoctrineDbalSingleTableAdapter extends DoctrineDbalAdapter +{ + /** + * Constructor. + * + * @param QueryBuilder $queryBuilder A DBAL query builder. + * @param string $countField Primary key for the table in query. Used in count expression. Must include table alias + */ + public function __construct(QueryBuilder $queryBuilder, $countField) + { + if ($this->hasQueryBuilderJoins($queryBuilder)) { + throw new InvalidArgumentException('The query builder cannot have joins.'); + } + + $countQueryBuilderModifier = $this->createCountQueryModifier($countField); + + parent::__construct($queryBuilder, $countQueryBuilderModifier); + } + + private function hasQueryBuilderJoins(QueryBuilder $queryBuilder) + { + $joins = $queryBuilder->getQueryPart('join'); + + return !empty($joins); + } + + private function createCountQueryModifier($countField) + { + $select = $this->createSelectForCountField($countField); + + return function (QueryBuilder $queryBuilder) use ($select) { + $queryBuilder->select($select) + ->setMaxResults(1); + }; + } + + private function createSelectForCountField($countField) + { + if ($this->countFieldHasNoAlias($countField)) { + throw new InvalidArgumentException('The $countField must contain a table alias in the string.'); + } + + return sprintf('COUNT(DISTINCT %s) AS total_results', $countField); + } + + private function countFieldHasNoAlias($countField) + { + return strpos($countField, '.') === false; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.php new file mode 100644 index 0000000..2f144f2 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.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 Pagerfanta\Adapter; + +use Doctrine\ODM\MongoDB\Query\Builder; + +/** + * DoctrineODMMongoDBAdapter. + * + * @author Pablo Díez + */ +class DoctrineODMMongoDBAdapter implements AdapterInterface +{ + private $queryBuilder; + + /** + * Constructor. + * + * @param Builder $queryBuilder A DoctrineMongo query builder. + */ + public function __construct(Builder $queryBuilder) + { + $this->queryBuilder = $queryBuilder; + } + + /** + * Returns the query builder. + * + * @return Builder The query builder. + */ + public function getQueryBuilder() + { + return $this->queryBuilder; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->queryBuilder->getQuery()->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return $this->queryBuilder + ->limit($length) + ->skip($offset) + ->getQuery() + ->execute(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMPhpcrAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMPhpcrAdapter.php new file mode 100644 index 0000000..cc509b8 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineODMPhpcrAdapter.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 Pagerfanta\Adapter; + +use Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder; +use Doctrine\ODM\PHPCR\Query\Query; + +/** + * Pagerfanta adapter for Doctrine PHPCR-ODM. + * + * @author David Buchmann + */ +class DoctrineODMPhpcrAdapter implements AdapterInterface +{ + private $queryBuilder; + + /** + * Constructor. + * + * @param QueryBuilder $queryBuilder A Doctrine PHPCR-ODM query builder. + */ + public function __construct(QueryBuilder $queryBuilder) + { + $this->queryBuilder = $queryBuilder; + } + + /** + * Returns the query builder. + * + * @return QueryBuilder The query builder. + */ + public function getQueryBuilder() + { + return $this->queryBuilder; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->queryBuilder->getQuery()->execute(null, Query::HYDRATE_PHPCR)->getRows()->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return $this->queryBuilder + ->getQuery() + ->setMaxResults($length) + ->setFirstResult($offset) + ->execute(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineORMAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineORMAdapter.php new file mode 100644 index 0000000..f3c584e --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineORMAdapter.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 Pagerfanta\Adapter; + +use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator; + +/** + * DoctrineORMAdapter. + * + * @author Christophe Coevoet + */ +class DoctrineORMAdapter implements AdapterInterface +{ + /** + * @var \Doctrine\ORM\Tools\Pagination\Paginator + */ + private $paginator; + + /** + * Constructor. + * + * @param \Doctrine\ORM\Query|\Doctrine\ORM\QueryBuilder $query A Doctrine ORM query or query builder. + * @param Boolean $fetchJoinCollection Whether the query joins a collection (true by default). + * @param Boolean|null $useOutputWalkers Whether to use output walkers pagination mode + */ + public function __construct($query, $fetchJoinCollection = true, $useOutputWalkers = null) + { + $this->paginator = new DoctrinePaginator($query, $fetchJoinCollection); + $this->paginator->setUseOutputWalkers($useOutputWalkers); + } + + /** + * Returns the query + * + * @return \Doctrine\ORM\Query + */ + public function getQuery() + { + return $this->paginator->getQuery(); + } + + /** + * Returns whether the query joins a collection. + * + * @return Boolean Whether the query joins a collection. + */ + public function getFetchJoinCollection() + { + return $this->paginator->getFetchJoinCollection(); + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return count($this->paginator); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + $this->paginator + ->getQuery() + ->setFirstResult($offset) + ->setMaxResults($length); + + return $this->paginator->getIterator(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineSelectableAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineSelectableAdapter.php new file mode 100644 index 0000000..3d447bc --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/DoctrineSelectableAdapter.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta\Adapter; + +use Doctrine\Common\Collections\Criteria; +use Doctrine\Common\Collections\Selectable; + +/** + * DoctrineSelectableAdapter. + * + * @author Boris Guéry + */ +class DoctrineSelectableAdapter implements AdapterInterface +{ + /** + * @var Selectable + */ + private $selectable; + + /** + * @var Criteria + */ + private $criteria; + + /** + * Constructor. + * + * @param Selectable $selectable An implementation of the Selectable interface. + * @param Criteria $criteria A Doctrine criteria. + */ + public function __construct(Selectable $selectable, Criteria $criteria) + { + $this->selectable = $selectable; + $this->criteria = $criteria; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + $firstResult = null; + $maxResults = null; + + $criteria = $this->createCriteria($firstResult, $maxResults); + + return $this->selectable->matching($criteria)->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + $firstResult = $offset; + $maxResults = $length; + + $criteria = $this->createCriteria($firstResult, $maxResults); + + return $this->selectable->matching($criteria); + } + + private function createCriteria($firstResult, $maxResult) + { + $criteria = clone $this->criteria; + $criteria->setFirstResult($firstResult); + $criteria->setMaxResults($maxResult); + + return $criteria; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ElasticaAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ElasticaAdapter.php new file mode 100644 index 0000000..d974b7c --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/ElasticaAdapter.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta\Adapter; + +use Elastica\Query; +use Elastica\SearchableInterface; + +class ElasticaAdapter implements AdapterInterface +{ + /** + * @var Query + */ + private $query; + + /** + * @var \Elastica\ResultSet + */ + private $resultSet; + + /** + * @var SearchableInterface + */ + private $searchable; + + /** + * @var array + */ + private $options; + + /** + * @var int|null + * + * Used to limit the number of totalHits returned by ES. + * For more information, see: https://github.com/whiteoctober/Pagerfanta/pull/213#issue-87631892 + */ + private $maxResults; + + public function __construct(SearchableInterface $searchable, Query $query, array $options = array(), $maxResults = null) + { + $this->searchable = $searchable; + $this->query = $query; + $this->options = $options; + $this->maxResults = $maxResults; + } + + /** + * Returns the number of results. + * + * @return integer The number of results. + */ + public function getNbResults() + { + if (!$this->resultSet) { + $totalHits = $this->searchable->search($this->query, $this->options)->getTotalHits(); + } else { + $totalHits = $this->resultSet->getTotalHits(); + } + + if (null === $this->maxResults) { + return $totalHits; + } + + return min($totalHits, $this->maxResults); + } + + /** + * Returns the Elastica ResultSet. Will return null if getSlice has not yet been + * called. + * + * @return \Elastica\ResultSet|null + */ + public function getResultSet() + { + return $this->resultSet; + } + + /** + * Returns an slice of the results. + * + * @param integer $offset The offset. + * @param integer $length The length. + * + * @return array|\Traversable The slice. + */ + public function getSlice($offset, $length) + { + return $this->resultSet = $this->searchable->search($this->query, array_merge($this->options, array( + 'from' => $offset, + 'size' => $length + ))); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/FixedAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/FixedAdapter.php new file mode 100644 index 0000000..3c22076 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/FixedAdapter.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 Pagerfanta\Adapter; + +/** + * Provides you with an adapter that returns always the same data. + * + * Best used when you need to do a custom paging solution and don't + * want to implement a full adapter for a one-off use case. + * + * @author Jordi Boggiano + */ +class FixedAdapter implements AdapterInterface +{ + private $nbResults; + private $results; + + /** + * @param int $nbResults + * @param array|\Traversable $results + */ + public function __construct($nbResults, $results) + { + $this->nbResults = $nbResults; + $this->results = $results; + } + + /** + * {@inheritDoc} + */ + public function getNbResults() + { + return $this->nbResults; + } + + /** + * {@inheritDoc} + */ + public function getSlice($offset, $length) + { + return $this->results; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MandangoAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MandangoAdapter.php new file mode 100644 index 0000000..a9b76ac --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MandangoAdapter.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta\Adapter; + +use Mandango\Query; + +/** + * MandangoAdapter. + * + * @author Pablo Díez + */ +class MandangoAdapter implements AdapterInterface +{ + private $query; + + /** + * Constructor. + * + * @param Query $query The query. + */ + public function __construct(Query $query) + { + $this->query = $query; + } + + /** + * Returns the query. + * + * @return Query The query. + */ + public function getQuery() + { + return $this->query; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->query->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return $this->query->limit($length)->skip($offset)->all(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MongoAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MongoAdapter.php new file mode 100644 index 0000000..eb134f9 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/MongoAdapter.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 Pagerfanta\Adapter; + +/** + * MongoAdapter. + * + * @author Sergey Ponomaryov + */ +class MongoAdapter implements AdapterInterface +{ + private $cursor; + + /** + * Constructor. + * + * @param \MongoCursor $cursor The cursor. + */ + public function __construct(\MongoCursor $cursor) + { + $this->cursor = $cursor; + } + + /** + * Returns the cursor. + * + * @return \MongoCursor The cursor. + */ + public function getCursor() + { + return $this->cursor; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->cursor->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + $this->cursor->limit($length); + $this->cursor->skip($offset); + + return $this->cursor; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/NullAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/NullAdapter.php new file mode 100644 index 0000000..452e354 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/NullAdapter.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 Pagerfanta\Adapter; + +/** + * NullAdapter. + * + * @author Benjamin Dulau + */ +class NullAdapter implements AdapterInterface +{ + private $nbResults; + + /** + * Constructor. + * + * @param integer $nbResults Total item count. + */ + public function __construct($nbResults = 0) + { + $this->nbResults = (int) $nbResults; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->nbResults; + } + + /** + * The following methods are derived from code of the Zend Framework + * 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) + * + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + if ($offset >= $this->nbResults) { + return array(); + } + + $nullArrayLength = $this->calculateNullArrayLength($offset, $length); + + return $this->createNullArray($nullArrayLength); + } + + private function calculateNullArrayLength($offset, $length) + { + $remainCount = $this->remainCount($offset); + if ($length > $remainCount) { + return $remainCount; + } + + return $length; + } + + private function remainCount($offset) + { + return $this->nbResults - $offset; + } + + private function createNullArray($length) + { + return array_fill(0, $length, null); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/Propel2Adapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/Propel2Adapter.php new file mode 100644 index 0000000..0a716b6 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/Propel2Adapter.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 Pagerfanta\Adapter; + +use Propel\Runtime\ActiveQuery\ModelCriteria; + +/** + * Propel2Adapter. + * + * @author Raphael YAN + */ +class Propel2Adapter implements AdapterInterface +{ + /** + * @var ModelCriteria $query + */ + private $query; + + /** + * Constructor. + * + * @param ModelCriteria $query + */ + public function __construct(ModelCriteria $query) + { + $this->query = $query; + } + + /** + * Returns the query. + * + * @return ModelCriteria + */ + public function getQuery() + { + return $this->query; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + $q = clone $this->getQuery(); + + $q->offset(0); + + return $q->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + $q = clone $this->getQuery(); + + $q->limit($length); + $q->offset($offset); + + return $q->find(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/PropelAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/PropelAdapter.php new file mode 100644 index 0000000..979be88 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/PropelAdapter.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 Pagerfanta\Adapter; + +/** + * PropelAdapter. + * + * @author William DURAND + */ +class PropelAdapter implements AdapterInterface +{ + private $query; + + /** + * Constructor. + * + * @param \ModelCriteria $query + */ + public function __construct($query) + { + $this->query = $query; + } + + /** + * Returns the query. + * + * @return \ModelCriteria + */ + public function getQuery() + { + return $this->query; + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + $q = clone $this->getQuery(); + + $q->limit(0); + $q->offset(0); + + return $q->count(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + $q = clone $this->getQuery(); + + $q->limit($length); + $q->offset($offset); + + return $q->find(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/SolariumAdapter.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/SolariumAdapter.php new file mode 100644 index 0000000..7a59f41 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Adapter/SolariumAdapter.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta\Adapter; + +use Pagerfanta\Exception\InvalidArgumentException; +use Solarium\QueryType\Select\Query\Query; +use Solarium\Core\Client\Client; +use Solarium_Query_Select; +use Solarium_Client; + +/** + * SolariumAdapter. + * + * @author Igor Wiedler + */ +class SolariumAdapter implements AdapterInterface +{ + private $client; + private $query; + private $resultSet; + private $endPoint; + private $resultSetStart; + private $resultSetRows; + + /** + * Constructor. + * + * @param Solarium_Client|Client $client A Solarium client. + * @param Solarium_Query_Select|Query $query A Solarium select query. + */ + public function __construct($client, $query) + { + $this->checkClient($client); + $this->checkQuery($query); + + $this->client = $client; + $this->query = $query; + } + + private function checkClient($client) + { + if ($this->isClientInvalid($client)) { + throw new InvalidArgumentException($this->getClientInvalidMessage($client)); + } + } + + private function isClientInvalid($client) + { + return !($client instanceof Client) && + !($client instanceof Solarium_Client); + } + + private function getClientInvalidMessage($client) + { + return sprintf('The client object should be a Solarium_Client or Solarium\Core\Client\Client instance, %s given', + get_class($client) + ); + } + + private function checkQuery($query) + { + if ($this->isQueryInvalid($query)) { + throw new InvalidArgumentException($this->getQueryInvalidMessage($query)); + } + } + + private function isQueryInvalid($query) + { + return !($query instanceof Query) && + !($query instanceof Solarium_Query_Select); + } + + private function getQueryInvalidMessage($query) + { + return sprintf('The query object should be a Solarium_Query_Select or Solarium\QueryType\Select\Query\Query instance, %s given', + get_class($query) + ); + } + + /** + * {@inheritdoc} + */ + public function getNbResults() + { + return $this->getResultSet()->getNumFound(); + } + + /** + * {@inheritdoc} + */ + public function getSlice($offset, $length) + { + return $this->getResultSet($offset, $length); + } + + /** + * @param int $start + * @param int $rows + * + * @return \Solarium_Result_Select|\Solarium\QueryType\Select\Result\Result + **/ + public function getResultSet($start = null, $rows = null) + { + if ($this->resultSetStartAndRowsAreNotNullAndChange($start, $rows)) { + $this->resultSetStart = $start; + $this->resultSetRows = $rows; + + $this->modifyQuery(); + $this->clearResultSet(); + } + + if ($this->resultSetEmpty()) { + $this->resultSet = $this->createResultSet(); + } + + return $this->resultSet; + } + + private function resultSetStartAndRowsAreNotNullAndChange($start, $rows) + { + return $this->resultSetStartAndRowsAreNotNull($start, $rows) && + $this->resultSetStartAndRowsChange($start, $rows); + } + + private function resultSetStartAndRowsAreNotNull($start, $rows) + { + return $start !== null && $rows !== null; + } + + private function resultSetStartAndRowsChange($start, $rows) + { + return $start !== $this->resultSetStart || $rows !== $this->resultSetRows; + } + + private function modifyQuery() + { + $this->query + ->setStart($this->resultSetStart) + ->setRows($this->resultSetRows); + } + + private function createResultSet() + { + return $this->client->select($this->query, $this->endPoint); + } + + private function clearResultSet() + { + $this->resultSet = null; + } + + private function resultSetEmpty() + { + return $this->resultSet === null; + } + + public function setEndPoint($endPoint) + { + $this->endPoint = $endPoint; + + return $this; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/Exception.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/Exception.php new file mode 100644 index 0000000..d26ad02 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/Exception.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 Pagerfanta\Exception; + +/** + * Exception. + * + * @author Pablo Díez + */ +interface Exception +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/InvalidArgumentException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..d4fdb94 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/InvalidArgumentException.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 Pagerfanta\Exception; + +/** + * InvalidArgumentException. + * + * @author Pablo Díez + */ +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1CurrentPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1CurrentPageException.php new file mode 100644 index 0000000..da559ac --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1CurrentPageException.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 Pagerfanta\Exception; + +/** + * LessThan1CurrentPageException. + * + * @author Pablo Díez + */ +class LessThan1CurrentPageException extends NotValidCurrentPageException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1MaxPerPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1MaxPerPageException.php new file mode 100644 index 0000000..847886f --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LessThan1MaxPerPageException.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 Pagerfanta\Exception; + +/** + * LessThan1MaxPerPageException + * + * @author Pablo Díez + */ +class LessThan1MaxPerPageException extends NotValidMaxPerPageException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LogicException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LogicException.php new file mode 100644 index 0000000..14c9b11 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/LogicException.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 Pagerfanta\Exception; + +/** + * LogicException. + * + * @author Pablo Díez + */ +class LogicException extends \LogicException implements Exception +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotBooleanException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotBooleanException.php new file mode 100644 index 0000000..ea3d85f --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotBooleanException.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 Pagerfanta\Exception; + +/** + * @author Pablo Díez + */ +class NotBooleanException extends InvalidArgumentException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerCurrentPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerCurrentPageException.php new file mode 100644 index 0000000..119fd44 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerCurrentPageException.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 Pagerfanta\Exception; + +/** + * NotIntegerCurrentPageException. + * + * @author Pablo Díez + */ +class NotIntegerCurrentPageException extends NotValidCurrentPageException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerException.php new file mode 100644 index 0000000..ff6de11 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerException.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 Pagerfanta\Exception; + +/** + * NotIntegerItemException. + * + * @author Alexandre Fayeaux + */ +class NotIntegerException extends InvalidArgumentException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerMaxPerPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerMaxPerPageException.php new file mode 100644 index 0000000..5fb1008 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotIntegerMaxPerPageException.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 Pagerfanta\Exception; + +/** + * NotIntegerMaxPerPageException + * + * @author Pablo Díez + */ +class NotIntegerMaxPerPageException extends NotValidMaxPerPageException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidCurrentPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidCurrentPageException.php new file mode 100644 index 0000000..ef136de --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidCurrentPageException.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 Pagerfanta\Exception; + +/** + * NotValidCurrentPageException. + * + * @author Pablo Díez + */ +class NotValidCurrentPageException extends InvalidArgumentException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidMaxPerPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidMaxPerPageException.php new file mode 100644 index 0000000..20e78b9 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/NotValidMaxPerPageException.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 Pagerfanta\Exception; + +/** + * NotValidMaxPerPageException. + * + * @author Pablo Díez + */ +class NotValidMaxPerPageException extends InvalidArgumentException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/OutOfRangeCurrentPageException.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/OutOfRangeCurrentPageException.php new file mode 100644 index 0000000..5100ddb --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Exception/OutOfRangeCurrentPageException.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 Pagerfanta\Exception; + +/** + * OutOfRangeCurrentPageException. + * + * @author Pablo Díez + */ +class OutOfRangeCurrentPageException extends NotValidCurrentPageException +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Pagerfanta.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Pagerfanta.php new file mode 100644 index 0000000..36858fd --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/Pagerfanta.php @@ -0,0 +1,527 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta; + +use OutOfBoundsException; +use Pagerfanta\Adapter\AdapterInterface; +use Pagerfanta\Exception\LogicException; +use Pagerfanta\Exception\NotBooleanException; +use Pagerfanta\Exception\NotIntegerException; +use Pagerfanta\Exception\NotIntegerMaxPerPageException; +use Pagerfanta\Exception\LessThan1MaxPerPageException; +use Pagerfanta\Exception\NotIntegerCurrentPageException; +use Pagerfanta\Exception\LessThan1CurrentPageException; +use Pagerfanta\Exception\OutOfRangeCurrentPageException; + +/** + * Represents a paginator. + * + * @author Pablo Díez + */ +class Pagerfanta implements \Countable, \IteratorAggregate, PagerfantaInterface +{ + private $adapter; + private $allowOutOfRangePages; + private $normalizeOutOfRangePages; + private $maxPerPage; + private $currentPage; + private $nbResults; + private $currentPageResults; + + /** + * @param AdapterInterface $adapter An adapter. + */ + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + $this->allowOutOfRangePages = false; + $this->normalizeOutOfRangePages = false; + $this->maxPerPage = 10; + $this->currentPage = 1; + } + + /** + * Returns the adapter. + * + * @return AdapterInterface The adapter. + */ + public function getAdapter() + { + return $this->adapter; + } + + /** + * Sets whether or not allow out of range pages. + * + * @param Boolean $value + * + * @return self + */ + public function setAllowOutOfRangePages($value) + { + $this->allowOutOfRangePages = $this->filterBoolean($value); + + return $this; + } + + /** + * Returns whether or not allow out of range pages. + * + * @return Boolean + */ + public function getAllowOutOfRangePages() + { + return $this->allowOutOfRangePages; + } + + /** + * Sets whether or not normalize out of range pages. + * + * @param Boolean $value + * + * @return self + */ + public function setNormalizeOutOfRangePages($value) + { + $this->normalizeOutOfRangePages = $this->filterBoolean($value); + + return $this; + } + + /** + * Returns whether or not normalize out of range pages. + * + * @return Boolean + */ + public function getNormalizeOutOfRangePages() + { + return $this->normalizeOutOfRangePages; + } + + private function filterBoolean($value) + { + if (!is_bool($value)) { + throw new NotBooleanException(); + } + + return $value; + } + + /** + * Sets the max per page. + * + * Tries to convert from string and float. + * + * @param integer $maxPerPage + * + * @return self + * + * @throws NotIntegerMaxPerPageException If the max per page is not an integer even converting. + * @throws LessThan1MaxPerPageException If the max per page is less than 1. + */ + public function setMaxPerPage($maxPerPage) + { + $this->maxPerPage = $this->filterMaxPerPage($maxPerPage); + $this->resetForMaxPerPageChange(); + + return $this; + } + + private function filterMaxPerPage($maxPerPage) + { + $maxPerPage = $this->toInteger($maxPerPage); + $this->checkMaxPerPage($maxPerPage); + + return $maxPerPage; + } + + private function checkMaxPerPage($maxPerPage) + { + if (!is_int($maxPerPage)) { + throw new NotIntegerMaxPerPageException(); + } + + if ($maxPerPage < 1) { + throw new LessThan1MaxPerPageException(); + } + } + + private function resetForMaxPerPageChange() + { + $this->currentPageResults = null; + $this->nbResults = null; + } + + /** + * Returns the max per page. + * + * @return integer + */ + public function getMaxPerPage() + { + return $this->maxPerPage; + } + + /** + * Sets the current page. + * + * Tries to convert from string and float. + * + * @param integer $currentPage + * + * @return self + * + * @throws NotIntegerCurrentPageException If the current page is not an integer even converting. + * @throws LessThan1CurrentPageException If the current page is less than 1. + * @throws OutOfRangeCurrentPageException If It is not allowed out of range pages and they are not normalized. + */ + public function setCurrentPage($currentPage) + { + $this->useDeprecatedCurrentPageBooleanArguments(func_get_args()); + + $this->currentPage = $this->filterCurrentPage($currentPage); + $this->resetForCurrentPageChange(); + + return $this; + } + + private function useDeprecatedCurrentPageBooleanArguments($arguments) + { + $this->useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments); + $this->useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments); + } + + private function useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments) + { + $index = 1; + $method = 'setAllowOutOfRangePages'; + + $this->useDeprecatedBooleanArgument($arguments, $index, $method); + } + + private function useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments) + { + $index = 2; + $method = 'setNormalizeOutOfRangePages'; + + $this->useDeprecatedBooleanArgument($arguments, $index, $method); + } + + private function useDeprecatedBooleanArgument($arguments, $index, $method) + { + if (isset($arguments[$index])) { + $this->$method($arguments[$index]); + } + } + + private function filterCurrentPage($currentPage) + { + $currentPage = $this->toInteger($currentPage); + $this->checkCurrentPage($currentPage); + $currentPage = $this->filterOutOfRangeCurrentPage($currentPage); + + return $currentPage; + } + + private function checkCurrentPage($currentPage) + { + if (!is_int($currentPage)) { + throw new NotIntegerCurrentPageException(); + } + + if ($currentPage < 1) { + throw new LessThan1CurrentPageException(); + } + } + + private function filterOutOfRangeCurrentPage($currentPage) + { + if ($this->notAllowedCurrentPageOutOfRange($currentPage)) { + return $this->normalizeOutOfRangeCurrentPage($currentPage); + } + + return $currentPage; + } + + private function notAllowedCurrentPageOutOfRange($currentPage) + { + return !$this->getAllowOutOfRangePages() && + $this->currentPageOutOfRange($currentPage); + } + + private function currentPageOutOfRange($currentPage) + { + return $currentPage > 1 && $currentPage > $this->getNbPages(); + } + + /** + * @param int $currentPage + * + * @return int + * + * @throws OutOfRangeCurrentPageException If the page should not be normalized + */ + private function normalizeOutOfRangeCurrentPage($currentPage) + { + if ($this->getNormalizeOutOfRangePages()) { + return $this->getNbPages(); + } + + throw new OutOfRangeCurrentPageException(sprintf('Page "%d" does not exist. The currentPage must be inferior to "%d"', $currentPage, $this->getNbPages())); + } + + private function resetForCurrentPageChange() + { + $this->currentPageResults = null; + } + + /** + * Returns the current page. + * + * @return integer + */ + public function getCurrentPage() + { + return $this->currentPage; + } + + /** + * Returns the results for the current page. + * + * @return array|\Traversable + */ + public function getCurrentPageResults() + { + if ($this->notCachedCurrentPageResults()) { + $this->currentPageResults = $this->getCurrentPageResultsFromAdapter(); + } + + return $this->currentPageResults; + } + + private function notCachedCurrentPageResults() + { + return $this->currentPageResults === null; + } + + private function getCurrentPageResultsFromAdapter() + { + $offset = $this->calculateOffsetForCurrentPageResults(); + $length = $this->getMaxPerPage(); + + return $this->adapter->getSlice($offset, $length); + } + + private function calculateOffsetForCurrentPageResults() + { + return ($this->getCurrentPage() - 1) * $this->getMaxPerPage(); + } + + /** + * Calculates the current page offset start + * + * @return int + */ + public function getCurrentPageOffsetStart() + { + return $this->getNbResults() ? + $this->calculateOffsetForCurrentPageResults() + 1 : + 0; + } + + /** + * Calculates the current page offset end + * + * @return int + */ + public function getCurrentPageOffsetEnd() + { + return $this->hasNextPage() ? + $this->getCurrentPage() * $this->getMaxPerPage() : + $this->getNbResults(); + } + + /** + * Returns the number of results. + * + * @return integer + */ + public function getNbResults() + { + if ($this->notCachedNbResults()) { + $this->nbResults = $this->getAdapter()->getNbResults(); + } + + return $this->nbResults; + } + + private function notCachedNbResults() + { + return $this->nbResults === null; + } + + /** + * Returns the number of pages. + * + * @return integer + */ + public function getNbPages() + { + $nbPages = $this->calculateNbPages(); + + if ($nbPages == 0) { + return $this->minimumNbPages(); + } + + return $nbPages; + } + + private function calculateNbPages() + { + return (int) ceil($this->getNbResults() / $this->getMaxPerPage()); + } + + private function minimumNbPages() + { + return 1; + } + + /** + * Returns if the number of results is higher than the max per page. + * + * @return Boolean + */ + public function haveToPaginate() + { + return $this->getNbResults() > $this->maxPerPage; + } + + /** + * Returns whether there is previous page or not. + * + * @return Boolean + */ + public function hasPreviousPage() + { + return $this->currentPage > 1; + } + + /** + * Returns the previous page. + * + * @return integer + * + * @throws LogicException If there is no previous page. + */ + public function getPreviousPage() + { + if (!$this->hasPreviousPage()) { + throw new LogicException('There is no previous page.'); + } + + return $this->currentPage - 1; + } + + /** + * Returns whether there is next page or not. + * + * @return Boolean + */ + public function hasNextPage() + { + return $this->currentPage < $this->getNbPages(); + } + + /** + * Returns the next page. + * + * @return integer + * + * @throws LogicException If there is no next page. + */ + public function getNextPage() + { + if (!$this->hasNextPage()) { + throw new LogicException('There is no next page.'); + } + + return $this->currentPage + 1; + } + + /** + * Implements the \Countable interface. + * + * Return integer The number of results. + */ + public function count() + { + return $this->getNbResults(); + } + + /** + * Implements the \IteratorAggregate interface. + * + * Returns an \ArrayIterator instance with the current results. + */ + public function getIterator() + { + $results = $this->getCurrentPageResults(); + + if ($results instanceof \Iterator) { + return $results; + } + + if ($results instanceof \IteratorAggregate) { + return $results->getIterator(); + } + + return new \ArrayIterator($results); + } + + private function toInteger($value) + { + if ($this->needsToIntegerConversion($value)) { + return (int) $value; + } + + return $value; + } + + private function needsToIntegerConversion($value) + { + return (is_string($value) || is_float($value)) && (int) $value == $value; + } + + /** + * Get page number of the item at specified position (1-based index) + * + * @param integer $position + * + * @return integer + */ + public function getPageNumberForItemAtPosition($position) + { + if (!is_int($position)) { + throw new NotIntegerException(); + } + + if ($this->getNbResults() < $position) { + throw new OutOfBoundsException(sprintf( + 'Item requested at position %d, but there are only %d items.', + $position, + $this->getNbResults() + )); + } + + return (int) ceil($position/$this->getMaxPerPage()); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/PagerfantaInterface.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/PagerfantaInterface.php new file mode 100644 index 0000000..c18a297 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/PagerfantaInterface.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 Pagerfanta; + +/** + * @deprecated + */ +interface PagerfantaInterface +{ +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/DefaultView.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/DefaultView.php new file mode 100644 index 0000000..65cc34a --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/DefaultView.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 Pagerfanta\View; + +use Pagerfanta\PagerfantaInterface; +use Pagerfanta\View\Template\TemplateInterface; +use Pagerfanta\View\Template\DefaultTemplate; + +/** + * @author Pablo Díez + */ +class DefaultView implements ViewInterface +{ + private $template; + + private $pagerfanta; + private $proximity; + + private $currentPage; + private $nbPages; + + private $startPage; + private $endPage; + + public function __construct(TemplateInterface $template = null) + { + $this->template = $template ?: $this->createDefaultTemplate(); + } + + protected function createDefaultTemplate() + { + return new DefaultTemplate(); + } + + /** + * {@inheritdoc} + */ + public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array()) + { + $this->initializePagerfanta($pagerfanta); + $this->initializeOptions($options); + + $this->configureTemplate($routeGenerator, $options); + + return $this->generate(); + } + + private function initializePagerfanta(PagerfantaInterface $pagerfanta) + { + $this->pagerfanta = $pagerfanta; + + $this->currentPage = $pagerfanta->getCurrentPage(); + $this->nbPages = $pagerfanta->getNbPages(); + } + + private function initializeOptions($options) + { + $this->proximity = isset($options['proximity']) ? + (int) $options['proximity'] : + $this->getDefaultProximity(); + } + + protected function getDefaultProximity() + { + return 2; + } + + private function configureTemplate($routeGenerator, $options) + { + $this->template->setRouteGenerator($routeGenerator); + $this->template->setOptions($options); + } + + private function generate() + { + $pages = $this->generatePages(); + + return $this->generateContainer($pages); + } + + private function generateContainer($pages) + { + return str_replace('%pages%', $pages, $this->template->container()); + } + + private function generatePages() + { + $this->calculateStartAndEndPage(); + + return $this->previous(). + $this->first(). + $this->secondIfStartIs3(). + $this->dotsIfStartIsOver3(). + $this->pages(). + $this->dotsIfEndIsUnder3ToLast(). + $this->secondToLastIfEndIs3ToLast(). + $this->last(). + $this->next(); + } + + private function calculateStartAndEndPage() + { + $startPage = $this->currentPage - $this->proximity; + $endPage = $this->currentPage + $this->proximity; + + if ($this->startPageUnderflow($startPage)) { + $endPage = $this->calculateEndPageForStartPageUnderflow($startPage, $endPage); + $startPage = 1; + } + if ($this->endPageOverflow($endPage)) { + $startPage = $this->calculateStartPageForEndPageOverflow($startPage, $endPage); + $endPage = $this->nbPages; + } + + $this->startPage = $startPage; + $this->endPage = $endPage; + } + + private function startPageUnderflow($startPage) + { + return $startPage < 1; + } + + private function endPageOverflow($endPage) + { + return $endPage > $this->nbPages; + } + + private function calculateEndPageForStartPageUnderflow($startPage, $endPage) + { + return min($endPage + (1 - $startPage), $this->nbPages); + } + + private function calculateStartPageForEndPageOverflow($startPage, $endPage) + { + return max($startPage - ($endPage - $this->nbPages), 1); + } + + private function previous() + { + if ($this->pagerfanta->hasPreviousPage()) { + return $this->template->previousEnabled($this->pagerfanta->getPreviousPage()); + } + + return $this->template->previousDisabled(); + } + + private function first() + { + if ($this->startPage > 1) { + return $this->template->first(); + } + } + + private function secondIfStartIs3() + { + if ($this->startPage == 3) { + return $this->template->page(2); + } + } + + private function dotsIfStartIsOver3() + { + if ($this->startPage > 3) { + return $this->template->separator(); + } + } + + private function pages() + { + $pages = ''; + + foreach (range($this->startPage, $this->endPage) as $page) { + $pages .= $this->page($page); + } + + return $pages; + } + + private function page($page) + { + if ($page == $this->currentPage) { + return $this->template->current($page); + } + + return $this->template->page($page); + } + + private function dotsIfEndIsUnder3ToLast() + { + if ($this->endPage < $this->toLast(3)) { + return $this->template->separator(); + } + } + + private function secondToLastIfEndIs3ToLast() + { + if ($this->endPage == $this->toLast(3)) { + return $this->template->page($this->toLast(2)); + } + } + + private function toLast($n) + { + return $this->pagerfanta->getNbPages() - ($n - 1); + } + + private function last() + { + if ($this->pagerfanta->getNbPages() > $this->endPage) { + return $this->template->last($this->pagerfanta->getNbPages()); + } + } + + private function next() + { + if ($this->pagerfanta->hasNextPage()) { + return $this->template->nextEnabled($this->pagerfanta->getNextPage()); + } + + return $this->template->nextDisabled(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'default'; + } +} + +/* + +CSS: + +.pagerfanta { +} + +.pagerfanta a, +.pagerfanta span { + display: inline-block; + border: 1px solid blue; + color: blue; + margin-right: .2em; + padding: .25em .35em; +} + +.pagerfanta a { + text-decoration: none; +} + +.pagerfanta a:hover { + background: #ccf; +} + +.pagerfanta .dots { + border-width: 0; +} + +.pagerfanta .current { + background: #ccf; + font-weight: bold; +} + +.pagerfanta .disabled { + border-color: #ccf; + color: #ccf; +} + +COLORS: + +.pagerfanta a, +.pagerfanta span { + border-color: blue; + color: blue; +} + +.pagerfanta a:hover { + background: #ccf; +} + +.pagerfanta .current { + background: #ccf; +} + +.pagerfanta .disabled { + border-color: #ccf; + color: #cf; +} + +*/ diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/OptionableView.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/OptionableView.php new file mode 100644 index 0000000..e6147c7 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/OptionableView.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 Pagerfanta\View; + +use Pagerfanta\PagerfantaInterface; + +/** + * OptionableView. + * + * This view renders another view with a default options to reuse them in a project. + * + * @author Pablo Díez + */ +class OptionableView implements ViewInterface +{ + private $view; + private $defaultOptions; + + /** + * Constructor. + * + * @param ViewInterface $view A view. + * @param array $defaultOptions An array of default options. + */ + public function __construct(ViewInterface $view, array $defaultOptions) + { + $this->view = $view; + $this->defaultOptions = $defaultOptions; + } + + /** + * {@inheritdoc} + */ + public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array()) + { + return $this->view->render($pagerfanta, $routeGenerator, array_merge($this->defaultOptions, $options)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'optionable'; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/SemanticUiView.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/SemanticUiView.php new file mode 100644 index 0000000..62ca06b --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/SemanticUiView.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 Pagerfanta\View; + +use Pagerfanta\View\Template\SemanticUiTemplate; + +/** + * SemanticUiView. + * + * View that can be used with the pagination module + * from the Semantic UI CSS Toolkit + * http://semantic-ui.com/ + * + * @author Loïc Frémont + */ +class SemanticUiView extends DefaultView +{ + protected function createDefaultTemplate() + { + return new SemanticUiTemplate(); + } + + protected function getDefaultProximity() + { + return 3; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'semantic_ui'; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/DefaultTemplate.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/DefaultTemplate.php new file mode 100644 index 0000000..e040894 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/DefaultTemplate.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 Pagerfanta\View\Template; + +/** + * @author Pablo Díez + */ +class DefaultTemplate extends Template +{ + static protected $defaultOptions = array( + 'prev_message' => 'Previous', + 'next_message' => 'Next', + 'css_disabled_class' => 'disabled', + 'css_dots_class' => 'dots', + 'css_current_class' => 'current', + 'dots_text' => '...', + 'container_template' => '', + 'page_template' => '%text%', + 'span_template' => '%text%', + 'rel_previous' => 'prev', + 'rel_next' => 'next' + ); + + public function container() + { + return $this->option('container_template'); + } + + public function page($page) + { + $text = $page; + + return $this->pageWithText($page, $text); + } + + public function pageWithText($page, $text, $rel = null) + { + $search = array('%href%', '%text%', '%rel%'); + + $href = $this->generateRoute($page); + $replace = $rel ? array($href, $text, ' rel="' . $rel . '"') : array($href, $text, ''); + + return str_replace($search, $replace, $this->option('page_template')); + } + + public function previousDisabled() + { + return $this->generateSpan($this->option('css_disabled_class'), $this->option('prev_message')); + } + + public function previousEnabled($page) + { + return $this->pageWithText($page, $this->option('prev_message'), $this->option('rel_previous')); + } + + public function nextDisabled() + { + return $this->generateSpan($this->option('css_disabled_class'), $this->option('next_message')); + } + + public function nextEnabled($page) + { + return $this->pageWithText($page, $this->option('next_message'), $this->option('rel_next')); + } + + public function first() + { + return $this->page(1); + } + + public function last($page) + { + return $this->page($page); + } + + public function current($page) + { + return $this->generateSpan($this->option('css_current_class'), $page); + } + + public function separator() + { + return $this->generateSpan($this->option('css_dots_class'), $this->option('dots_text')); + } + + private function generateSpan($class, $page) + { + $search = array('%class%', '%text%'); + $replace = array($class, $page); + + return str_replace($search, $replace, $this->option('span_template')); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/SemanticUiTemplate.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/SemanticUiTemplate.php new file mode 100644 index 0000000..f607155 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/SemanticUiTemplate.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 Pagerfanta\View\Template; + +/** + * @author Loïc Frémont + */ +class SemanticUiTemplate extends Template +{ + static protected $defaultOptions = array( + 'prev_message' => '← Previous', + 'next_message' => 'Next →', + 'dots_message' => '…', + 'active_suffix' => '', + 'css_container_class' => 'ui stackable fluid pagination menu', + 'css_item_class' => 'item', + 'css_prev_class' => 'prev', + 'css_next_class' => 'next', + 'css_disabled_class' => 'disabled', + 'css_dots_class' => 'disabled', + 'css_active_class' => 'active', + ); + + public function container() + { + return sprintf('
    %%pages%%
    ', + $this->option('css_container_class') + ); + } + + public function page($page) + { + $text = $page; + + return $this->pageWithText($page, $text); + } + + public function pageWithText($page, $text) + { + $class = null; + + return $this->pageWithTextAndClass($page, $text, $class); + } + + private function pageWithTextAndClass($page, $text, $class) + { + $href = $this->generateRoute($page); + + return $this->link($class, $href, $text); + } + + public function previousDisabled() + { + $class = $this->previousDisabledClass(); + $text = $this->option('prev_message'); + + return $this->div($class, $text); + } + + private function previousDisabledClass() + { + return $this->option('css_prev_class').' '.$this->option('css_disabled_class'); + } + + public function previousEnabled($page) + { + $text = $this->option('prev_message'); + $class = $this->option('css_prev_class'); + + return $this->pageWithTextAndClass($page, $text, $class); + } + + public function nextDisabled() + { + $class = $this->nextDisabledClass(); + $text = $this->option('next_message'); + + return $this->div($class, $text); + } + + private function nextDisabledClass() + { + return $this->option('css_next_class').' '.$this->option('css_disabled_class'); + } + + public function nextEnabled($page) + { + $text = $this->option('next_message'); + $class = $this->option('css_next_class'); + + return $this->pageWithTextAndClass($page, $text, $class); + } + + public function first() + { + return $this->page(1); + } + + public function last($page) + { + return $this->page($page); + } + + public function current($page) + { + $text = trim($page.' '.$this->option('active_suffix')); + $class = $this->option('css_active_class'); + + return $this->div($class, $text); + } + + public function separator() + { + $class = $this->option('css_dots_class'); + $text = $this->option('dots_message'); + + return $this->div($class, $text); + } + + private function link($class, $href, $text) + { + $item_class = $this->option('css_item_class'); + + return sprintf('%s', $item_class, $class, $href, $text); + } + + private function div($class, $text) + { + $item_class = $this->option('css_item_class'); + + return sprintf('
    %s
    ', $item_class, $class, $text); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/Template.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/Template.php new file mode 100644 index 0000000..0713602 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/Template.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 Pagerfanta\View\Template; + +/** + * @author Pablo Díez + */ +abstract class Template implements TemplateInterface +{ + protected static $defaultOptions = array(); + + private $routeGenerator; + private $options; + + public function __construct() + { + $this->initializeOptions(); + } + + public function setRouteGenerator($routeGenerator) + { + $this->routeGenerator = $routeGenerator; + } + + public function setOptions(array $options) + { + $this->options = array_merge($this->options, $options); + } + + private function initializeOptions() + { + $this->options = static::$defaultOptions; + } + + protected function generateRoute($page) + { + return call_user_func($this->getRouteGenerator(), $page); + } + + private function getRouteGenerator() + { + if (!$this->routeGenerator) { + throw new \RuntimeException('There is no route generator.'); + } + + return $this->routeGenerator; + } + + protected function option($name) + { + if (!isset($this->options[$name])) { + throw new \InvalidArgumentException(sprintf('The option "%s" does not exist.', $name)); + } + + return $this->options[$name]; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TemplateInterface.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TemplateInterface.php new file mode 100644 index 0000000..5408127 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TemplateInterface.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 Pagerfanta\View\Template; + +/** + * @author Pablo Díez + */ +interface TemplateInterface +{ + /** + * Renders the container for the pagination. + * + * The %pages% placeholder will be replaced by the rendering of pages + * + * @return string + */ + public function container(); + + /** + * Renders a given page. + * + * @param int $page + * + * @return string + */ + public function page($page); + + /** + * Renders a given page with a specified text. + * + * @param int $page + * @param string $text + * + * @return string + */ + public function pageWithText($page, $text); + + /** + * Renders the disabled state of the previous page. + * + * @return string + */ + public function previousDisabled(); + + /** + * Renders the enabled state of the previous page. + * + * @param int $page + * + * @return string + */ + public function previousEnabled($page); + + /** + * Renders the disabled state of the next page. + * + * @return string + */ + public function nextDisabled(); + + /** + * Renders the enabled state of the next page. + * + * @param int $page + * + * @return string + */ + public function nextEnabled($page); + + /** + * Renders the first page. + * + * @return string + */ + public function first(); + + /** + * Renders the last page. + * + * @param int $page + * + * @return string + */ + public function last($page); + + /** + * Renders the current page. + * + * @param int $page + * + * @return string + */ + public function current($page); + + /** + * Renders the separator between pages. + * + * @return string + */ + public function separator(); +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap3Template.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap3Template.php new file mode 100644 index 0000000..525352f --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap3Template.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 Pagerfanta\View\Template; + +/** + * TwitterBootstrap3Template + */ +class TwitterBootstrap3Template extends TwitterBootstrapTemplate +{ + public function __construct() + { + parent::__construct(); + + $this->setOptions(array('active_suffix' => '(current)')); + } + + public function container() + { + return sprintf('
      %%pages%%
    ', + $this->option('css_container_class') + ); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap4Template.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap4Template.php new file mode 100644 index 0000000..9bd6a4c --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrap4Template.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 Pagerfanta\View\Template; + +/** + * TwitterBootstrap4Template + */ +class TwitterBootstrap4Template extends TwitterBootstrap3Template +{ + protected function linkLi($class, $href, $text, $rel = null) + { + $liClass = implode(' ', array_filter(array('page-item', $class))); + $rel = $rel ? sprintf(' rel="%s"', $rel) : ''; + + return sprintf('
  • %s
  • ', $liClass, $href, $rel, $text); + } + + protected function spanLi($class, $text) + { + $liClass = implode(' ', array_filter(array('page-item', $class))); + + return sprintf('
  • %s
  • ', $liClass, $text); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrapTemplate.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrapTemplate.php new file mode 100644 index 0000000..474c730 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/Template/TwitterBootstrapTemplate.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Pagerfanta\View\Template; + +/** + * @author Pablo Díez + */ +class TwitterBootstrapTemplate extends Template +{ + static protected $defaultOptions = array( + 'prev_message' => '← Previous', + 'next_message' => 'Next →', + 'dots_message' => '…', + 'active_suffix' => '', + 'css_container_class' => 'pagination', + 'css_prev_class' => 'prev', + 'css_next_class' => 'next', + 'css_disabled_class' => 'disabled', + 'css_dots_class' => 'disabled', + 'css_active_class' => 'active', + 'rel_previous' => 'prev', + 'rel_next' => 'next' + ); + + public function container() + { + return sprintf('
      %%pages%%
    ', + $this->option('css_container_class') + ); + } + + public function page($page) + { + $text = $page; + + return $this->pageWithText($page, $text); + } + + public function pageWithText($page, $text) + { + $class = null; + + return $this->pageWithTextAndClass($page, $text, $class); + } + + private function pageWithTextAndClass($page, $text, $class, $rel = null) + { + $href = $this->generateRoute($page); + + return $this->linkLi($class, $href, $text, $rel); + } + + public function previousDisabled() + { + $class = $this->previousDisabledClass(); + $text = $this->option('prev_message'); + + return $this->spanLi($class, $text); + } + + private function previousDisabledClass() + { + return $this->option('css_prev_class').' '.$this->option('css_disabled_class'); + } + + public function previousEnabled($page) + { + $text = $this->option('prev_message'); + $class = $this->option('css_prev_class'); + $rel = $this->option('rel_previous'); + + return $this->pageWithTextAndClass($page, $text, $class, $rel); + } + + public function nextDisabled() + { + $class = $this->nextDisabledClass(); + $text = $this->option('next_message'); + + return $this->spanLi($class, $text); + } + + private function nextDisabledClass() + { + return $this->option('css_next_class').' '.$this->option('css_disabled_class'); + } + + public function nextEnabled($page) + { + $text = $this->option('next_message'); + $class = $this->option('css_next_class'); + $rel = $this->option('rel_next'); + + return $this->pageWithTextAndClass($page, $text, $class, $rel); + } + + public function first() + { + return $this->page(1); + } + + public function last($page) + { + return $this->page($page); + } + + public function current($page) + { + $text = trim($page.' '.$this->option('active_suffix')); + $class = $this->option('css_active_class'); + + return $this->spanLi($class, $text); + } + + public function separator() + { + $class = $this->option('css_dots_class'); + $text = $this->option('dots_message'); + + return $this->spanLi($class, $text); + } + + protected function linkLi($class, $href, $text, $rel = null) + { + $liClass = $class ? sprintf(' class="%s"', $class) : ''; + $rel = $rel ? sprintf(' rel="%s"', $rel) : ''; + + return sprintf('%s', $liClass, $href, $rel, $text); + } + + protected function spanLi($class, $text) + { + $liClass = $class ? sprintf(' class="%s"', $class) : ''; + + return sprintf('%s', $liClass, $text); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap3View.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap3View.php new file mode 100644 index 0000000..f314e38 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap3View.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 Pagerfanta\View; + +use Pagerfanta\View\Template\TwitterBootstrap3Template; + +/** + * TwitterBootstrap3View. + * + * View that can be used with the pagination module + * from the Twitter Bootstrap3 CSS Toolkit + * http://getbootstrap.com/ + * + */ +class TwitterBootstrap3View extends TwitterBootstrapView +{ + protected function createDefaultTemplate() + { + return new TwitterBootstrap3Template(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'twitter_bootstrap3'; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap4View.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap4View.php new file mode 100644 index 0000000..de8190f --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrap4View.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 Pagerfanta\View; + +use Pagerfanta\View\Template\TwitterBootstrap4Template; + +/** + * TwitterBootstrap4View. + * + * View that can be used with the pagination module + * from the Twitter Bootstrap4 CSS Toolkit + * http://getbootstrap.com/ + * + */ +class TwitterBootstrap4View extends TwitterBootstrapView +{ + protected function createDefaultTemplate() + { + return new TwitterBootstrap4Template(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'twitter_bootstrap4'; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrapView.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrapView.php new file mode 100644 index 0000000..942f9c1 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/TwitterBootstrapView.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 Pagerfanta\View; + +use Pagerfanta\View\Template\TwitterBootstrapTemplate; + +/** + * TwitterBootstrapView. + * + * View that can be used with the pagination module + * from the Twitter Bootstrap CSS Toolkit + * http://twitter.github.com/bootstrap/ + * + * @author Pablo Díez + * @author Jan Sorgalla + */ +class TwitterBootstrapView extends DefaultView +{ + protected function createDefaultTemplate() + { + return new TwitterBootstrapTemplate(); + } + + protected function getDefaultProximity() + { + return 3; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'twitter_bootstrap'; + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactory.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactory.php new file mode 100644 index 0000000..16e3b37 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactory.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 Pagerfanta\View; + +use Pagerfanta\Exception\InvalidArgumentException; + +/** + * ViewFactory. + * + * @author Pablo Díez + */ +class ViewFactory implements ViewFactoryInterface +{ + private $views; + + /** + * Constructor. + */ + public function __construct() + { + $this->views = array(); + } + + /** + * {@inheritdoc} + */ + public function set($name, ViewInterface $view) + { + $this->views[$name] = $view; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return isset($this->views[$name]); + } + + /** + * {@inheritdoc} + */ + public function add(array $views) + { + foreach ($views as $name => $view) { + $this->set($name, $view); + } + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name)); + } + + return $this->views[$name]; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + if (!$this->has($name)) { + throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name)); + } + + unset($this->views[$name]); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->views; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->views = array(); + } +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactoryInterface.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactoryInterface.php new file mode 100644 index 0000000..97398e6 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewFactoryInterface.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 Pagerfanta\View; + +/** + * ViewFactoryInterface. + * + * @author Pablo Díez + */ +interface ViewFactoryInterface +{ + /** + * Sets a view. + * + * @param string $name The view name. + * @param ViewInterface $view The view. + */ + public function set($name, ViewInterface $view); + + /** + * Returns whether a view exists or not. + * + * @param string $name The name. + * + * @return Boolean Whether a view exists or not. + */ + public function has($name); + + /** + * Adds views. + * + * @param array $views An array of views. + */ + public function add(array $views); + + /** + * Returns a view. + * + * @param string $name The name. + * + * @return ViewInterface The view. + * + * @throws \InvalidArgumentException If the view does not exist. + */ + public function get($name); + + /** + * Returns all the views. + * + * @return array The views. + */ + public function all(); + + /** + * Removes a view. + * + * @param string $name The name. + * + * @throws \InvalidArgumentException If the view does not exist. + */ + public function remove($name); + + /** + * Clears the views. + */ + public function clear(); +} diff --git a/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewInterface.php b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewInterface.php new file mode 100644 index 0000000..a8dad35 --- /dev/null +++ b/vendor/pagerfanta/pagerfanta/src/Pagerfanta/View/ViewInterface.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 Pagerfanta\View; + +use Pagerfanta\PagerfantaInterface; + +/** + * ViewInterface. + * + * @author Pablo Díez + */ +interface ViewInterface +{ + /** + * Renders a pagerfanta. + * + * The route generator can be any callable to generate + * the routes receiving the page number as first and + * unique argument. + * + * @param PagerfantaInterface $pagerfanta A pagerfanta. + * @param callable $routeGenerator A callable to generate the routes. + * @param array $options An array of options (optional). + */ + public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array()); + + /** + * Returns the canonical name. + * + * @return string The canonical name. + */ + public function getName(); +} diff --git a/vendor/paragonie/random_compat/LICENSE b/vendor/paragonie/random_compat/LICENSE new file mode 100644 index 0000000..45c7017 --- /dev/null +++ b/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/vendor/paragonie/random_compat/build-phar.sh b/vendor/paragonie/random_compat/build-phar.sh new file mode 100755 index 0000000..b4a5ba3 --- /dev/null +++ b/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/vendor/paragonie/random_compat/composer.json b/vendor/paragonie/random_compat/composer.json new file mode 100644 index 0000000..1fa8de9 --- /dev/null +++ b/vendor/paragonie/random_compat/composer.json @@ -0,0 +1,34 @@ +{ + "name": "paragonie/random_compat", + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "random", + "polyfill", + "pseudorandom" + ], + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "support": { + "issues": "https://github.com/paragonie/random_compat/issues", + "email": "info@paragonie.com", + "source": "https://github.com/paragonie/random_compat" + }, + "require": { + "php": "^7" + }, + "require-dev": { + "vimeo/psalm": "^1", + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + } +} diff --git a/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey new file mode 100644 index 0000000..eb50ebf --- /dev/null +++ b/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/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc b/vendor/paragonie/random_compat/dist/random_compat.phar.pubkey.asc new file mode 100644 index 0000000..6a1d7f3 --- /dev/null +++ b/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/vendor/paragonie/random_compat/lib/random.php b/vendor/paragonie/random_compat/lib/random.php new file mode 100644 index 0000000..c7731a5 --- /dev/null +++ b/vendor/paragonie/random_compat/lib/random.php @@ -0,0 +1,32 @@ +buildFromDirectory(dirname(__DIR__).'/lib'); +rename( + dirname(__DIR__).'/lib/index.php', + dirname(__DIR__).'/lib/random.php' +); + +/** + * If we pass an (optional) path to a private key as a second argument, we will + * sign the Phar with OpenSSL. + * + * If you leave this out, it will produce an unsigned .phar! + */ +if ($argc > 1) { + if (!@is_readable($argv[1])) { + echo 'Could not read the private key file:', $argv[1], "\n"; + exit(255); + } + $pkeyFile = file_get_contents($argv[1]); + + $private = openssl_get_privatekey($pkeyFile); + if ($private !== false) { + $pkey = ''; + openssl_pkey_export($private, $pkey); + $phar->setSignatureAlgorithm(Phar::OPENSSL, $pkey); + + /** + * Save the corresponding public key to the file + */ + if (!@is_readable($dist.'/random_compat.phar.pubkey')) { + $details = openssl_pkey_get_details($private); + file_put_contents( + $dist.'/random_compat.phar.pubkey', + $details['key'] + ); + } + } else { + echo 'An error occurred reading the private key from OpenSSL.', "\n"; + exit(255); + } +} diff --git a/vendor/paragonie/random_compat/psalm-autoload.php b/vendor/paragonie/random_compat/psalm-autoload.php new file mode 100644 index 0000000..d71d1b8 --- /dev/null +++ b/vendor/paragonie/random_compat/psalm-autoload.php @@ -0,0 +1,9 @@ + + + + + + + + + + + + + + + diff --git a/vendor/psr/cache/CHANGELOG.md b/vendor/psr/cache/CHANGELOG.md new file mode 100644 index 0000000..58ddab0 --- /dev/null +++ b/vendor/psr/cache/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this project will be documented in this file, in reverse chronological order by release. + +## 1.0.1 - 2016-08-06 + +### Fixed + +- Make spacing consistent in phpdoc annotations php-fig/cache#9 - chalasr +- Fix grammar in phpdoc annotations php-fig/cache#10 - chalasr +- Be more specific in docblocks that `getItems()` and `deleteItems()` take an array of strings (`string[]`) compared to just `array` php-fig/cache#8 - GrahamCampbell +- For `expiresAt()` and `expiresAfter()` in CacheItemInterface fix docblock to specify null as a valid parameters as well as an implementation of DateTimeInterface php-fig/cache#7 - GrahamCampbell + +## 1.0.0 - 2015-12-11 + +Initial stable release; reflects accepted PSR-6 specification diff --git a/vendor/psr/cache/LICENSE.txt b/vendor/psr/cache/LICENSE.txt new file mode 100644 index 0000000..b1c2c97 --- /dev/null +++ b/vendor/psr/cache/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2015 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/vendor/psr/cache/README.md b/vendor/psr/cache/README.md new file mode 100644 index 0000000..c8706ce --- /dev/null +++ b/vendor/psr/cache/README.md @@ -0,0 +1,9 @@ +PSR Cache +========= + +This repository holds all interfaces defined by +[PSR-6](http://www.php-fig.org/psr/psr-6/). + +Note that this is not a Cache implementation of its own. It is merely an +interface that describes a Cache implementation. See the specification for more +details. diff --git a/vendor/psr/cache/composer.json b/vendor/psr/cache/composer.json new file mode 100644 index 0000000..e828fec --- /dev/null +++ b/vendor/psr/cache/composer.json @@ -0,0 +1,25 @@ +{ + "name": "psr/cache", + "description": "Common interface for caching libraries", + "keywords": ["psr", "psr-6", "cache"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/psr/cache/src/CacheException.php b/vendor/psr/cache/src/CacheException.php new file mode 100644 index 0000000..e27f22f --- /dev/null +++ b/vendor/psr/cache/src/CacheException.php @@ -0,0 +1,10 @@ +=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/psr/container/src/ContainerExceptionInterface.php b/vendor/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 0000000..d35c6b4 --- /dev/null +++ b/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,13 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } +} diff --git a/vendor/psr/log/Psr/Log/InvalidArgumentException.php b/vendor/psr/log/Psr/Log/InvalidArgumentException.php new file mode 100644 index 0000000..67f852d --- /dev/null +++ b/vendor/psr/log/Psr/Log/InvalidArgumentException.php @@ -0,0 +1,7 @@ +logger = $logger; + } +} diff --git a/vendor/psr/log/Psr/Log/LoggerInterface.php b/vendor/psr/log/Psr/Log/LoggerInterface.php new file mode 100644 index 0000000..5ea7243 --- /dev/null +++ b/vendor/psr/log/Psr/Log/LoggerInterface.php @@ -0,0 +1,123 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function alert($message, array $context = array()) + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function critical($message, array $context = array()) + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function error($message, array $context = array()) + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function warning($message, array $context = array()) + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function notice($message, array $context = array()) + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function info($message, array $context = array()) + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string $message + * @param array $context + * + * @return void + */ + public function debug($message, array $context = array()) + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + abstract public function log($level, $message, array $context = array()); +} diff --git a/vendor/psr/log/Psr/Log/NullLogger.php b/vendor/psr/log/Psr/Log/NullLogger.php new file mode 100644 index 0000000..d8cd682 --- /dev/null +++ b/vendor/psr/log/Psr/Log/NullLogger.php @@ -0,0 +1,28 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + * + * @return void + */ + public function log($level, $message, array $context = array()) + { + // noop + } +} diff --git a/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php new file mode 100644 index 0000000..a0391a5 --- /dev/null +++ b/vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php @@ -0,0 +1,140 @@ + ". + * + * Example ->error('Foo') would yield "error Foo". + * + * @return string[] + */ + abstract public function getLogs(); + + public function testImplements() + { + $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->getLogger()); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $logger = $this->getLogger(); + $logger->{$level}($message, array('user' => 'Bob')); + $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->assertEquals($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}'), + ); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $logger = $this->getLogger(); + $logger->log('invalid level', 'Foo'); + } + + public function testContextReplacement() + { + $logger = $this->getLogger(); + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('info {Message {nothing} Bob Bar a}'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + 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() + { + $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->getLogger()->warning('Crazy context data', $context); + + $expected = array('warning Crazy context data'); + $this->assertEquals($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $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 +{ + public function __toString() + { + } +} diff --git a/vendor/psr/log/README.md b/vendor/psr/log/README.md new file mode 100644 index 0000000..574bc1c --- /dev/null +++ b/vendor/psr/log/README.md @@ -0,0 +1,45 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/vendor/psr/log/composer.json b/vendor/psr/log/composer.json new file mode 100644 index 0000000..87934d7 --- /dev/null +++ b/vendor/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/psr/simple-cache/.editorconfig b/vendor/psr/simple-cache/.editorconfig new file mode 100644 index 0000000..48542cb --- /dev/null +++ b/vendor/psr/simple-cache/.editorconfig @@ -0,0 +1,12 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/vendor/psr/simple-cache/LICENSE.md b/vendor/psr/simple-cache/LICENSE.md new file mode 100644 index 0000000..e49a7c8 --- /dev/null +++ b/vendor/psr/simple-cache/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +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/vendor/psr/simple-cache/README.md b/vendor/psr/simple-cache/README.md new file mode 100644 index 0000000..43641d1 --- /dev/null +++ b/vendor/psr/simple-cache/README.md @@ -0,0 +1,8 @@ +PHP FIG Simple Cache PSR +======================== + +This repository holds all interfaces related to PSR-16. + +Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details. + +You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package. diff --git a/vendor/psr/simple-cache/composer.json b/vendor/psr/simple-cache/composer.json new file mode 100644 index 0000000..2978fa5 --- /dev/null +++ b/vendor/psr/simple-cache/composer.json @@ -0,0 +1,25 @@ +{ + "name": "psr/simple-cache", + "description": "Common interfaces for simple caching", + "keywords": ["psr", "psr-16", "cache", "simple-cache", "caching"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + } +} diff --git a/vendor/psr/simple-cache/src/CacheException.php b/vendor/psr/simple-cache/src/CacheException.php new file mode 100644 index 0000000..eba5381 --- /dev/null +++ b/vendor/psr/simple-cache/src/CacheException.php @@ -0,0 +1,10 @@ + value pairs. Cache keys that do not exist or are stale will have $default as value. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function getMultiple($keys, $default = null); + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * @param iterable $values A list of key => value pairs for a multiple-set operation. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * + * @return bool True on success and false on failure. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $values is neither an array nor a Traversable, + * or if any of the $values are not a legal value. + */ + public function setMultiple($values, $ttl = null); + + /** + * Deletes multiple cache items in a single operation. + * + * @param iterable $keys A list of string-based keys to be deleted. + * + * @return bool True if the items were successfully removed. False if there was an error. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function deleteMultiple($keys); + + /** + * Determines whether an item is present in the cache. + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * + * @return bool + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if the $key string is not a legal value. + */ + public function has($key); +} diff --git a/vendor/psr/simple-cache/src/InvalidArgumentException.php b/vendor/psr/simple-cache/src/InvalidArgumentException.php new file mode 100644 index 0000000..6a9524a --- /dev/null +++ b/vendor/psr/simple-cache/src/InvalidArgumentException.php @@ -0,0 +1,13 @@ +setName('sylius:fixtures:list') + ->setDescription('Lists available fixtures') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): void + { + $this->listSuites($output); + $this->listFixtures($output); + } + + /** + * @param OutputInterface $output + */ + private function listSuites(OutputInterface $output): void + { + $suites = $this->getSuiteRegistry()->getSuites(); + + $output->writeln('Available suites:'); + + foreach ($suites as $suite) { + $output->writeln(' - ' . $suite->getName()); + } + } + + /** + * @param OutputInterface $output + */ + private function listFixtures(OutputInterface $output): void + { + $fixtures = $this->getFixtureRegistry()->getFixtures(); + + $output->writeln('Available fixtures:'); + + foreach ($fixtures as $name => $fixture) { + $output->writeln(' - ' . $name); + } + } + + /** + * @return SuiteRegistryInterface + */ + private function getSuiteRegistry(): SuiteRegistryInterface + { + return $this->getContainer()->get('sylius_fixtures.suite_registry'); + } + + /** + * @return FixtureRegistryInterface + */ + private function getFixtureRegistry(): FixtureRegistryInterface + { + return $this->getContainer()->get('sylius_fixtures.fixture_registry'); + } +} diff --git a/vendor/sylius/fixtures-bundle/Command/FixturesLoadCommand.php b/vendor/sylius/fixtures-bundle/Command/FixturesLoadCommand.php new file mode 100644 index 0000000..9959993 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Command/FixturesLoadCommand.php @@ -0,0 +1,87 @@ +setName('sylius:fixtures:load') + ->setDescription('Loads fixtures from given suite') + ->addArgument('suite', InputArgument::OPTIONAL, 'Suite name', 'default') + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output): void + { + if ($input->isInteractive()) { + /** @var QuestionHelper $questionHelper */ + $questionHelper = $this->getHelper('question'); + + $output->writeln(sprintf( + "\nWarning! Loading fixtures will purge your database for the %s environment.\n", + $input->getOption('env') + )); + + if (!$questionHelper->ask($input, $output, new ConfirmationQuestion('Continue? (y/N) ', false))) { + return; + } + } + + $this->loadSuites($input); + } + + /** + * @param InputInterface $input + */ + private function loadSuites(InputInterface $input): void + { + $suiteName = $input->getArgument('suite'); + $suite = $this->getSuiteRegistry()->getSuite($suiteName); + + $this->getSuiteLoader()->load($suite); + } + + /** + * @return SuiteRegistryInterface + */ + private function getSuiteRegistry(): SuiteRegistryInterface + { + return $this->getContainer()->get('sylius_fixtures.suite_registry'); + } + + /** + * @return SuiteLoaderInterface + */ + private function getSuiteLoader(): SuiteLoaderInterface + { + return $this->getContainer()->get('sylius_fixtures.suite_loader'); + } +} diff --git a/vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/FixtureRegistryPass.php b/vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/FixtureRegistryPass.php new file mode 100644 index 0000000..aaa3e30 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/FixtureRegistryPass.php @@ -0,0 +1,38 @@ +has('sylius_fixtures.fixture_registry')) { + return; + } + + $fixtureRegistry = $container->findDefinition('sylius_fixtures.fixture_registry'); + + $taggedServices = $container->findTaggedServiceIds('sylius_fixtures.fixture'); + foreach (array_keys($taggedServices) as $id) { + $fixtureRegistry->addMethodCall('addFixture', [new Reference($id)]); + } + } +} diff --git a/vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/ListenerRegistryPass.php b/vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/ListenerRegistryPass.php new file mode 100644 index 0000000..b3d79c9 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/DependencyInjection/Compiler/ListenerRegistryPass.php @@ -0,0 +1,38 @@ +has('sylius_fixtures.listener_registry')) { + return; + } + + $listenerRegistry = $container->findDefinition('sylius_fixtures.listener_registry'); + + $taggedServices = $container->findTaggedServiceIds('sylius_fixtures.listener'); + foreach (array_keys($taggedServices) as $id) { + $listenerRegistry->addMethodCall('addListener', [new Reference($id)]); + } + } +} diff --git a/vendor/sylius/fixtures-bundle/DependencyInjection/Configuration.php b/vendor/sylius/fixtures-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..656e4d4 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,139 @@ +root('sylius_fixtures'); + + $this->buildSuitesNode($rootNode); + + return $treeBuilder; + } + + /** + * @param ArrayNodeDefinition $rootNode + */ + private function buildSuitesNode(ArrayNodeDefinition $rootNode): void + { + /** @var ArrayNodeDefinition $suitesNode */ + $suitesNode = $rootNode + ->children() + ->arrayNode('suites') + ->useAttributeAsKey('name') + ->arrayPrototype() + ; + + $suitesNode + ->validate() + ->ifArray() + ->then(function (array $value) { + if (!isset($value['fixtures'])) { + return $value; + } + + foreach ($value['fixtures'] as $fixtureKey => &$fixtureValue) { + if (!isset($fixtureValue['name'])) { + $fixtureValue['name'] = $fixtureKey; + } + } + + return $value; + }) + ; + + $this->buildFixturesNode($suitesNode); + $this->buildListenersNode($suitesNode); + } + + /** + * @param ArrayNodeDefinition $suitesNode + */ + private function buildFixturesNode(ArrayNodeDefinition $suitesNode): void + { + /** @var ArrayNodeDefinition $fixturesNode */ + $fixturesNode = $suitesNode + ->children() + ->arrayNode('fixtures') + ->useAttributeAsKey('alias') + ->arrayPrototype() + ; + + $fixturesNode->children()->scalarNode('name')->cannotBeEmpty(); + + $this->buildAttributesNode($fixturesNode); + } + + /** + * @param ArrayNodeDefinition $suitesNode + */ + private function buildListenersNode(ArrayNodeDefinition $suitesNode): void + { + /** @var ArrayNodeDefinition $listenersNode */ + $listenersNode = $suitesNode + ->children() + ->arrayNode('listeners') + ->useAttributeAsKey('name') + ->arrayPrototype() + ; + + $this->buildAttributesNode($listenersNode); + } + + /** + * @param ArrayNodeDefinition $node + */ + private function buildAttributesNode(ArrayNodeDefinition $node): void + { + $attributesNodeBuilder = $node->canBeUnset()->children(); + $attributesNodeBuilder->integerNode('priority')->defaultValue(0); + + /** @var ArrayNodeDefinition $optionsNode */ + $optionsNode = $attributesNodeBuilder->arrayNode('options'); + $optionsNode->addDefaultChildrenIfNoneSet(); + + $optionsNode + ->validate() + ->ifTrue(function (array $values) { + foreach ($values as $value) { + if (!is_array($value)) { + return true; + } + } + + return false; + }) + ->thenInvalid('Options have to be an array!') + ; + + $optionsNode + ->beforeNormalization() + ->always(function ($value) { + return [$value]; + }) + ; + + $optionsNode->variablePrototype()->cannotBeEmpty()->defaultValue([]); + } +} diff --git a/vendor/sylius/fixtures-bundle/DependencyInjection/SyliusFixturesExtension.php b/vendor/sylius/fixtures-bundle/DependencyInjection/SyliusFixturesExtension.php new file mode 100644 index 0000000..d010397 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/DependencyInjection/SyliusFixturesExtension.php @@ -0,0 +1,70 @@ +processConfiguration($this->getConfiguration([], $container), $config); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + + $loader->load('services.xml'); + + $this->registerSuites($config, $container); + } + + /** + * {@inheritdoc} + */ + public function prepend(ContainerBuilder $container): void + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + + $extensionsNamesToConfigurationFiles = [ + 'doctrine' => 'doctrine/orm.xml', + 'doctrine_mongodb' => 'doctrine/mongodb-odm.xml', + 'doctrine_phpcr' => 'doctrine/phpcr-odm.xml', + ]; + + foreach ($extensionsNamesToConfigurationFiles as $extensionName => $configurationFile) { + if (!$container->hasExtension($extensionName)) { + continue; + } + + $loader->load('services/integrations/' . $configurationFile); + } + } + + /** + * @param array $config + * @param ContainerBuilder $container + */ + private function registerSuites(array $config, ContainerBuilder $container): void + { + $suiteRegistry = $container->findDefinition('sylius_fixtures.suite_registry'); + foreach ($config['suites'] as $suiteName => $suiteConfiguration) { + $suiteRegistry->addMethodCall('addSuite', [$suiteName, $suiteConfiguration]); + } + } +} diff --git a/vendor/sylius/fixtures-bundle/Fixture/AbstractFixture.php b/vendor/sylius/fixtures-bundle/Fixture/AbstractFixture.php new file mode 100644 index 0000000..edf8336 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Fixture/AbstractFixture.php @@ -0,0 +1,41 @@ +root($this->getName()); + + $this->configureOptionsNode($optionsNode); + + return $treeBuilder; + } + + /** + * @param ArrayNodeDefinition $optionsNode + */ + protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void + { + // empty + } +} diff --git a/vendor/sylius/fixtures-bundle/Fixture/FixtureInterface.php b/vendor/sylius/fixtures-bundle/Fixture/FixtureInterface.php new file mode 100644 index 0000000..0d26204 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Fixture/FixtureInterface.php @@ -0,0 +1,29 @@ +fixtures, $fixture->getName(), 'Fixture with name "%s" is already registered.'); + + $this->fixtures[$fixture->getName()] = $fixture; + } + + /** + * {@inheritdoc} + */ + public function getFixture(string $name): FixtureInterface + { + if (!isset($this->fixtures[$name])) { + throw new FixtureNotFoundException($name); + } + + return $this->fixtures[$name]; + } + + /** + * {@inheritdoc} + */ + public function getFixtures(): array + { + return $this->fixtures; + } +} diff --git a/vendor/sylius/fixtures-bundle/Fixture/FixtureRegistryInterface.php b/vendor/sylius/fixtures-bundle/Fixture/FixtureRegistryInterface.php new file mode 100644 index 0000000..e8fdcc2 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Fixture/FixtureRegistryInterface.php @@ -0,0 +1,31 @@ +root($this->getName()); + + $this->configureOptionsNode($optionsNode); + + return $treeBuilder; + } + + /** + * @param ArrayNodeDefinition $optionsNode + */ + protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void + { + // empty + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/AfterFixtureListenerInterface.php b/vendor/sylius/fixtures-bundle/Listener/AfterFixtureListenerInterface.php new file mode 100644 index 0000000..8750085 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/AfterFixtureListenerInterface.php @@ -0,0 +1,23 @@ +suite = $suite; + $this->fixture = $fixture; + $this->fixtureOptions = $fixtureOptions; + } + + /** + * @return SuiteInterface + */ + public function suite(): SuiteInterface + { + return $this->suite; + } + + /** + * @return FixtureInterface + */ + public function fixture(): FixtureInterface + { + return $this->fixture; + } + + /** + * @return array + */ + public function fixtureOptions(): array + { + return $this->fixtureOptions; + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/ListenerInterface.php b/vendor/sylius/fixtures-bundle/Listener/ListenerInterface.php new file mode 100644 index 0000000..fcadc78 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/ListenerInterface.php @@ -0,0 +1,24 @@ +listeners, $listener->getName(), 'Listener with name "%s" is already registered.'); + + $this->listeners[$listener->getName()] = $listener; + } + + /** + * {@inheritdoc} + */ + public function getListener(string $name): ListenerInterface + { + if (!isset($this->listeners[$name])) { + throw new ListenerNotFoundException($name); + } + + return $this->listeners[$name]; + } + + /** + * {@inheritdoc} + */ + public function getListeners(): array + { + return $this->listeners; + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/ListenerRegistryInterface.php b/vendor/sylius/fixtures-bundle/Listener/ListenerRegistryInterface.php new file mode 100644 index 0000000..4b8dfe8 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/ListenerRegistryInterface.php @@ -0,0 +1,31 @@ +logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function beforeSuite(SuiteEvent $suiteEvent, array $options): void + { + $this->logger->notice(sprintf('Running suite "%s"...', $suiteEvent->suite()->getName())); + } + + /** + * {@inheritdoc} + */ + public function beforeFixture(FixtureEvent $fixtureEvent, array $options): void + { + $this->logger->notice(sprintf('Running fixture "%s"...', $fixtureEvent->fixture()->getName())); + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'logger'; + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/MongoDBPurgerListener.php b/vendor/sylius/fixtures-bundle/Listener/MongoDBPurgerListener.php new file mode 100644 index 0000000..4056813 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/MongoDBPurgerListener.php @@ -0,0 +1,70 @@ +managerRegistry = $managerRegistry; + } + + /** + * {@inheritdoc} + */ + public function beforeSuite(SuiteEvent $suiteEvent, array $options): void + { + foreach ($options['managers'] as $managerName) { + /** @var DocumentManager $manager */ + $manager = $this->managerRegistry->getManager($managerName); + + $purger = new MongoDBPurger($manager); + $purger->purge(); + } + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'mongodb_purger'; + } + + /** + * {@inheritdoc} + */ + protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void + { + $optionsNode + ->children() + ->arrayNode('managers') + ->defaultValue([null]) + ->scalarPrototype() + ; + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/ORMPurgerListener.php b/vendor/sylius/fixtures-bundle/Listener/ORMPurgerListener.php new file mode 100644 index 0000000..e20a072 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/ORMPurgerListener.php @@ -0,0 +1,92 @@ + ORMPurger::PURGE_MODE_DELETE, + 'truncate' => ORMPurger::PURGE_MODE_TRUNCATE, + ]; + + /** + * @param ManagerRegistry $managerRegistry + */ + public function __construct(ManagerRegistry $managerRegistry) + { + $this->managerRegistry = $managerRegistry; + } + + /** + * {@inheritdoc} + */ + public function beforeSuite(SuiteEvent $suiteEvent, array $options): void + { + foreach ($options['managers'] as $managerName) { + /** @var EntityManagerInterface $manager */ + $manager = $this->managerRegistry->getManager($managerName); + + $purger = new ORMPurger($manager, $options['exclude']); + $purger->setPurgeMode(static::$purgeModes[$options['mode']]); + $purger->purge(); + } + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'orm_purger'; + } + + /** + * {@inheritdoc} + */ + protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void + { + $optionsNodeBuilder = $optionsNode->children(); + + $optionsNodeBuilder + ->enumNode('mode') + ->values(['delete', 'truncate']) + ->defaultValue('delete') + ; + + $optionsNodeBuilder + ->arrayNode('managers') + ->defaultValue([null]) + ->scalarPrototype() + ; + + $optionsNodeBuilder + ->arrayNode('exclude') + ->defaultValue([]) + ->scalarPrototype() + ; + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/PHPCRPurgerListener.php b/vendor/sylius/fixtures-bundle/Listener/PHPCRPurgerListener.php new file mode 100644 index 0000000..3ced617 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/PHPCRPurgerListener.php @@ -0,0 +1,70 @@ +managerRegistry = $managerRegistry; + } + + /** + * {@inheritdoc} + */ + public function beforeSuite(SuiteEvent $suiteEvent, array $options): void + { + foreach ($options['managers'] as $managerName) { + /** @var DocumentManagerInterface $manager */ + $manager = $this->managerRegistry->getManager($managerName); + + $purger = new PHPCRPurger($manager); + $purger->purge(); + } + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return 'phpcr_purger'; + } + + /** + * {@inheritdoc} + */ + protected function configureOptionsNode(ArrayNodeDefinition $optionsNode): void + { + $optionsNode + ->children() + ->arrayNode('managers') + ->defaultValue([null]) + ->scalarPrototype() + ; + } +} diff --git a/vendor/sylius/fixtures-bundle/Listener/SuiteEvent.php b/vendor/sylius/fixtures-bundle/Listener/SuiteEvent.php new file mode 100644 index 0000000..2edab70 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Listener/SuiteEvent.php @@ -0,0 +1,40 @@ +suite = $suite; + } + + /** + * @return SuiteInterface + */ + public function suite(): SuiteInterface + { + return $this->suite; + } +} diff --git a/vendor/sylius/fixtures-bundle/Loader/FixtureLoader.php b/vendor/sylius/fixtures-bundle/Loader/FixtureLoader.php new file mode 100644 index 0000000..3dc2ac0 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Loader/FixtureLoader.php @@ -0,0 +1,28 @@ +load($options); + } +} diff --git a/vendor/sylius/fixtures-bundle/Loader/FixtureLoaderInterface.php b/vendor/sylius/fixtures-bundle/Loader/FixtureLoaderInterface.php new file mode 100644 index 0000000..315a0e8 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Loader/FixtureLoaderInterface.php @@ -0,0 +1,27 @@ +decoratedFixtureLoader = $decoratedFixtureLoader; + } + + /** + * {@inheritdoc} + */ + public function load(SuiteInterface $suite, FixtureInterface $fixture, array $options): void + { + $fixtureEvent = new FixtureEvent($suite, $fixture, $options); + + $this->executeBeforeFixtureListeners($suite, $fixtureEvent); + + $this->decoratedFixtureLoader->load($suite, $fixture, $options); + + $this->executeAfterFixtureListeners($suite, $fixtureEvent); + } + + /** + * @param SuiteInterface $suite + * @param FixtureEvent $fixtureEvent + */ + private function executeBeforeFixtureListeners(SuiteInterface $suite, FixtureEvent $fixtureEvent): void + { + foreach ($suite->getListeners() as $listener => $listenerOptions) { + if (!$listener instanceof BeforeFixtureListenerInterface) { + continue; + } + + $listener->beforeFixture($fixtureEvent, $listenerOptions); + } + } + + /** + * @param SuiteInterface $suite + * @param FixtureEvent $fixtureEvent + */ + private function executeAfterFixtureListeners(SuiteInterface $suite, FixtureEvent $fixtureEvent): void + { + foreach ($suite->getListeners() as $listener => $listenerOptions) { + if (!$listener instanceof AfterFixtureListenerInterface) { + continue; + } + + $listener->afterFixture($fixtureEvent, $listenerOptions); + } + } +} diff --git a/vendor/sylius/fixtures-bundle/Loader/HookableSuiteLoader.php b/vendor/sylius/fixtures-bundle/Loader/HookableSuiteLoader.php new file mode 100644 index 0000000..9169f59 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Loader/HookableSuiteLoader.php @@ -0,0 +1,79 @@ +decoratedSuiteLoader = $decoratedSuiteLoader; + } + + /** + * {@inheritdoc} + */ + public function load(SuiteInterface $suite): void + { + $suiteEvent = new SuiteEvent($suite); + + $this->executeBeforeSuiteListeners($suite, $suiteEvent); + + $this->decoratedSuiteLoader->load($suite); + + $this->executeAfterSuiteListeners($suite, $suiteEvent); + } + + /** + * @param SuiteInterface $suite + * @param SuiteEvent $suiteEvent + */ + private function executeBeforeSuiteListeners(SuiteInterface $suite, SuiteEvent $suiteEvent): void + { + foreach ($suite->getListeners() as $listener => $listenerOptions) { + if (!$listener instanceof BeforeSuiteListenerInterface) { + continue; + } + + $listener->beforeSuite($suiteEvent, $listenerOptions); + } + } + + /** + * @param SuiteInterface $suite + * @param SuiteEvent $suiteEvent + */ + private function executeAfterSuiteListeners(SuiteInterface $suite, SuiteEvent $suiteEvent): void + { + foreach ($suite->getListeners() as $listener => $listenerOptions) { + if (!$listener instanceof AfterSuiteListenerInterface) { + continue; + } + + $listener->afterSuite($suiteEvent, $listenerOptions); + } + } +} diff --git a/vendor/sylius/fixtures-bundle/Loader/SuiteLoader.php b/vendor/sylius/fixtures-bundle/Loader/SuiteLoader.php new file mode 100644 index 0000000..8be4f54 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Loader/SuiteLoader.php @@ -0,0 +1,45 @@ +fixtureLoader = $fixtureLoader; + } + + /** + * {@inheritdoc} + */ + public function load(SuiteInterface $suite): void + { + /** @var FixtureInterface $fixture */ + /** @var array $fixtureOptions */ + foreach ($suite->getFixtures() as $fixture => $fixtureOptions) { + $this->fixtureLoader->load($suite, $fixture, $fixtureOptions); + } + } +} diff --git a/vendor/sylius/fixtures-bundle/Loader/SuiteLoaderInterface.php b/vendor/sylius/fixtures-bundle/Loader/SuiteLoaderInterface.php new file mode 100644 index 0000000..3003f7c --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Loader/SuiteLoaderInterface.php @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/fixture.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/fixture.xml new file mode 100644 index 0000000..7bb8f2d --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/fixture.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/mongodb-odm.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/mongodb-odm.xml new file mode 100644 index 0000000..abd9114 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/mongodb-odm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/orm.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/orm.xml new file mode 100644 index 0000000..632aeb7 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/orm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/phpcr-odm.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/phpcr-odm.xml new file mode 100644 index 0000000..0255ad8 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/integrations/doctrine/phpcr-odm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/listener.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/listener.xml new file mode 100644 index 0000000..062c7e5 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/listener.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/loader.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/loader.xml new file mode 100644 index 0000000..ccaed60 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/loader.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/logger.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/logger.xml new file mode 100644 index 0000000..c798e19 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/logger.xml @@ -0,0 +1,44 @@ + + + + + + + + + + sylius_fixtures + + + + + + + null + true + + \Monolog\Logger::NOTICE + \Monolog\Logger::INFO + + + + + + + + + + %%message%% %%context%% %%extra%% + + + + diff --git a/vendor/sylius/fixtures-bundle/Resources/config/services/suite.xml b/vendor/sylius/fixtures-bundle/Resources/config/services/suite.xml new file mode 100644 index 0000000..5f2fb5c --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Resources/config/services/suite.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/sylius/fixtures-bundle/Suite/LazySuiteRegistry.php b/vendor/sylius/fixtures-bundle/Suite/LazySuiteRegistry.php new file mode 100644 index 0000000..e5be712 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Suite/LazySuiteRegistry.php @@ -0,0 +1,78 @@ +suiteFactory = $suiteFactory; + } + + /** + * @param string $name + * @param array $configuration + */ + public function addSuite(string $name, array $configuration): void + { + $this->suiteDefinitions[$name] = $configuration; + } + + /** + * {@inheritdoc} + */ + public function getSuite(string $name): SuiteInterface + { + if (isset($this->suites[$name])) { + return $this->suites[$name]; + } + + if (!isset($this->suiteDefinitions[$name])) { + throw new SuiteNotFoundException($name); + } + + return $this->suites[$name] = $this->suiteFactory->createSuite($name, $this->suiteDefinitions[$name]); + } + + /** + * {@inheritdoc} + */ + public function getSuites(): array + { + $suites = []; + foreach (array_keys($this->suiteDefinitions) as $name) { + $suites[$name] = $this->getSuite($name); + } + + return $suites; + } +} diff --git a/vendor/sylius/fixtures-bundle/Suite/Suite.php b/vendor/sylius/fixtures-bundle/Suite/Suite.php new file mode 100644 index 0000000..b91fc56 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Suite/Suite.php @@ -0,0 +1,96 @@ +name = $name; + $this->fixtures = new SplPriorityQueue(); + $this->listeners = new SplPriorityQueue(); + } + + /** + * @param FixtureInterface $fixture + * @param array $options + * @param int $priority + */ + public function addFixture(FixtureInterface $fixture, array $options, int $priority = 0): void + { + $this->fixtures->insert(['fixture' => $fixture, 'options' => $options], $priority); + } + + /** + * @param ListenerInterface $listener + * @param array $options + * @param int $priority + */ + public function addListener(ListenerInterface $listener, array $options, int $priority = 0): void + { + $this->listeners->insert(['listener' => $listener, 'options' => $options], $priority); + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getFixtures(): iterable + { + $fixtures = clone $this->fixtures; + foreach ($fixtures as $fixture) { + yield $fixture['fixture'] => $fixture['options']; + } + } + + /** + * {@inheritdoc} + */ + public function getListeners(): iterable + { + $listeners = clone $this->listeners; + foreach ($listeners as $listener) { + yield $listener['listener'] => $listener['options']; + } + } +} diff --git a/vendor/sylius/fixtures-bundle/Suite/SuiteFactory.php b/vendor/sylius/fixtures-bundle/Suite/SuiteFactory.php new file mode 100644 index 0000000..1498b91 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Suite/SuiteFactory.php @@ -0,0 +1,106 @@ +fixtureRegistry = $fixtureRegistry; + $this->listenerRegistry = $listenerRegistry; + $this->optionsProcessor = $optionsProcessor; + } + + /** + * {@inheritdoc} + */ + public function createSuite(string $name, array $configuration): SuiteInterface + { + Assert::keyExists($configuration, 'fixtures'); + Assert::keyExists($configuration, 'listeners'); + + $suite = new Suite($name); + + foreach ($configuration['fixtures'] as $fixtureAlias => $fixtureAttributes) { + $this->addFixtureToSuite($suite, $fixtureAlias, $fixtureAttributes); + } + + foreach ($configuration['listeners'] as $listenerName => $listenerAttributes) { + $this->addListenerToSuite($suite, $listenerName, $listenerAttributes); + } + + return $suite; + } + + /** + * @param Suite $suite + * @param string $fixtureAlias + * @param array $fixtureAttributes + */ + private function addFixtureToSuite(Suite $suite, string $fixtureAlias, array $fixtureAttributes): void + { + Assert::keyExists($fixtureAttributes, 'name'); + Assert::keyExists($fixtureAttributes, 'options'); + + $fixture = $this->fixtureRegistry->getFixture($fixtureAttributes['name']); + $fixtureOptions = $this->optionsProcessor->processConfiguration($fixture, $fixtureAttributes['options']); + $fixturePriority = $fixtureAttributes['priority'] ?? 0; + + $suite->addFixture($fixture, $fixtureOptions, $fixturePriority); + } + + /** + * @param Suite $suite + * @param string $listenerName + * @param array $listenerAttributes + */ + private function addListenerToSuite(Suite $suite, string $listenerName, array $listenerAttributes): void + { + Assert::keyExists($listenerAttributes, 'options'); + + $listener = $this->listenerRegistry->getListener($listenerName); + $listenerOptions = $this->optionsProcessor->processConfiguration($listener, $listenerAttributes['options']); + $listenerPriority = $listenerAttributes['priority'] ?? 0; + + $suite->addListener($listener, $listenerOptions, $listenerPriority); + } +} diff --git a/vendor/sylius/fixtures-bundle/Suite/SuiteFactoryInterface.php b/vendor/sylius/fixtures-bundle/Suite/SuiteFactoryInterface.php new file mode 100644 index 0000000..07774ab --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Suite/SuiteFactoryInterface.php @@ -0,0 +1,25 @@ +addCompilerPass(new FixtureRegistryPass()); + $container->addCompilerPass(new ListenerRegistryPass()); + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/FixtureRegistryPassTest.php b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/FixtureRegistryPassTest.php new file mode 100644 index 0000000..fb04ab0 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/FixtureRegistryPassTest.php @@ -0,0 +1,48 @@ +setDefinition('sylius_fixtures.fixture_registry', new Definition()); + $this->setDefinition('acme.fixture', (new Definition())->addTag('sylius_fixtures.fixture')); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'sylius_fixtures.fixture_registry', + 'addFixture', + [new Reference('acme.fixture')] + ); + } + + /** + * {@inheritdoc} + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new FixtureRegistryPass()); + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/ListenerRegistryPassTest.php b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/ListenerRegistryPassTest.php new file mode 100644 index 0000000..6db7183 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/Compiler/ListenerRegistryPassTest.php @@ -0,0 +1,48 @@ +setDefinition('sylius_fixtures.listener_registry', new Definition()); + $this->setDefinition('acme.listener', (new Definition())->addTag('sylius_fixtures.listener')); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'sylius_fixtures.listener_registry', + 'addListener', + [new Reference('acme.listener')] + ); + } + + /** + * {@inheritdoc} + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new ListenerRegistryPass()); + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/ConfigurationTest.php b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..f87559c --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,338 @@ +assertConfigurationIsValid( + [['suites' => ['suite' => ['fixtures' => ['fixture' => null]]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function multiple_suites_are_allowed(): void + { + $this->assertConfigurationIsValid( + [['suites' => [ + 'first_suite' => ['fixtures' => ['fixture' => null]], + 'second_suite' => ['fixtures' => ['fixture' => null]], + ]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function consecutive_configurations_can_add_suites(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => [ + 'first_suite' => ['fixtures' => ['fixture' => null]], + ]], + ['suites' => [ + 'second_suite' => ['fixtures' => ['fixture' => null]], + ]], + ], + ['suites' => [ + 'first_suite' => ['fixtures' => ['fixture' => ['name' => 'fixture', 'options' => [[]], 'priority' => 0]]], + 'second_suite' => ['fixtures' => ['fixture' => ['name' => 'fixture', 'options' => [[]], 'priority' => 0]]], + ]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function suite_can_have_multiple_fixtures(): void + { + $this->assertConfigurationIsValid( + [['suites' => ['suite' => ['fixtures' => [ + 'first_fixture' => null, + 'second_fixture' => null, + ]]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function consecutive_configurations_can_remove_a_fixture_from_the_suite(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['fixtures' => [ + 'first_fixture' => null, + 'second_fixture' => null, + ]]]], + ['suites' => ['suite' => ['fixtures' => [ + 'second_fixture' => false, + ]]]], + ], + ['suites' => ['suite' => ['fixtures' => [ + 'first_fixture' => ['name' => 'first_fixture', 'options' => [[]], 'priority' => 0], + ]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function consecutive_configurations_can_add_fixtures_to_the_suite(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['fixtures' => [ + 'first_fixture' => null, + ]]]], + ['suites' => ['suite' => ['fixtures' => [ + 'second_fixture' => null, + ]]]], + ], + ['suites' => ['suite' => ['fixtures' => [ + 'first_fixture' => ['name' => 'first_fixture', 'options' => [[]], 'priority' => 0], + 'second_fixture' => ['name' => 'second_fixture', 'options' => [[]], 'priority' => 0], + ]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function all_fixture_options_from_consecutive_configurations_are_collected(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['fixtures' => [ + 'fixture' => ['options' => ['option' => 4]], + ]]]], + ['suites' => ['suite' => ['fixtures' => [ + 'fixture' => ['options' => ['option' => 2]], + ]]]], + ], + ['suites' => ['suite' => ['fixtures' => [ + 'fixture' => ['name' => 'fixture', 'options' => [['option' => 4], ['option' => 2]], 'priority' => 0], + ]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function fixture_options_are_not_replaced_implicitly(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['fixtures' => [ + 'fixture' => ['options' => ['option' => 4]], + ]]]], + ['suites' => ['suite' => ['fixtures' => [ + 'fixture' => null, + ]]]], + ], + ['suites' => ['suite' => ['fixtures' => [ + 'fixture' => ['name' => 'fixture', 'options' => [['option' => 4]], 'priority' => 0], + ]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function fixtures_options_are_an_array(): void + { + $this->assertPartialConfigurationIsInvalid( + [['suites' => ['suite' => ['fixtures' => ['fixture' => [ + 'options' => 42, + ]]]]]], + 'suites.*.fixtures' + ); + + $this->assertPartialConfigurationIsInvalid( + [['suites' => ['suite' => ['fixtures' => ['fixture' => [ + 'options' => 'string string', + ]]]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function fixtures_options_may_contain_nested_arrays(): void + { + $this->assertProcessedConfigurationEquals( + [['suites' => ['suite' => ['fixtures' => ['fixture' => [ + 'options' => ['nested' => ['key' => 'value']], + ]]]]]], + ['suites' => ['suite' => ['fixtures' => ['fixture' => [ + 'options' => [['nested' => ['key' => 'value']]], + 'name' => 'fixture', // FIXME: something is wrong inside the test library and it's not excluded + ]]]]], + 'suites.*.fixtures.*.options' + ); + } + + /** + * @test + */ + public function listeners_options_are_an_array(): void + { + $this->assertPartialConfigurationIsInvalid( + [['suites' => ['suite' => ['listeners' => ['listener' => [ + 'options' => 42, + ]]]]]], + 'suites.*.listeners' + ); + + $this->assertPartialConfigurationIsInvalid( + [['suites' => ['suite' => ['listeners' => ['listener' => [ + 'options' => 'string string', + ]]]]]], + 'suites.*.listeners' + ); + } + + /** + * @test + */ + public function listeners_options_may_contain_nested_arrays(): void + { + $this->assertProcessedConfigurationEquals( + [['suites' => ['suite' => ['listeners' => ['listener' => [ + 'options' => ['nested' => ['key' => 'value']], + ]]]]]], + ['suites' => ['suite' => ['listeners' => ['listener' => [ + 'options' => [['nested' => ['key' => 'value']]], + ]]]]], + 'suites.*.listeners.*.options' + ); + } + + /** + * @test + */ + public function consecutive_configurations_can_remove_a_listener_from_the_suite(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['listeners' => [ + 'first_listener' => null, + 'second_listener' => null, + ]]]], + ['suites' => ['suite' => ['listeners' => [ + 'second_listener' => false, + ]]]], + ], + ['suites' => ['suite' => ['listeners' => [ + 'first_listener' => ['options' => [[]], 'priority' => 0], + ]]]], + 'suites.*.listeners' + ); + } + + /** + * @test + */ + public function consecutive_configurations_can_add_a_listener_to_the_suite(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['listeners' => [ + 'first_listener' => null, + ]]]], + ['suites' => ['suite' => ['listeners' => [ + 'second_listener' => null, + ]]]], + ], + ['suites' => ['suite' => ['listeners' => [ + 'first_listener' => ['options' => [[]], 'priority' => 0], + 'second_listener' => ['options' => [[]], 'priority' => 0], + ]]]], + 'suites.*.listeners' + ); + } + + /** + * @test + */ + public function fixtures_can_be_aliased_with_different_names(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['fixtures' => [ + 'admin_user' => ['name' => 'user', 'options' => ['admin' => true]], + 'regular_user' => ['name' => 'user', 'options' => ['admin' => false]], + ]]]], + ], + ['suites' => ['suite' => ['fixtures' => [ + 'admin_user' => ['name' => 'user', 'options' => [['admin' => true]], 'priority' => 0], + 'regular_user' => ['name' => 'user', 'options' => [['admin' => false]], 'priority' => 0], + ]]]], + 'suites.*.fixtures' + ); + } + + /** + * @test + */ + public function consecutive_configurations_can_add_aliased_fixtures_to_the_suite(): void + { + $this->assertProcessedConfigurationEquals( + [ + ['suites' => ['suite' => ['fixtures' => [ + 'admin_user' => ['name' => 'user', 'options' => ['admin' => true]], + ]]]], + ['suites' => ['suite' => ['fixtures' => [ + 'regular_user' => ['name' => 'user', 'options' => ['admin' => false]], + ]]]], + ], + ['suites' => ['suite' => ['fixtures' => [ + 'admin_user' => ['name' => 'user', 'options' => [['admin' => true]], 'priority' => 0], + 'regular_user' => ['name' => 'user', 'options' => [['admin' => false]], 'priority' => 0], + ]]]], + 'suites.*.fixtures' + ); + } + + /** + * {@inheritdoc} + */ + protected function getConfiguration(): ConfigurationInterface + { + return new Configuration(); + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/SyliusFixturesExtensionTest.php b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/SyliusFixturesExtensionTest.php new file mode 100644 index 0000000..0e18d8d --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/DependencyInjection/SyliusFixturesExtensionTest.php @@ -0,0 +1,52 @@ +load(); + } + + /** + * @test + */ + public function it_registers_configured_suites(): void + { + $this->load(['suites' => [ + 'suite_name' => [], + ]]); + + $suiteRegistryDefinition = $this->container->findDefinition('sylius_fixtures.suite_registry'); + $suiteMethodCall = $suiteRegistryDefinition->getMethodCalls()[0]; + + static::assertSame('addSuite', $suiteMethodCall[0]); + static::assertSame('suite_name', $suiteMethodCall[1][0]); + } + + /** + * {@inheritdoc} + */ + protected function getContainerExtensions(): array + { + return [new SyliusFixturesExtension()]; + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/Listener/MongoDBPurgerListenerTest.php b/vendor/sylius/fixtures-bundle/Tests/Listener/MongoDBPurgerListenerTest.php new file mode 100644 index 0000000..672e33d --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/Listener/MongoDBPurgerListenerTest.php @@ -0,0 +1,49 @@ +assertProcessedConfigurationEquals([[]], ['managers' => [null]], 'managers'); + } + + /** + * @test + */ + public function managers_are_optional(): void + { + $this->assertProcessedConfigurationEquals([['managers' => ['custom']]], ['managers' => ['custom']], 'managers'); + } + + /** + * {@inheritdoc} + */ + protected function getConfiguration(): ConfigurationInterface + { + return new MongoDBPurgerListener($this->getMockBuilder(ManagerRegistry::class)->getMock()); + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/Listener/ORMPurgerListenerTest.php b/vendor/sylius/fixtures-bundle/Tests/Listener/ORMPurgerListenerTest.php new file mode 100644 index 0000000..3e4c959 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/Listener/ORMPurgerListenerTest.php @@ -0,0 +1,73 @@ +assertProcessedConfigurationEquals([[]], ['mode' => 'delete'], 'mode'); + } + + /** + * @test + */ + public function purge_mode_can_be_changed_to_truncate(): void + { + $this->assertProcessedConfigurationEquals([['mode' => 'truncate']], ['mode' => 'truncate'], 'mode'); + } + + /** + * @test + */ + public function purge_mode_can_be_either_delete_or_truncate(): void + { + $this->assertPartialConfigurationIsInvalid([['mode' => 'lol']], 'mode'); + } + + /** + * @test + */ + public function managers_are_set_to_null_by_default(): void + { + $this->assertProcessedConfigurationEquals([[]], ['managers' => [null]], 'managers'); + } + + /** + * @test + */ + public function managers_are_optional(): void + { + $this->assertProcessedConfigurationEquals([['managers' => ['custom']]], ['managers' => ['custom']], 'managers'); + } + + /** + * {@inheritdoc} + */ + protected function getConfiguration(): ConfigurationInterface + { + return new ORMPurgerListener($this->getMockBuilder(ManagerRegistry::class)->getMock()); + } +} diff --git a/vendor/sylius/fixtures-bundle/Tests/Listener/PHPCRPurgerListenerTest.php b/vendor/sylius/fixtures-bundle/Tests/Listener/PHPCRPurgerListenerTest.php new file mode 100644 index 0000000..3e91cf6 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/Tests/Listener/PHPCRPurgerListenerTest.php @@ -0,0 +1,49 @@ +assertProcessedConfigurationEquals([[]], ['managers' => [null]], 'managers'); + } + + /** + * @test + */ + public function managers_are_optional(): void + { + $this->assertProcessedConfigurationEquals([['managers' => ['custom']]], ['managers' => ['custom']], 'managers'); + } + + /** + * {@inheritdoc} + */ + protected function getConfiguration(): ConfigurationInterface + { + return new PHPCRPurgerListener($this->getMockBuilder(ManagerRegistry::class)->getMock()); + } +} diff --git a/vendor/sylius/fixtures-bundle/composer.json b/vendor/sylius/fixtures-bundle/composer.json new file mode 100644 index 0000000..5965a38 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/composer.json @@ -0,0 +1,73 @@ +{ + "name": "sylius/fixtures-bundle", + "type": "symfony-bundle", + "description": "Configurable fixtures for Symfony applications.", + "keywords": ["sylius", "fixtures", "symfony"], + "homepage": "http://sylius.com", + "license": "MIT", + "authors": [ + { + "name": "Kamil Kokot", + "homepage": "http://kamil.kokot.me" + }, + { + "name": "Sylius project", + "homepage": "http://sylius.com" + }, + { + "name": "Community contributions", + "homepage": "http://github.com/Sylius/Sylius/contributors" + } + ], + "require": { + "php": "^7.2", + + "doctrine/data-fixtures": "^1.2", + "monolog/monolog": "^1.8", + "symfony/framework-bundle": "^3.4|^4.1.1", + "symfony/monolog-bridge": "^3.4|^4.1.1", + "webmozart/assert": "^1.0", + "zendframework/zend-stdlib": "^3.0" + }, + "require-dev": { + "doctrine/orm": "^2.5", + "doctrine/doctrine-bundle": "^1.3", + "matthiasnoback/symfony-config-test": "^3.0", + "matthiasnoback/symfony-dependency-injection-test": "^2.0", + "phpspec/phpspec": "^4.0", + "phpunit/phpunit": "^6.5", + "twig/twig": "^2.0", + "symfony/browser-kit": "^3.4|^4.1.1", + "symfony/dependency-injection": "^3.4|^4.1.1", + "symfony/templating": "^3.4|^4.1.1", + "symfony/translation": "^3.4|^4.1.1", + "symfony/twig-bundle": "^3.4|^4.1.1", + "symfony/security-csrf": "^3.4|^4.1.1", + "polishsymfonycommunity/symfony-mocker-container": "^1.0" + }, + "config": { + "bin-dir": "bin" + }, + "autoload": { + "psr-4": { "Sylius\\Bundle\\FixturesBundle\\": "" } + }, + "autoload-dev": { + "psr-4": { + "Sylius\\Bundle\\FixturesBundle\\spec\\": "spec/" + }, + "classmap": ["test/app/AppKernel.php"] + }, + "minimum-stability": "dev", + "prefer-stable": true, + "repositories": [ + { + "type": "path", + "url": "../../*/*" + } + ], + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + } +} diff --git a/vendor/sylius/fixtures-bundle/phpspec.yml.dist b/vendor/sylius/fixtures-bundle/phpspec.yml.dist new file mode 100644 index 0000000..8b324c7 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/phpspec.yml.dist @@ -0,0 +1,5 @@ +suites: + main: + namespace: Sylius\Bundle\FixturesBundle + psr4_prefix: Sylius\Bundle\FixturesBundle + src_path: . diff --git a/vendor/sylius/fixtures-bundle/phpunit.xml.dist b/vendor/sylius/fixtures-bundle/phpunit.xml.dist new file mode 100644 index 0000000..809783f --- /dev/null +++ b/vendor/sylius/fixtures-bundle/phpunit.xml.dist @@ -0,0 +1,17 @@ + + + + + + + + + + ./test/ + ./Tests/ + + + diff --git a/vendor/sylius/fixtures-bundle/spec/Fixture/FixtureNotFoundExceptionSpec.php b/vendor/sylius/fixtures-bundle/spec/Fixture/FixtureNotFoundExceptionSpec.php new file mode 100644 index 0000000..6fad4e0 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Fixture/FixtureNotFoundExceptionSpec.php @@ -0,0 +1,34 @@ +beConstructedWith('fixture_name'); + } + + function it_is_an_invalid_argument_exception(): void + { + $this->shouldHaveType(\InvalidArgumentException::class); + } + + function it_has_preformatted_message(): void + { + $this->getMessage()->shouldReturn('Fixture with name "fixture_name" could not be found!'); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Fixture/FixtureRegistrySpec.php b/vendor/sylius/fixtures-bundle/spec/Fixture/FixtureRegistrySpec.php new file mode 100644 index 0000000..090e539 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Fixture/FixtureRegistrySpec.php @@ -0,0 +1,59 @@ +shouldImplement(FixtureRegistryInterface::class); + } + + function it_has_a_fixtures(FixtureInterface $fixture): void + { + $fixture->getName()->willReturn('fixture'); + + $this->addFixture($fixture); + + $this->getFixture('fixture')->shouldReturn($fixture); + $this->getFixtures()->shouldReturn(['fixture' => $fixture]); + } + + function it_throws_an_exception_if_trying_to_another_fixture_with_the_same_name( + FixtureInterface $fixture, + FixtureInterface $anotherFixture + ): void { + $fixture->getName()->willReturn('fixture'); + $anotherFixture->getName()->willReturn('fixture'); + + $this->addFixture($fixture); + $this->shouldThrow(\InvalidArgumentException::class)->during('addFixture', [$fixture]); + $this->shouldThrow(\InvalidArgumentException::class)->during('addFixture', [$anotherFixture]); + } + + function it_returns_an_empty_fixtures_list_if_it_does_not_have_any_fixtures(): void + { + $this->getFixtures()->shouldReturn([]); + } + + function it_throws_an_exception_if_trying_to_get_unexisting_fixture_by_name(): void + { + $this->shouldThrow(FixtureNotFoundException::class)->during('getFixture', ['fixture']); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Listener/ListenerNotFoundExceptionSpec.php b/vendor/sylius/fixtures-bundle/spec/Listener/ListenerNotFoundExceptionSpec.php new file mode 100644 index 0000000..db97b5f --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Listener/ListenerNotFoundExceptionSpec.php @@ -0,0 +1,34 @@ +beConstructedWith('listener_name'); + } + + function it_is_an_invalid_argument_exception(): void + { + $this->shouldHaveType(\InvalidArgumentException::class); + } + + function it_has_preformatted_message(): void + { + $this->getMessage()->shouldReturn('Listener with name "listener_name" could not be found!'); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Listener/ListenerRegistrySpec.php b/vendor/sylius/fixtures-bundle/spec/Listener/ListenerRegistrySpec.php new file mode 100644 index 0000000..becfbb3 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Listener/ListenerRegistrySpec.php @@ -0,0 +1,59 @@ +shouldImplement(ListenerRegistryInterface::class); + } + + function it_has_a_listener(ListenerInterface $listener): void + { + $listener->getName()->willReturn('listener_name'); + + $this->addListener($listener); + + $this->getListener('listener_name')->shouldReturn($listener); + $this->getListeners()->shouldReturn(['listener_name' => $listener]); + } + + function it_throws_an_exception_if_trying_to_another_listener_with_the_same_name( + ListenerInterface $listener, + ListenerInterface $anotherListener + ): void { + $listener->getName()->willReturn('listener_name'); + $anotherListener->getName()->willReturn('listener_name'); + + $this->addListener($listener); + $this->shouldThrow(\InvalidArgumentException::class)->during('addListener', [$listener]); + $this->shouldThrow(\InvalidArgumentException::class)->during('addListener', [$anotherListener]); + } + + function it_returns_an_empty_listeners_list_if_it_does_not_have_any_listeners(): void + { + $this->getListeners()->shouldReturn([]); + } + + function it_throws_an_exception_if_trying_to_get_unexisting_listener_by_name(): void + { + $this->shouldThrow(ListenerNotFoundException::class)->during('getListener', ['listener_name']); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Listener/LoggerListenerSpec.php b/vendor/sylius/fixtures-bundle/spec/Listener/LoggerListenerSpec.php new file mode 100644 index 0000000..9892217 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Listener/LoggerListenerSpec.php @@ -0,0 +1,70 @@ +beConstructedWith($logger); + } + + function it_implements_listener_interface(): void + { + $this->shouldImplement(ListenerInterface::class); + } + + function it_listens_for_before_suite_events(): void + { + $this->shouldImplement(BeforeSuiteListenerInterface::class); + } + + function it_listens_for_before_fixture_events(): void + { + $this->shouldImplement(BeforeFixtureListenerInterface::class); + } + + function it_logs_suite_name_on_before_suite_event(LoggerInterface $logger, SuiteInterface $suite): void + { + $suite->getName()->willReturn('uber_suite'); + + $logger->notice(Argument::that(function ($argument) use ($suite) { + return false !== strpos($argument, $suite->getWrappedObject()->getName()); + }))->shouldBeCalled(); + + $this->beforeSuite(new SuiteEvent($suite->getWrappedObject()), []); + } + + function it_logs_fixture_name_on_before_fixture_event(LoggerInterface $logger, SuiteInterface $suite, FixtureInterface $fixture): void + { + $fixture->getName()->willReturn('uber_fixture'); + + $logger->notice(Argument::that(function ($argument) use ($fixture) { + return false !== strpos($argument, $fixture->getWrappedObject()->getName()); + }))->shouldBeCalled(); + + $this->beforeFixture(new FixtureEvent($suite->getWrappedObject(), $fixture->getWrappedObject(), []), []); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Loader/FixtureLoaderSpec.php b/vendor/sylius/fixtures-bundle/spec/Loader/FixtureLoaderSpec.php new file mode 100644 index 0000000..dbbcf1a --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Loader/FixtureLoaderSpec.php @@ -0,0 +1,34 @@ +shouldImplement(FixtureLoaderInterface::class); + } + + function it_loads_a_fixture(SuiteInterface $suite, FixtureInterface $fixture): void + { + $fixture->load(['options'])->shouldBeCalled(); + + $this->load($suite, $fixture, ['options']); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Loader/HookableFixtureLoaderSpec.php b/vendor/sylius/fixtures-bundle/spec/Loader/HookableFixtureLoaderSpec.php new file mode 100644 index 0000000..cbcade2 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Loader/HookableFixtureLoaderSpec.php @@ -0,0 +1,104 @@ +beConstructedWith($decoratedFixtureLoader); + } + + function it_implements_fixture_loader_interface(): void + { + $this->shouldImplement(FixtureLoaderInterface::class); + } + + function it_delegates_fixture_loading_to_the_base_loader( + FixtureLoaderInterface $decoratedFixtureLoader, + SuiteInterface $suite, + FixtureInterface $fixture + ): void { + $suite->getListeners()->willReturn([]); + + $decoratedFixtureLoader->load($suite, $fixture, ['fixture_option' => 'fixture_value'])->shouldBeCalled(); + + $this->load($suite, $fixture, ['fixture_option' => 'fixture_value']); + } + + function it_executes_before_fixture_listeners( + FixtureLoaderInterface $decoratedFixtureLoader, + SuiteInterface $suite, + FixtureInterface $fixture, + BeforeFixtureListenerInterface $beforeFixtureListener + ): void { + $suite->getListeners()->will(function () use ($beforeFixtureListener) { + yield $beforeFixtureListener->getWrappedObject() => []; + }); + + $beforeFixtureListener->beforeFixture(new FixtureEvent($suite->getWrappedObject(), $fixture->getWrappedObject(), ['fixture_option' => 'fixture_value']), [])->shouldBeCalledTimes(1); + + $decoratedFixtureLoader->load($suite, $fixture, ['fixture_option' => 'fixture_value'])->shouldBeCalled(); + + $this->load($suite, $fixture, ['fixture_option' => 'fixture_value']); + } + + function it_executes_after_fixture_listeners( + FixtureLoaderInterface $decoratedFixtureLoader, + SuiteInterface $suite, + FixtureInterface $fixture, + AfterFixtureListenerInterface $afterFixtureListener + ): void { + $suite->getListeners()->will(function () use ($afterFixtureListener) { + yield $afterFixtureListener->getWrappedObject() => []; + }); + + $decoratedFixtureLoader->load($suite, $fixture, ['fixture_option' => 'fixture_value'])->shouldBeCalled(); + + $afterFixtureListener->afterFixture(new FixtureEvent($suite->getWrappedObject(), $fixture->getWrappedObject(), ['fixture_option' => 'fixture_value']), [])->shouldBeCalledTimes(1); + + $this->load($suite, $fixture, ['fixture_option' => 'fixture_value']); + } + + function it_executes_customized_fixture_listeners( + FixtureLoaderInterface $decoratedFixtureLoader, + SuiteInterface $suite, + FixtureInterface $fixture, + BeforeFixtureListenerInterface $beforeFixtureListener, + AfterFixtureListenerInterface $afterFixtureListener + ): void { + $suite->getListeners()->will(function () use ($beforeFixtureListener, $afterFixtureListener) { + yield $beforeFixtureListener->getWrappedObject() => ['listener_option1' => 'listener_value1']; + yield $afterFixtureListener->getWrappedObject() => ['listener_option2' => 'listener_value2']; + }); + + $fixtureEvent = new FixtureEvent($suite->getWrappedObject(), $fixture->getWrappedObject(), ['fixture_option' => 'fixture_value']); + + $beforeFixtureListener->beforeFixture($fixtureEvent, ['listener_option1' => 'listener_value1'])->shouldBeCalledTimes(1); + + $decoratedFixtureLoader->load($suite, $fixture, ['fixture_option' => 'fixture_value'])->shouldBeCalled(); + + $afterFixtureListener->afterFixture($fixtureEvent, ['listener_option2' => 'listener_value2'])->shouldBeCalledTimes(1); + + $this->load($suite, $fixture, ['fixture_option' => 'fixture_value']); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Loader/HookableSuiteLoaderSpec.php b/vendor/sylius/fixtures-bundle/spec/Loader/HookableSuiteLoaderSpec.php new file mode 100644 index 0000000..a5daf11 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Loader/HookableSuiteLoaderSpec.php @@ -0,0 +1,95 @@ +beConstructedWith($decoratedSuiteLoader); + } + + function it_implements_suite_loader_interface(): void + { + $this->shouldImplement(SuiteLoaderInterface::class); + } + + function it_delegates_suite_loading_to_the_base_loader(SuiteLoaderInterface $decoratedSuiteLoader, SuiteInterface $suite): void + { + $suite->getListeners()->willReturn([]); + + $decoratedSuiteLoader->load($suite)->shouldBeCalled(); + + $this->load($suite); + } + + function it_executes_before_suite_listeners( + SuiteLoaderInterface $decoratedSuiteLoader, + SuiteInterface $suite, + BeforeSuiteListenerInterface $beforeSuiteListener + ): void { + $suite->getListeners()->will(function () use ($beforeSuiteListener) { + yield $beforeSuiteListener->getWrappedObject() => []; + }); + + $beforeSuiteListener->beforeSuite(new SuiteEvent($suite->getWrappedObject()), [])->shouldBeCalledTimes(1); + + $decoratedSuiteLoader->load($suite)->shouldBeCalled(); + + $this->load($suite); + } + + function it_executes_after_suite_listeners( + SuiteLoaderInterface $decoratedSuiteLoader, + SuiteInterface $suite, + AfterSuiteListenerInterface $afterSuiteListener + ): void { + $suite->getListeners()->will(function () use ($afterSuiteListener) { + yield $afterSuiteListener->getWrappedObject() => []; + }); + + $decoratedSuiteLoader->load($suite)->shouldBeCalled(); + + $afterSuiteListener->afterSuite(new SuiteEvent($suite->getWrappedObject()), [])->shouldBeCalledTimes(1); + + $this->load($suite); + } + + function it_executes_customized_suite_listeners( + SuiteLoaderInterface $decoratedSuiteLoader, + SuiteInterface $suite, + BeforeSuiteListenerInterface $beforeSuiteListener, + AfterSuiteListenerInterface $afterSuiteListener + ): void { + $suite->getListeners()->will(function () use ($beforeSuiteListener, $afterSuiteListener) { + yield $beforeSuiteListener->getWrappedObject() => ['listener_option1' => 'listener_value1']; + yield $afterSuiteListener->getWrappedObject() => ['listener_option2' => 'listener_value2']; + }); + + $beforeSuiteListener->beforeSuite(new SuiteEvent($suite->getWrappedObject()), ['listener_option1' => 'listener_value1'])->shouldBeCalledTimes(1); + + $decoratedSuiteLoader->load($suite)->shouldBeCalled(); + + $afterSuiteListener->afterSuite(new SuiteEvent($suite->getWrappedObject()), ['listener_option2' => 'listener_value2'])->shouldBeCalledTimes(1); + + $this->load($suite); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Loader/SuiteLoaderSpec.php b/vendor/sylius/fixtures-bundle/spec/Loader/SuiteLoaderSpec.php new file mode 100644 index 0000000..97f27ed --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Loader/SuiteLoaderSpec.php @@ -0,0 +1,50 @@ +beConstructedWith($fixtureLoader); + } + + function it_implements_suite_loader_interface(): void + { + $this->shouldImplement(SuiteLoaderInterface::class); + } + + function it_loads_suite_fixtures( + FixtureLoaderInterface $fixtureLoader, + SuiteInterface $suite, + FixtureInterface $firstFixture, + FixtureInterface $secondFixture + ): void { + $suite->getFixtures()->will(function () use ($firstFixture, $secondFixture) { + yield $firstFixture->getWrappedObject() => ['options 1']; + yield $secondFixture->getWrappedObject() => ['options 2']; + }); + + $fixtureLoader->load($suite, $firstFixture, ['options 1'])->shouldBeCalled(); + $fixtureLoader->load($suite, $secondFixture, ['options 2'])->shouldBeCalled(); + + $this->load($suite); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Suite/LazySuiteRegistrySpec.php b/vendor/sylius/fixtures-bundle/spec/Suite/LazySuiteRegistrySpec.php new file mode 100644 index 0000000..9932d96 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Suite/LazySuiteRegistrySpec.php @@ -0,0 +1,63 @@ +beConstructedWith($suiteFactory); + } + + function it_implements_suite_registry_interface(): void + { + $this->shouldImplement(SuiteRegistryInterface::class); + } + + function it_returns_a_constructed_suite(SuiteFactoryInterface $suiteFactory, SuiteInterface $suite): void + { + $this->addSuite('suite_name', ['fixtures' => []]); + + $suiteFactory->createSuite('suite_name', ['fixtures' => []])->willReturn($suite); + + $this->getSuite('suite_name')->shouldReturn($suite); + $this->getSuites()->shouldReturn(['suite_name' => $suite]); + } + + function it_constructs_a_suite_only_once(SuiteFactoryInterface $suiteFactory, SuiteInterface $suite): void + { + $this->addSuite('suite_name', ['fixtures' => []]); + + $suiteFactory->createSuite('suite_name', ['fixtures' => []])->shouldBeCalledTimes(1)->willReturn($suite); + + $this->getSuite('suite_name')->shouldReturn($suite); + $this->getSuite('suite_name')->shouldReturn($suite); + } + + function it_returns_an_empty_suites_list_if_none_was_registered(): void + { + $this->getSuites()->shouldReturn([]); + } + + function it_throws_an_exception_if_trying_to_get_unexisting_suite(): void + { + $this->shouldThrow(SuiteNotFoundException::class)->during('getSuite', ['the_river_snake_is_dangerous']); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Suite/SuiteFactorySpec.php b/vendor/sylius/fixtures-bundle/spec/Suite/SuiteFactorySpec.php new file mode 100644 index 0000000..b427e8e --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Suite/SuiteFactorySpec.php @@ -0,0 +1,223 @@ +beConstructedWith($fixtureRegistry, $listenerRegistry, $optionsProcessor); + } + + function it_implements_suite_factory_interface(): void + { + $this->shouldImplement(SuiteFactoryInterface::class); + } + + function it_creates_a_new_empty_suite(): void + { + $suite = $this->createSuite('suite_name', ['listeners' => [], 'fixtures' => []]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getFixtures()->shouldIterateAs([]); + } + + function it_creates_a_new_suite_with_fixtures( + FixtureRegistryInterface $fixtureRegistry, + Processor $optionsProcessor, + FixtureInterface $firstFixture, + FixtureInterface $secondFixture + ): void { + $fixtureRegistry->getFixture('first_fixture')->willReturn($firstFixture); + $fixtureRegistry->getFixture('second_fixture')->willReturn($secondFixture); + + $optionsProcessor->processConfiguration($firstFixture, [[]])->willReturn([]); + $optionsProcessor->processConfiguration($secondFixture, [[]])->willReturn([]); + + $suite = $this->createSuite('suite_name', ['listeners' => [], 'fixtures' => [ + 'first_fixture' => ['name' => 'first_fixture', 'options' => [[]]], + 'second_fixture' => ['name' => 'second_fixture', 'options' => [[]]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getFixtures()->shouldIterateAs($this->createGenerator($firstFixture, $secondFixture)); + } + + function it_creates_a_new_suite_with_fixtures_based_on_its_name_rather_than_alias( + FixtureRegistryInterface $fixtureRegistry, + Processor $optionsProcessor, + FixtureInterface $fixture + ): void { + $fixtureRegistry->getFixture('fixture_name')->shouldBeCalled()->willReturn($fixture); + $fixtureRegistry->getFixture('fixture_alias')->shouldNotBeCalled(); + + $optionsProcessor->processConfiguration($fixture, [[]])->willReturn([]); + + $suite = $this->createSuite('suite_name', ['listeners' => [], 'fixtures' => [ + 'fixture_alias' => ['name' => 'fixture_name', 'options' => [[]]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getFixtures()->shouldIterateAs($this->createGenerator($fixture)); + } + + function it_creates_a_new_suite_with_prioritized_fixtures( + FixtureRegistryInterface $fixtureRegistry, + Processor $optionsProcessor, + FixtureInterface $fixture, + FixtureInterface $higherPriorityFixture + ): void { + $fixtureRegistry->getFixture('fixture')->willReturn($fixture); + $fixtureRegistry->getFixture('higher_priority_fixture')->willReturn($higherPriorityFixture); + + $optionsProcessor->processConfiguration($fixture, [[]])->willReturn([]); + $optionsProcessor->processConfiguration($higherPriorityFixture, [[]])->willReturn([]); + + $suite = $this->createSuite('suite_name', ['listeners' => [], 'fixtures' => [ + 'fixture' => ['name' => 'fixture', 'priority' => 5, 'options' => [[]]], + 'higher_priority_fixture' => ['name' => 'higher_priority_fixture', 'priority' => 10, 'options' => [[]]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getFixtures()->shouldIterateAs($this->createGenerator($higherPriorityFixture, $fixture)); + } + + function it_creates_a_new_suite_with_customized_fixture( + FixtureRegistryInterface $fixtureRegistry, + Processor $optionsProcessor, + FixtureInterface $fixture + ): void { + $fixtureRegistry->getFixture('fixture')->willReturn($fixture); + + $optionsProcessor->processConfiguration($fixture, [['fixture_option' => 'fixture_value']])->willReturn(['fixture_option' => 'fixture_value']); + + $suite = $this->createSuite('suite_name', ['listeners' => [], 'fixtures' => [ + 'fixture' => ['name' => 'fixture', 'options' => [['fixture_option' => 'fixture_value']]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getFixtures()->shouldHaveKeyWithValue($fixture, ['fixture_option' => 'fixture_value']); + } + + function it_creates_a_new_suite_with_listeners( + ListenerRegistryInterface $listenerRegistry, + Processor $optionsProcessor, + ListenerInterface $firstListener, + ListenerInterface $secondListener + ): void { + $listenerRegistry->getListener('first_listener')->willReturn($firstListener); + $listenerRegistry->getListener('second_listener')->willReturn($secondListener); + + $optionsProcessor->processConfiguration($firstListener, [[]])->willReturn([]); + $optionsProcessor->processConfiguration($secondListener, [[]])->willReturn([]); + + $suite = $this->createSuite('suite_name', ['fixtures' => [], 'listeners' => [ + 'first_listener' => ['options' => [[]]], + 'second_listener' => ['options' => [[]]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getListeners()->shouldIterateAs($this->createGenerator($firstListener, $secondListener)); + } + + function it_creates_a_new_suite_with_prioritized_listeners( + ListenerRegistryInterface $listenerRegistry, + Processor $optionsProcessor, + ListenerInterface $listener, + ListenerInterface $higherPriorityListener + ): void { + $listenerRegistry->getListener('listener')->willReturn($listener); + $listenerRegistry->getListener('higher_priority_listener')->willReturn($higherPriorityListener); + + $optionsProcessor->processConfiguration($listener, [[]])->willReturn([]); + $optionsProcessor->processConfiguration($higherPriorityListener, [[]])->willReturn([]); + + $suite = $this->createSuite('suite_name', ['fixtures' => [], 'listeners' => [ + 'listener' => ['priority' => 5, 'options' => [[]]], + 'higher_priority_listener' => ['priority' => 10, 'options' => [[]]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getListeners()->shouldIterateAs($this->createGenerator($higherPriorityListener, $listener)); + } + + function it_creates_a_new_suite_with_customized_listener( + ListenerRegistryInterface $listenerRegistry, + Processor $optionsProcessor, + ListenerInterface $listener + ): void { + $listenerRegistry->getListener('listener')->willReturn($listener); + + $optionsProcessor->processConfiguration($listener, [['listener_option' => 'listener_value']])->willReturn(['listener_option' => 'listener_value']); + + $suite = $this->createSuite('suite_name', ['fixtures' => [], 'listeners' => [ + 'listener' => ['options' => [['listener_option' => 'listener_value']]], + ]]); + + $suite->getName()->shouldReturn('suite_name'); + $suite->getListeners()->shouldHaveKeyWithValue($listener, ['listener_option' => 'listener_value']); + } + + function it_throws_an_exception_if_suite_options_does_not_have_fixtures(): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('createSuite', ['suite_name', ['listeners' => []]]); + } + + function it_throws_an_exception_if_suite_options_does_not_have_listeners(): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('createSuite', ['suite_name', ['fixtures' => []]]); + } + + function it_throws_an_exception_if_fixture_does_not_have_options_defined(): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('createSuite', ['suite_name', ['listeners' => [], 'fixtures' => [ + 'fixture' => ['name' => 'fixture'], + ]]]); + } + + function it_throws_an_exception_if_fixture_does_not_have_name_defined(): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('createSuite', ['suite_name', ['listeners' => [], 'fixtures' => [ + 'fixture' => ['options' => []], + ]]]); + } + + function it_throws_an_exception_if_listener_does_not_have_options_defined(): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('createSuite', ['suite_name', ['fixtures' => [], 'listeners' => [ + 'listener' => [], + ]]]); + } + + /** + * @param Collaborator[] ...$collaborators + * + * @return \Generator + */ + private function createGenerator(Collaborator ...$collaborators): \Generator + { + foreach ($collaborators as $collaborator) { + yield $collaborator->getWrappedObject() => []; + } + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Suite/SuiteNotFoundExceptionSpec.php b/vendor/sylius/fixtures-bundle/spec/Suite/SuiteNotFoundExceptionSpec.php new file mode 100644 index 0000000..354811c --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Suite/SuiteNotFoundExceptionSpec.php @@ -0,0 +1,34 @@ +beConstructedWith('suite_name'); + } + + function it_is_an_invalid_argument_exception(): void + { + $this->shouldHaveType(\InvalidArgumentException::class); + } + + function it_has_preformatted_message(): void + { + $this->getMessage()->shouldReturn('Suite with name "suite_name" could not be found!'); + } +} diff --git a/vendor/sylius/fixtures-bundle/spec/Suite/SuiteSpec.php b/vendor/sylius/fixtures-bundle/spec/Suite/SuiteSpec.php new file mode 100644 index 0000000..08404a8 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/spec/Suite/SuiteSpec.php @@ -0,0 +1,88 @@ +beConstructedWith('suite_name'); + } + + function it_implements_suite_interface(): void + { + $this->shouldImplement(SuiteInterface::class); + } + + function it_has_name(): void + { + $this->getName()->shouldReturn('suite_name'); + } + + function it_has_no_fixtures_by_default(): void + { + $this->getFixtures()->shouldIterateAs([]); + } + + function it_allows_for_adding_a_fixture(FixtureInterface $fixture): void + { + $this->addFixture($fixture, []); + + $this->getFixtures()->shouldHaveKey($fixture); + } + + function it_stores_a_fixture_with_its_options(FixtureInterface $fixture): void + { + $this->addFixture($fixture, ['fixture_option' => 'fixture_name']); + + $this->getFixtures()->shouldHaveKeyWithValue($fixture, ['fixture_option' => 'fixture_name']); + } + + function it_stores_multiple_fixtures_as_queue(FixtureInterface $firstFixture, FixtureInterface $secondFixture): void + { + $this->addFixture($firstFixture, []); + $this->addFixture($secondFixture, []); + + $this->getFixtures()->shouldIterateAs($this->createGenerator($firstFixture, $secondFixture)); + } + + function it_keeps_the_priority_of_fixtures( + FixtureInterface $regularFixture, + FixtureInterface $higherPriorityFixture, + FixtureInterface $lowerPriorityFixture + ): void { + $this->addFixture($regularFixture, []); + $this->addFixture($higherPriorityFixture, [], 10); + $this->addFixture($lowerPriorityFixture, [], -10); + + $this->getFixtures()->shouldIterateAs($this->createGenerator($higherPriorityFixture, $regularFixture, $lowerPriorityFixture)); + } + + /** + * @param Collaborator[] ...$collaborators + * + * @return \Generator + */ + private function createGenerator(Collaborator ...$collaborators): \Generator + { + foreach ($collaborators as $collaborator) { + yield $collaborator->getWrappedObject() => []; + } + } +} diff --git a/vendor/sylius/fixtures-bundle/test/app/AppKernel.php b/vendor/sylius/fixtures-bundle/test/app/AppKernel.php new file mode 100644 index 0000000..991739d --- /dev/null +++ b/vendor/sylius/fixtures-bundle/test/app/AppKernel.php @@ -0,0 +1,60 @@ +load(__DIR__ . '/config/config.yml'); + } + + /** + * {@inheritdoc} + */ + protected function getContainerBaseClass() + { + if (0 === strpos($this->environment, 'test')) { + return MockerContainer::class; + } + + return parent::getContainerBaseClass(); + } +} diff --git a/vendor/sylius/fixtures-bundle/test/app/config/config.yml b/vendor/sylius/fixtures-bundle/test/app/config/config.yml new file mode 100644 index 0000000..0a6f82a --- /dev/null +++ b/vendor/sylius/fixtures-bundle/test/app/config/config.yml @@ -0,0 +1,32 @@ +imports: + - { resource: "@SyliusFixturesBundle/test/app/config/parameters.yml" } + +framework: + assets: false + translator: { fallbacks: ["%locale%"] } + secret: "%secret%" + router: + resource: "%kernel.project_dir%/app/config/routing.yml" + csrf_protection: true + templating: + engines: ['twig'] + default_locale: "%locale%" + session: + handler_id: ~ + storage_id: session.storage.mock_file + http_method_override: true + test: ~ + +twig: + debug: "%kernel.debug%" + strict_variables: "%kernel.debug%" + +doctrine: + dbal: + driver: "%database_driver%" + path: "%database_path%" + charset: UTF8 + orm: + entity_managers: + default: + auto_mapping: true diff --git a/vendor/sylius/fixtures-bundle/test/app/config/parameters.yml b/vendor/sylius/fixtures-bundle/test/app/config/parameters.yml new file mode 100644 index 0000000..5532124 --- /dev/null +++ b/vendor/sylius/fixtures-bundle/test/app/config/parameters.yml @@ -0,0 +1,6 @@ +parameters: + database_driver: pdo_sqlite + database_path: "%kernel.project_dir%/app/db.sql" + + locale: en_US + secret: "Three can keep a secret, if two of them are dead." diff --git a/vendor/sylius/fixtures-bundle/test/app/console b/vendor/sylius/fixtures-bundle/test/app/console new file mode 100755 index 0000000..296012b --- /dev/null +++ b/vendor/sylius/fixtures-bundle/test/app/console @@ -0,0 +1,24 @@ +#!/usr/bin/env php +run($input); diff --git a/vendor/sylius/fixtures-bundle/test/composer.json b/vendor/sylius/fixtures-bundle/test/composer.json new file mode 100644 index 0000000..cb50c8b --- /dev/null +++ b/vendor/sylius/fixtures-bundle/test/composer.json @@ -0,0 +1,3 @@ +{ + "name": "example/test-application" +} diff --git a/vendor/sylius/fixtures-bundle/test/src/Tests/SyliusFixturesBundleTest.php b/vendor/sylius/fixtures-bundle/test/src/Tests/SyliusFixturesBundleTest.php new file mode 100644 index 0000000..9a68fdc --- /dev/null +++ b/vendor/sylius/fixtures-bundle/test/src/Tests/SyliusFixturesBundleTest.php @@ -0,0 +1,41 @@ +getContainer(); + + $services = $container->getServiceIds(); + + $services = array_filter($services, function (string $serviceId): bool { + return 0 === strpos($serviceId, 'sylius.'); + }); + + foreach ($services as $id) { + Assert::assertNotNull($container->get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + } +} diff --git a/vendor/sylius/resource/.gitignore b/vendor/sylius/resource/.gitignore new file mode 100644 index 0000000..46aed33 --- /dev/null +++ b/vendor/sylius/resource/.gitignore @@ -0,0 +1,5 @@ +vendor/ +bin/ + +composer.phar +composer.lock diff --git a/vendor/sylius/resource/Exception/DeleteHandlingException.php b/vendor/sylius/resource/Exception/DeleteHandlingException.php new file mode 100644 index 0000000..23c17fa --- /dev/null +++ b/vendor/sylius/resource/Exception/DeleteHandlingException.php @@ -0,0 +1,63 @@ +flash = $flash; + $this->apiResponseCode = $apiResponseCode; + } + + /** + * @return string + */ + public function getFlash(): string + { + return $this->flash; + } + + /** + * @return int + */ + public function getApiResponseCode(): int + { + return $this->apiResponseCode; + } +} diff --git a/vendor/sylius/resource/Exception/RaceConditionException.php b/vendor/sylius/resource/Exception/RaceConditionException.php new file mode 100644 index 0000000..32e1dfe --- /dev/null +++ b/vendor/sylius/resource/Exception/RaceConditionException.php @@ -0,0 +1,31 @@ +getCode() : 0, + $previous + ); + } +} diff --git a/vendor/sylius/resource/Exception/UnexpectedTypeException.php b/vendor/sylius/resource/Exception/UnexpectedTypeException.php new file mode 100644 index 0000000..3f7a511 --- /dev/null +++ b/vendor/sylius/resource/Exception/UnexpectedTypeException.php @@ -0,0 +1,30 @@ +flash = $flash; + $this->apiResponseCode = $apiResponseCode; + } + + /** + * @return string + */ + public function getFlash(): string + { + return $this->flash; + } + + /** + * @return int + */ + public function getApiResponseCode(): int + { + return $this->apiResponseCode; + } +} diff --git a/vendor/sylius/resource/Factory/Factory.php b/vendor/sylius/resource/Factory/Factory.php new file mode 100644 index 0000000..e8b2390 --- /dev/null +++ b/vendor/sylius/resource/Factory/Factory.php @@ -0,0 +1,41 @@ +className = $className; + } + + /** + * {@inheritdoc} + */ + public function createNew() + { + return new $this->className(); + } +} diff --git a/vendor/sylius/resource/Factory/FactoryInterface.php b/vendor/sylius/resource/Factory/FactoryInterface.php new file mode 100644 index 0000000..defa690 --- /dev/null +++ b/vendor/sylius/resource/Factory/FactoryInterface.php @@ -0,0 +1,22 @@ +factory = $factory; + $this->localeProvider = $localeProvider; + } + + /** + * {@inheritdoc} + * + * @throws UnexpectedTypeException + */ + public function createNew() + { + $resource = $this->factory->createNew(); + + if (!$resource instanceof TranslatableInterface) { + throw new UnexpectedTypeException($resource, TranslatableInterface::class); + } + + $resource->setCurrentLocale($this->localeProvider->getDefaultLocaleCode()); + $resource->setFallbackLocale($this->localeProvider->getDefaultLocaleCode()); + + return $resource; + } +} diff --git a/vendor/sylius/resource/Factory/TranslatableFactoryInterface.php b/vendor/sylius/resource/Factory/TranslatableFactoryInterface.php new file mode 100644 index 0000000..cc1fe6b --- /dev/null +++ b/vendor/sylius/resource/Factory/TranslatableFactoryInterface.php @@ -0,0 +1,18 @@ +digits = implode(range(0, 9)); + + $this->uriSafeAlphabet = + implode(range(0, 9)) + . implode(range('a', 'z')) + . implode(range('A', 'Z')) + . implode(['-', '_', '~']) + ; + } + + /** + * {@inheritdoc} + */ + public function generateUriSafeString(int $length): string + { + return $this->generateStringOfLength($length, $this->uriSafeAlphabet); + } + + /** + * {@inheritdoc} + */ + public function generateNumeric(int $length): string + { + return $this->generateStringOfLength($length, $this->digits); + } + + /** + * {@inheritdoc} + */ + public function generateInt(int $min, int $max): int + { + return random_int($min, $max); + } + + /** + * @param int $length + * @param string $alphabet + * + * @return string + */ + private function generateStringOfLength(int $length, string $alphabet): string + { + $alphabetMaxIndex = strlen($alphabet) - 1; + $randomString = ''; + + for ($i = 0; $i < $length; ++$i) { + $index = random_int(0, $alphabetMaxIndex); + $randomString .= $alphabet[$index]; + } + + return $randomString; + } +} diff --git a/vendor/sylius/resource/Generator/RandomnessGeneratorInterface.php b/vendor/sylius/resource/Generator/RandomnessGeneratorInterface.php new file mode 100644 index 0000000..5c7a201 --- /dev/null +++ b/vendor/sylius/resource/Generator/RandomnessGeneratorInterface.php @@ -0,0 +1,39 @@ +name = $name; + $this->applicationName = $applicationName; + + $this->driver = $parameters['driver']; + $this->templatesNamespace = array_key_exists('templates', $parameters) ? $parameters['templates'] : null; + + $this->parameters = $parameters; + } + + /** + * @param string $alias + * @param array $parameters + * + * @return self + */ + public static function fromAliasAndConfiguration(string $alias, array $parameters): self + { + [$applicationName, $name] = self::parseAlias($alias); + + return new self($name, $applicationName, $parameters); + } + + /** + * {@inheritdoc} + */ + public function getAlias(): string + { + return $this->applicationName . '.' . $this->name; + } + + /** + * {@inheritdoc} + */ + public function getApplicationName(): string + { + return $this->applicationName; + } + + /** + * {@inheritdoc} + */ + public function getName(): string + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getHumanizedName(): string + { + return strtolower(trim(preg_replace(['/([A-Z])/', '/[_\s]+/'], ['_$1', ' '], $this->name))); + } + + /** + * {@inheritdoc} + */ + public function getPluralName(): string + { + return Inflector::pluralize($this->name); + } + + /** + * {@inheritdoc} + */ + public function getDriver(): string + { + return $this->driver; + } + + /** + * {@inheritdoc} + */ + public function getTemplatesNamespace(): ?string + { + return $this->templatesNamespace; + } + + /** + * {@inheritdoc} + */ + public function getParameter(string $name) + { + if (!$this->hasParameter($name)) { + throw new \InvalidArgumentException(sprintf('Parameter "%s" is not configured for resource "%s".', $name, $this->getAlias())); + } + + return $this->parameters[$name]; + } + + /** + * {@inheritdoc} + */ + public function hasParameter(string $name): bool + { + return array_key_exists($name, $this->parameters); + } + + /** + * {@inheritdoc} + */ + public function getParameters(): array + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function getClass(string $name): string + { + if (!$this->hasClass($name)) { + throw new \InvalidArgumentException(sprintf('Class "%s" is not configured for resource "%s".', $name, $this->getAlias())); + } + + return $this->parameters['classes'][$name]; + } + + /** + * {@inheritdoc} + */ + public function hasClass(string $name): bool + { + return isset($this->parameters['classes'][$name]); + } + + /** + * {@inheritdoc} + */ + public function getServiceId(string $serviceName): string + { + return sprintf('%s.%s.%s', $this->applicationName, $serviceName, $this->name); + } + + /** + * {@inheritdoc} + */ + public function getPermissionCode(string $permissionName): string + { + return sprintf('%s.%s.%s', $this->applicationName, $this->name, $permissionName); + } + + /** + * @param string $alias + * + * @return array + */ + private static function parseAlias(string $alias): array + { + if (false === strpos($alias, '.')) { + throw new \InvalidArgumentException(sprintf('Invalid alias "%s" supplied, it should conform to the following format ".".', $alias)); + } + + return explode('.', $alias); + } +} diff --git a/vendor/sylius/resource/Metadata/MetadataInterface.php b/vendor/sylius/resource/Metadata/MetadataInterface.php new file mode 100644 index 0000000..5afe4fd --- /dev/null +++ b/vendor/sylius/resource/Metadata/MetadataInterface.php @@ -0,0 +1,105 @@ +metadata; + } + + /** + * {@inheritdoc} + */ + public function get(string $alias): MetadataInterface + { + if (!array_key_exists($alias, $this->metadata)) { + throw new \InvalidArgumentException(sprintf('Resource "%s" does not exist.', $alias)); + } + + return $this->metadata[$alias]; + } + + /** + * {@inheritdoc} + */ + public function getByClass(string $className): MetadataInterface + { + foreach ($this->metadata as $metadata) { + if ($className === $metadata->getClass('model')) { + return $metadata; + } + } + + throw new \InvalidArgumentException(sprintf('Resource with model class "%s" does not exist.', $className)); + } + + /** + * {@inheritdoc} + */ + public function add(MetadataInterface $metadata): void + { + $this->metadata[$metadata->getAlias()] = $metadata; + } + + /** + * {@inheritdoc} + */ + public function addFromAliasAndConfiguration(string $alias, array $configuration): void + { + $this->add(Metadata::fromAliasAndConfiguration($alias, $configuration)); + } +} diff --git a/vendor/sylius/resource/Metadata/RegistryInterface.php b/vendor/sylius/resource/Metadata/RegistryInterface.php new file mode 100644 index 0000000..b53b121 --- /dev/null +++ b/vendor/sylius/resource/Metadata/RegistryInterface.php @@ -0,0 +1,54 @@ +translatable; + } + + /** + * {@inheritdoc} + */ + public function setTranslatable(?TranslatableInterface $translatable): void + { + if ($translatable === $this->translatable) { + return; + } + + $previousTranslatable = $this->translatable; + $this->translatable = $translatable; + + if (null !== $previousTranslatable) { + $previousTranslatable->removeTranslation($this); + } + + if (null !== $translatable) { + $translatable->addTranslation($this); + } + } + + /** + * {@inheritdoc} + */ + public function getLocale(): ?string + { + return $this->locale; + } + + /** + * {@inheritdoc} + */ + public function setLocale(?string $locale): void + { + $this->locale = $locale; + } +} diff --git a/vendor/sylius/resource/Model/ArchivableInterface.php b/vendor/sylius/resource/Model/ArchivableInterface.php new file mode 100644 index 0000000..cc6c18c --- /dev/null +++ b/vendor/sylius/resource/Model/ArchivableInterface.php @@ -0,0 +1,27 @@ +archivedAt; + } + + /** + * @param \DateTimeInterface|null $archivedAt + */ + public function setArchivedAt(?\DateTimeInterface $archivedAt): void + { + $this->archivedAt = $archivedAt; + } +} diff --git a/vendor/sylius/resource/Model/CodeAwareInterface.php b/vendor/sylius/resource/Model/CodeAwareInterface.php new file mode 100644 index 0000000..eacdb8d --- /dev/null +++ b/vendor/sylius/resource/Model/CodeAwareInterface.php @@ -0,0 +1,27 @@ +createdAt; + } + + /** + * @param \DateTimeInterface|null $createdAt + */ + public function setCreatedAt(?\DateTimeInterface $createdAt): void + { + $this->createdAt = $createdAt; + } + + /** + * @return \DateTimeInterface|null + */ + public function getUpdatedAt(): ?\DateTimeInterface + { + return $this->updatedAt; + } + + /** + * @param \DateTimeInterface|null $updatedAt + */ + public function setUpdatedAt(?\DateTimeInterface $updatedAt): void + { + $this->updatedAt = $updatedAt; + } +} diff --git a/vendor/sylius/resource/Model/ToggleableInterface.php b/vendor/sylius/resource/Model/ToggleableInterface.php new file mode 100644 index 0000000..d268827 --- /dev/null +++ b/vendor/sylius/resource/Model/ToggleableInterface.php @@ -0,0 +1,33 @@ +enabled; + } + + /** + * @param bool $enabled + */ + public function setEnabled(?bool $enabled): void + { + $this->enabled = (bool) $enabled; + } + + public function enable(): void + { + $this->enabled = true; + } + + public function disable(): void + { + $this->enabled = false; + } +} diff --git a/vendor/sylius/resource/Model/TranslatableInterface.php b/vendor/sylius/resource/Model/TranslatableInterface.php new file mode 100644 index 0000000..cf79b16 --- /dev/null +++ b/vendor/sylius/resource/Model/TranslatableInterface.php @@ -0,0 +1,58 @@ +translations = new ArrayCollection(); + } + + /** + * @param string|null $locale + * + * @return TranslationInterface + */ + public function getTranslation(?string $locale = null): TranslationInterface + { + $locale = $locale ?: $this->currentLocale; + if (null === $locale) { + throw new \RuntimeException('No locale has been set and current locale is undefined.'); + } + + if (isset($this->translationsCache[$locale])) { + return $this->translationsCache[$locale]; + } + + $translation = $this->translations->get($locale); + if (null !== $translation) { + $this->translationsCache[$locale] = $translation; + + return $translation; + } + + if ($locale !== $this->fallbackLocale) { + if (isset($this->translationsCache[$this->fallbackLocale])) { + return $this->translationsCache[$this->fallbackLocale]; + } + + $fallbackTranslation = $this->translations->get($this->fallbackLocale); + if (null !== $fallbackTranslation) { + $this->translationsCache[$this->fallbackLocale] = $fallbackTranslation; + + return $fallbackTranslation; + } + } + + $translation = $this->createTranslation(); + $translation->setLocale($locale); + + $this->addTranslation($translation); + + $this->translationsCache[$locale] = $translation; + + return $translation; + } + + /** + * @return Collection|TranslationInterface[] + */ + public function getTranslations(): Collection + { + return $this->translations; + } + + /** + * @param TranslationInterface $translation + * + * @return bool + */ + public function hasTranslation(TranslationInterface $translation): bool + { + return isset($this->translationsCache[$translation->getLocale()]) || $this->translations->containsKey($translation->getLocale()); + } + + /** + * @param TranslationInterface $translation + */ + public function addTranslation(TranslationInterface $translation): void + { + if (!$this->hasTranslation($translation)) { + $this->translationsCache[$translation->getLocale()] = $translation; + + $this->translations->set($translation->getLocale(), $translation); + $translation->setTranslatable($this); + } + } + + /** + * @param TranslationInterface $translation + */ + public function removeTranslation(TranslationInterface $translation): void + { + if ($this->translations->removeElement($translation)) { + unset($this->translationsCache[$translation->getLocale()]); + + $translation->setTranslatable(null); + } + } + + /** + * @param string $currentLocale + */ + public function setCurrentLocale(string $currentLocale): void + { + $this->currentLocale = $currentLocale; + } + + /** + * @param string $fallbackLocale + */ + public function setFallbackLocale(string $fallbackLocale): void + { + $this->fallbackLocale = $fallbackLocale; + } + + /** + * Create resource translation model. + * + * @return TranslationInterface + */ + abstract protected function createTranslation(): TranslationInterface; +} diff --git a/vendor/sylius/resource/Model/TranslationInterface.php b/vendor/sylius/resource/Model/TranslationInterface.php new file mode 100644 index 0000000..b407da8 --- /dev/null +++ b/vendor/sylius/resource/Model/TranslationInterface.php @@ -0,0 +1,37 @@ +interface = $interface; + $this->accessor = PropertyAccess::createPropertyAccessor(); + $this->arrayObject = new ArrayObject(); + } + + /** + * {@inheritdoc} + * + * @throws ExistingResourceException + * @throws UnexpectedTypeException + */ + public function add(ResourceInterface $resource): void + { + if (!$resource instanceof $this->interface) { + throw new UnexpectedTypeException($resource, $this->interface); + } + + if (in_array($resource, $this->findAll())) { + throw new ExistingResourceException(); + } + + $this->arrayObject->append($resource); + } + + /** + * {@inheritdoc} + */ + public function remove(ResourceInterface $resource): void + { + $newResources = array_filter($this->findAll(), function ($object) use ($resource) { + return $object !== $resource; + }); + + $this->arrayObject->exchangeArray($newResources); + } + + /** + * {@inheritdoc} + * + * @throws UnsupportedMethodException + */ + public function find($id) + { + return $this->findOneBy(['id' => $id]); + } + + /** + * {@inheritdoc} + */ + public function findAll(): array + { + return $this->arrayObject->getArrayCopy(); + } + + /** + * {@inheritdoc} + */ + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + $results = $this->findAll(); + + if (!empty($criteria)) { + $results = $this->applyCriteria($results, $criteria); + } + + if (!empty($orderBy)) { + $results = $this->applyOrder($results, $orderBy); + } + + $results = array_slice($results, $offset ?? 0, $limit); + + return $results; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException + */ + public function findOneBy(array $criteria): ?ResourceInterface + { + if (empty($criteria)) { + throw new \InvalidArgumentException('The criteria array needs to be set.'); + } + + $results = $this->applyCriteria($this->findAll(), $criteria); + + if ($result = reset($results)) { + return $result; + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getClassName(): string + { + return $this->interface; + } + + /** + * {@inheritdoc} + */ + public function createPaginator(array $criteria = [], array $sorting = []): iterable + { + $resources = $this->findAll(); + + if (!empty($sorting)) { + $resources = $this->applyOrder($resources, $sorting); + } + + if (!empty($criteria)) { + $resources = $this->applyCriteria($resources, $criteria); + } + + return new Pagerfanta(new ArrayAdapter($resources)); + } + + /** + * @param ResourceInterface[] $resources + * @param array $criteria + * + * @return ResourceInterface[]|array + */ + private function applyCriteria(array $resources, array $criteria): array + { + foreach ($this->arrayObject as $object) { + foreach ($criteria as $criterion => $value) { + if ($value !== $this->accessor->getValue($object, $criterion)) { + $key = array_search($object, $resources); + unset($resources[$key]); + } + } + } + + return $resources; + } + + /** + * @param ResourceInterface[] $resources + * @param array $orderBy + * + * @return ResourceInterface[] + */ + private function applyOrder(array $resources, array $orderBy): array + { + $results = $resources; + + foreach ($orderBy as $property => $order) { + $sortable = []; + + foreach ($results as $key => $object) { + $sortable[$key] = $this->accessor->getValue($object, $property); + } + + if (RepositoryInterface::ORDER_ASCENDING === $order) { + asort($sortable); + } + if (RepositoryInterface::ORDER_DESCENDING === $order) { + arsort($sortable); + } + + $results = []; + + foreach ($sortable as $key => $propertyValue) { + $results[$key] = $resources[$key]; + } + } + + return $results; + } +} diff --git a/vendor/sylius/resource/Repository/RepositoryInterface.php b/vendor/sylius/resource/Repository/RepositoryInterface.php new file mode 100644 index 0000000..ffad7e4 --- /dev/null +++ b/vendor/sylius/resource/Repository/RepositoryInterface.php @@ -0,0 +1,41 @@ +getPossibleTransitions() as $transition) { + $config = $this->config['transitions'][$transition]; + if (in_array($fromState, $config['from'], true)) { + return $transition; + } + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getTransitionToState(string $toState): ?string + { + foreach ($this->getPossibleTransitions() as $transition) { + $config = $this->config['transitions'][$transition]; + if ($toState === $config['to']) { + return $transition; + } + } + + return null; + } +} diff --git a/vendor/sylius/resource/StateMachine/StateMachineInterface.php b/vendor/sylius/resource/StateMachine/StateMachineInterface.php new file mode 100644 index 0000000..aba4b87 --- /dev/null +++ b/vendor/sylius/resource/StateMachine/StateMachineInterface.php @@ -0,0 +1,39 @@ +definedLocalesCodes = $definedLocalesCodes; + $this->defaultLocaleCode = $defaultLocaleCode; + } + + /** + * {@inheritdoc} + */ + public function getDefinedLocalesCodes(): array + { + return $this->definedLocalesCodes; + } + + /** + * {@inheritdoc} + */ + public function getDefaultLocaleCode(): string + { + return $this->defaultLocaleCode; + } +} diff --git a/vendor/sylius/resource/Translation/Provider/TranslationLocaleProviderInterface.php b/vendor/sylius/resource/Translation/Provider/TranslationLocaleProviderInterface.php new file mode 100644 index 0000000..b6fc67e --- /dev/null +++ b/vendor/sylius/resource/Translation/Provider/TranslationLocaleProviderInterface.php @@ -0,0 +1,27 @@ +translationLocaleProvider = $translationLocaleProvider; + } + + /** + * {@inheritdoc} + */ + public function assignLocale(TranslatableInterface $translatableEntity): void + { + $localeCode = $this->translationLocaleProvider->getDefaultLocaleCode(); + + $translatableEntity->setCurrentLocale($localeCode); + $translatableEntity->setFallbackLocale($localeCode); + } +} diff --git a/vendor/sylius/resource/Translation/TranslatableEntityLocaleAssignerInterface.php b/vendor/sylius/resource/Translation/TranslatableEntityLocaleAssignerInterface.php new file mode 100644 index 0000000..e069b12 --- /dev/null +++ b/vendor/sylius/resource/Translation/TranslatableEntityLocaleAssignerInterface.php @@ -0,0 +1,24 @@ +shouldHaveType(\Exception::class); + } + + function it_has_a_message(): void + { + $this->getMessage()->shouldReturn('Ups, something went wrong during deleting a resource, please try again.'); + } + + function it_has_a_flash(): void + { + $this->getFlash()->shouldReturn('something_went_wrong_error'); + } + + function it_has_an_api_response_code(): void + { + $this->getApiResponseCode()->shouldReturn(500); + } +} diff --git a/vendor/sylius/resource/spec/Exception/RaceConditionExceptionSpec.php b/vendor/sylius/resource/spec/Exception/RaceConditionExceptionSpec.php new file mode 100644 index 0000000..8a70058 --- /dev/null +++ b/vendor/sylius/resource/spec/Exception/RaceConditionExceptionSpec.php @@ -0,0 +1,40 @@ +shouldHaveType(UpdateHandlingException::class); + } + + function it_has_a_message(): void + { + $this->getMessage()->shouldReturn('Operated entity was previously modified.'); + } + + function it_has_a_flash(): void + { + $this->getFlash()->shouldReturn('race_condition_error'); + } + + function it_has_an_api_response_code(): void + { + $this->getApiResponseCode()->shouldReturn(409); + } +} diff --git a/vendor/sylius/resource/spec/Exception/UnexpectedTypeExceptionSpec.php b/vendor/sylius/resource/spec/Exception/UnexpectedTypeExceptionSpec.php new file mode 100644 index 0000000..e78fede --- /dev/null +++ b/vendor/sylius/resource/spec/Exception/UnexpectedTypeExceptionSpec.php @@ -0,0 +1,34 @@ +beConstructedWith('stringValue', '\ExpectedType'); + } + + function it_extends_invalid_argument_exception(): void + { + $this->shouldHaveType(\InvalidArgumentException::class); + } + + function it_has_a_message(): void + { + $this->getMessage()->shouldReturn('Expected argument of type "\ExpectedType", "string" given.'); + } +} diff --git a/vendor/sylius/resource/spec/Exception/UnsupportedMethodExceptionSpec.php b/vendor/sylius/resource/spec/Exception/UnsupportedMethodExceptionSpec.php new file mode 100644 index 0000000..506eacd --- /dev/null +++ b/vendor/sylius/resource/spec/Exception/UnsupportedMethodExceptionSpec.php @@ -0,0 +1,34 @@ +beConstructedWith('methodName'); + } + + function it_extends_exception(): void + { + $this->shouldHaveType(\Exception::class); + } + + function it_has_a_message(): void + { + $this->getMessage()->shouldReturn('The method "methodName" is not supported.'); + } +} diff --git a/vendor/sylius/resource/spec/Exception/UpdateHandlingExceptionSpec.php b/vendor/sylius/resource/spec/Exception/UpdateHandlingExceptionSpec.php new file mode 100644 index 0000000..00413d6 --- /dev/null +++ b/vendor/sylius/resource/spec/Exception/UpdateHandlingExceptionSpec.php @@ -0,0 +1,39 @@ +shouldHaveType(\Exception::class); + } + + function it_has_a_message(): void + { + $this->getMessage()->shouldReturn('Ups, something went wrong during updating a resource, please try again.'); + } + + function it_has_a_flash(): void + { + $this->getFlash()->shouldReturn('something_went_wrong_error'); + } + + function it_has_an_api_response_code(): void + { + $this->getApiResponseCode()->shouldReturn(400); + } +} diff --git a/vendor/sylius/resource/spec/Factory/FactorySpec.php b/vendor/sylius/resource/spec/Factory/FactorySpec.php new file mode 100644 index 0000000..56a5e18 --- /dev/null +++ b/vendor/sylius/resource/spec/Factory/FactorySpec.php @@ -0,0 +1,35 @@ +beConstructedWith(\stdClass::class); + } + + function it_implements_factory_interface(): void + { + $this->shouldHaveType(FactoryInterface::class); + } + + function it_creates_a_new_instance_of_a_resource(): void + { + $this->createNew()->shouldHaveType(\stdClass::class); + } +} diff --git a/vendor/sylius/resource/spec/Factory/TranslatableFactorySpec.php b/vendor/sylius/resource/spec/Factory/TranslatableFactorySpec.php new file mode 100644 index 0000000..a885a22 --- /dev/null +++ b/vendor/sylius/resource/spec/Factory/TranslatableFactorySpec.php @@ -0,0 +1,59 @@ +beConstructedWith($factory, $localeProvider); + } + + function it_implements_translatable_factory_interface(): void + { + $this->shouldImplement(TranslatableFactoryInterface::class); + } + + function it_throws_an_exception_if_resource_is_not_translatable(FactoryInterface $factory, \stdClass $resource): void + { + $factory->createNew()->willReturn($resource); + + $this + ->shouldThrow(UnexpectedTypeException::class) + ->during('createNew') + ; + } + + function it_creates_translatable_and_sets_locales( + FactoryInterface $factory, + TranslationLocaleProviderInterface $localeProvider, + TranslatableInterface $resource + ): void { + $localeProvider->getDefaultLocaleCode()->willReturn('pl_PL'); + + $factory->createNew()->willReturn($resource); + + $resource->setCurrentLocale('pl_PL')->shouldBeCalled(); + $resource->setFallbackLocale('pl_PL')->shouldBeCalled(); + + $this->createNew()->shouldReturn($resource); + } +} diff --git a/vendor/sylius/resource/spec/Fixtures/SampleBookResourceInterface.php b/vendor/sylius/resource/spec/Fixtures/SampleBookResourceInterface.php new file mode 100644 index 0000000..3edbdaa --- /dev/null +++ b/vendor/sylius/resource/spec/Fixtures/SampleBookResourceInterface.php @@ -0,0 +1,34 @@ +shouldImplement(RandomnessGeneratorInterface::class); + } + + function it_generates_random_uri_safe_string_of_length(): void + { + $length = 9; + + $this->generateUriSafeString($length)->shouldBeString(); + $this->generateUriSafeString($length)->shouldHaveLength($length); + } + + function it_generates_random_numeric_string_of_length(): void + { + $length = 12; + + $this->generateNumeric($length)->shouldBeString(); + $this->generateNumeric($length)->shouldBeNumeric(); + $this->generateNumeric($length)->shouldHaveLength($length); + } + + function it_generates_random_int_in_range(): void + { + $min = 12; + $max = 2000000; + + $this->generateInt($min, $max)->shouldBeInt(); + $this->generateInt($min, $max)->shouldBeInRange($min, $max); + } + + /** + * {@inheritdoc} + */ + public function getMatchers(): array + { + return [ + 'haveLength' => function ($subject, $length) { + return $length === strlen($subject); + }, + 'beInRange' => function ($subject, $min, $max) { + return $subject >= $min && $subject <= $max; + }, + ]; + } +} diff --git a/vendor/sylius/resource/spec/Metadata/MetadataSpec.php b/vendor/sylius/resource/spec/Metadata/MetadataSpec.php new file mode 100644 index 0000000..0f089c5 --- /dev/null +++ b/vendor/sylius/resource/spec/Metadata/MetadataSpec.php @@ -0,0 +1,138 @@ +beConstructedThrough('fromAliasAndConfiguration', [ + 'app.product', + [ + 'driver' => 'doctrine/orm', + 'templates' => 'AppBundle:Resource', + 'classes' => [ + 'model' => 'AppBundle\Model\Resource', + 'form' => [ + 'default' => 'AppBundle\Form\Type\ResourceType', + 'choice' => 'AppBundle\Form\Type\ResourceChoiceType', + 'autocomplete' => 'AppBundle\Type\ResourceAutocompleteType', + ], + ], + ], + ]); + } + + function it_implements_metadata_interface(): void + { + $this->shouldImplement(MetadataInterface::class); + } + + function it_has_alias(): void + { + $this->getAlias()->shouldReturn('app.product'); + } + + function it_has_application_name(): void + { + $this->getApplicationName()->shouldReturn('app'); + } + + function it_has_resource_name(): void + { + $this->getName()->shouldReturn('product'); + } + + function it_humanizes_simple_names(): void + { + $this->getHumanizedName()->shouldReturn('product'); + } + + function it_humanizes_more_complex_names(): void + { + $this->beConstructedThrough('fromAliasAndConfiguration', ['app.product_option', ['driver' => 'doctrine/orm']]); + + $this->getHumanizedName()->shouldReturn('product option'); + } + + function it_has_plural_resource_name(): void + { + $this->getPluralName()->shouldReturn('products'); + } + + function it_has_driver(): void + { + $this->getDriver()->shouldReturn('doctrine/orm'); + } + + function it_has_templates_namespace(): void + { + $this->getTemplatesNamespace()->shouldReturn('AppBundle:Resource'); + } + + function it_has_access_to_specific_config_parameter(): void + { + $this->getParameter('driver')->shouldReturn('doctrine/orm'); + } + + function it_checks_if_specific_parameter_exists(): void + { + $this->hasParameter('foo')->shouldReturn(false); + $this->hasParameter('driver')->shouldReturn(true); + } + + function it_throws_an_exception_when_parameter_does_not_exist(): void + { + $this + ->shouldThrow(\InvalidArgumentException::class) + ->during('getParameter', ['foo']) + ; + } + + function it_has_access_to_specific_classes(): void + { + $this->getClass('model')->shouldReturn('AppBundle\Model\Resource'); + } + + function it_throws_an_exception_when_class_does_not_exist(): void + { + $this + ->shouldThrow(\InvalidArgumentException::class) + ->during('getClass', ['foo']) + ; + } + + function it_checks_if_specific_class_exists(): void + { + $this->hasClass('bar')->shouldReturn(false); + $this->hasClass('model')->shouldReturn(true); + } + + function it_generates_service_id(): void + { + $this->getServiceId('factory')->shouldReturn('app.factory.product'); + $this->getServiceId('repository')->shouldReturn('app.repository.product'); + $this->getServiceId('form.type')->shouldReturn('app.form.type.product'); + } + + function it_generates_permission_code(): void + { + $this->getPermissionCode('show')->shouldReturn('app.product.show'); + $this->getPermissionCode('create')->shouldReturn('app.product.create'); + $this->getPermissionCode('custom')->shouldReturn('app.product.custom'); + } +} diff --git a/vendor/sylius/resource/spec/Metadata/RegistrySpec.php b/vendor/sylius/resource/spec/Metadata/RegistrySpec.php new file mode 100644 index 0000000..8b823f5 --- /dev/null +++ b/vendor/sylius/resource/spec/Metadata/RegistrySpec.php @@ -0,0 +1,89 @@ +shouldImplement(RegistryInterface::class); + } + + function it_returns_all_resources_metadata(MetadataInterface $metadata1, MetadataInterface $metadata2): void + { + $metadata1->getAlias()->willReturn('app.product'); + $metadata2->getAlias()->willReturn('app.order'); + + $this->add($metadata1); + $this->add($metadata2); + + $this->getAll()->shouldReturn(['app.product' => $metadata1, 'app.order' => $metadata2]); + } + + function it_throws_an_exception_if_resource_is_not_registered(): void + { + $this + ->shouldThrow(\InvalidArgumentException::class) + ->during('get', ['foo.bar']) + ; + } + + function it_returns_specific_metadata(MetadataInterface $metadata): void + { + $metadata->getAlias()->willReturn('app.shipping_method'); + + $this->add($metadata); + + $this->get('app.shipping_method')->shouldReturn($metadata); + } + + function it_throws_an_exception_if_resource_is_not_registered_with_class(): void + { + $this + ->shouldThrow(\InvalidArgumentException::class) + ->during('getByClass', ['App\Model\OrderItem']) + ; + } + + function it_returns_specific_metadata_by_model_class(MetadataInterface $metadata1, MetadataInterface $metadata2): void + { + $metadata1->getAlias()->willReturn('app.product'); + $metadata1->getClass('model')->willReturn('App\Model\Product'); + + $metadata2->getAlias()->willReturn('app.order'); + $metadata2->getClass('model')->willReturn('App\Model\Order'); + + $this->add($metadata1); + $this->add($metadata2); + + $this->getByClass('App\Model\Order')->shouldReturn($metadata2); + } + + function it_adds_metadata_from_configuration_array(): void + { + $this->addFromAliasAndConfiguration('app.product', [ + 'driver' => 'doctrine/orm', + 'classes' => [ + 'model' => 'App\Model\Product', + ], + ]); + + $this->get('app.product')->shouldHaveType(MetadataInterface::class); + $this->getByClass('App\Model\Product')->shouldHaveType(MetadataInterface::class); + } +} diff --git a/vendor/sylius/resource/spec/Model/AbstractTranslationSpec.php b/vendor/sylius/resource/spec/Model/AbstractTranslationSpec.php new file mode 100644 index 0000000..693dca0 --- /dev/null +++ b/vendor/sylius/resource/spec/Model/AbstractTranslationSpec.php @@ -0,0 +1,61 @@ +beAnInstanceOf('spec\Sylius\Component\Resource\Model\ConcreteTranslation'); + } + + function it_is_a_translation(): void + { + $this->shouldImplement(TranslationInterface::class); + } + + function its_translatable_is_mutable(TranslatableInterface $translatable): void + { + $this->setTranslatable($translatable); + $this->getTranslatable()->shouldReturn($translatable); + } + + function its_detaches_from_its_translatable_correctly( + TranslatableInterface $translatable1, + TranslatableInterface $translatable2 + ): void { + $translatable1->addTranslation(Argument::type(AbstractTranslation::class)); + $this->setTranslatable($translatable1); + + $translatable1->removeTranslation(Argument::type(AbstractTranslation::class)); + $translatable2->addTranslation(Argument::type(AbstractTranslation::class)); + $this->setTranslatable($translatable2); + } + + function its_locale_is_mutable(): void + { + $this->setLocale('en'); + $this->getLocale()->shouldReturn('en'); + } +} + +class ConcreteTranslation extends AbstractTranslation +{ +} diff --git a/vendor/sylius/resource/spec/Repository/Exception/ExistingResourceExceptionSpec.php b/vendor/sylius/resource/spec/Repository/Exception/ExistingResourceExceptionSpec.php new file mode 100644 index 0000000..b41a47c --- /dev/null +++ b/vendor/sylius/resource/spec/Repository/Exception/ExistingResourceExceptionSpec.php @@ -0,0 +1,29 @@ +shouldHaveType(\Exception::class); + } + + function it_has_a_message(): void + { + $this->getMessage()->shouldReturn('Given resource already exists in the repository.'); + } +} diff --git a/vendor/sylius/resource/spec/Repository/InMemoryRepositorySpec.php b/vendor/sylius/resource/spec/Repository/InMemoryRepositorySpec.php new file mode 100644 index 0000000..78eb752 --- /dev/null +++ b/vendor/sylius/resource/spec/Repository/InMemoryRepositorySpec.php @@ -0,0 +1,210 @@ +beConstructedWith(SampleBookResourceInterface::class); + } + + function it_throws_unexpected_type_exception_when_constructing_without_resource_interface(): void + { + $this->beConstructedWith(\stdClass::class); + + $this->shouldThrow(UnexpectedTypeException::class)->duringInstantiation(); + } + + function it_implements_repository_interface(): void + { + $this->shouldImplement(RepositoryInterface::class); + } + + function it_throws_invalid_argument_exception_when_adding_wrong_resource_type(ResourceInterface $resource): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('add', [$resource]); + } + + function it_adds_an_object(SampleBookResourceInterface $monocle): void + { + $monocle->getId()->willReturn(2); + + $this->add($monocle); + $this->findOneBy(['id' => 2])->shouldReturn($monocle); + } + + function it_throws_existing_resource_exception_on_adding_a_resource_which_is_already_in_repository(SampleBookResourceInterface $bike): void + { + $this->add($bike); + $this->shouldThrow(ExistingResourceException::class)->during('add', [$bike]); + } + + function it_removes_a_resource(SampleBookResourceInterface $shirt): void + { + $shirt->getId()->willReturn(5); + + $this->add($shirt); + $this->remove($shirt); + + $this->findOneBy(['id' => 5])->shouldReturn(null); + } + + function it_finds_object_by_id(SampleBookResourceInterface $monocle): void + { + $monocle->getId()->willReturn(2); + + $this->add($monocle); + $this->find(2)->shouldReturn($monocle); + } + + function it_returns_null_if_cannot_find_object_by_id(): void + { + $this->find(2)->shouldReturn(null); + } + + function it_returns_all_objects_when_finding_by_an_empty_parameter_array( + SampleBookResourceInterface $book, + SampleBookResourceInterface $shirt + ): void { + $book->getId()->willReturn(10); + $book->getName()->willReturn('Book'); + + $shirt->getId()->willReturn(5); + $shirt->getName()->willReturn('Shirt'); + + $this->add($book); + $this->add($shirt); + + $this->findBy([])->shouldReturn([$book, $shirt]); + } + + function it_finds_many_objects_by_multiple_criteria_orders_a_limit_and_an_offset( + SampleBookResourceInterface $firstBook, + SampleBookResourceInterface $secondBook, + SampleBookResourceInterface $thirdBook, + SampleBookResourceInterface $fourthBook, + SampleBookResourceInterface $wrongIdBook, + SampleBookResourceInterface $wrongNameBook + ): void { + $id = 80; + $name = 'Book'; + + $firstBook->getId()->willReturn($id); + $secondBook->getId()->willReturn($id); + $thirdBook->getId()->willReturn($id); + $fourthBook->getId()->willReturn($id); + $wrongNameBook->getId()->willReturn($id); + $wrongIdBook->getId()->willReturn(100); + + $firstBook->getName()->willReturn($name); + $secondBook->getName()->willReturn($name); + $thirdBook->getName()->willReturn($name); + $fourthBook->getName()->willReturn($name); + $wrongIdBook->getName()->willReturn($name); + $wrongNameBook->getName()->willReturn('Tome'); + + $firstBook->getRating()->willReturn(3); + $secondBook->getRating()->willReturn(2); + $thirdBook->getRating()->willReturn(2); + $fourthBook->getRating()->willReturn(4); + + $firstBook->getTitle()->willReturn('World War Z'); + $secondBook->getTitle()->willReturn('World War Z'); + $thirdBook->getTitle()->willReturn('Call of Cthulhu'); + $fourthBook->getTitle()->willReturn('Art of War'); + + $this->add($firstBook); + $this->add($secondBook); + $this->add($thirdBook); + $this->add($fourthBook); + $this->add($wrongIdBook); + $this->add($wrongNameBook); + + $this->findBy( + $criteria = [ + 'name' => $name, + 'id' => $id, + ], + $orderBy = [ + 'rating' => RepositoryInterface::ORDER_ASCENDING, + 'title' => RepositoryInterface::ORDER_DESCENDING, + ], + $limit = 2, + $offset = 1 + )->shouldReturn([$secondBook, $thirdBook]); + } + + function it_throws_invalid_argument_exception_when_finding_one_object_with_empty_parameter_array(): void + { + $this->shouldThrow(\InvalidArgumentException::class)->during('findOneBy', [[]]); + } + + function it_finds_one_object_by_parameter(SampleBookResourceInterface $book, SampleBookResourceInterface $shirt): void + { + $book->getName()->willReturn('Book'); + $shirt->getName()->willReturn('Shirt'); + + $this->add($book); + $this->add($shirt); + + $this->findOneBy(['name' => 'Book'])->shouldReturn($book); + } + + function it_returns_first_result_while_finding_one_by_parameters( + SampleBookResourceInterface $book, + SampleBookResourceInterface $secondBook + ): void { + $book->getName()->willReturn('Book'); + $secondBook->getName()->willReturn('Book'); + + $this->add($book); + $this->add($secondBook); + + $this->findOneBy(['name' => 'Book'])->shouldReturn($book); + } + + function it_finds_all_objects_in_memory(SampleBookResourceInterface $book, SampleBookResourceInterface $shirt): void + { + $this->add($book); + $this->add($shirt); + + $this->findAll()->shouldReturn([$book, $shirt]); + } + + function it_return_empty_array_when_memory_is_empty(): void + { + $this->findAll()->shouldReturn([]); + } + + function it_creates_paginator(): void + { + $this->createPaginator()->shouldHaveType(Pagerfanta::class); + } + + function it_returns_stated_class_name(): void + { + $this->getClassName()->shouldReturn(SampleBookResourceInterface::class); + } +} diff --git a/vendor/sylius/resource/spec/Translation/Provider/ImmutableTranslationLocaleProviderSpec.php b/vendor/sylius/resource/spec/Translation/Provider/ImmutableTranslationLocaleProviderSpec.php new file mode 100644 index 0000000..37b8cf1 --- /dev/null +++ b/vendor/sylius/resource/spec/Translation/Provider/ImmutableTranslationLocaleProviderSpec.php @@ -0,0 +1,40 @@ +beConstructedWith(['pl_PL', 'en_US'], 'pl_PL'); + } + + function it_implements_translation_locale_provider_interface(): void + { + $this->shouldImplement(TranslationLocaleProviderInterface::class); + } + + function it_returns_defined_locales_codes(): void + { + $this->getDefinedLocalesCodes()->shouldReturn(['pl_PL', 'en_US']); + } + + function it_returns_the_default_locale_code(): void + { + $this->getDefaultLocaleCode()->shouldReturn('pl_PL'); + } +} diff --git a/vendor/sylius/resource/spec/Translation/TranslatableEntityLocaleAssignerSpec.php b/vendor/sylius/resource/spec/Translation/TranslatableEntityLocaleAssignerSpec.php new file mode 100644 index 0000000..0ef12c1 --- /dev/null +++ b/vendor/sylius/resource/spec/Translation/TranslatableEntityLocaleAssignerSpec.php @@ -0,0 +1,44 @@ +beConstructedWith($translationLocaleProvider); + } + + function it_implements_traslatable_entity_locale_assigner_interface(): void + { + $this->shouldImplement(TranslatableEntityLocaleAssignerInterface::class); + } + + function it_should_assign_current_and_default_locale_to_given_translatable_entity( + TranslationLocaleProviderInterface $translationLocaleProvider, + TranslatableInterface $translatableEntity + ): void { + $translationLocaleProvider->getDefaultLocaleCode()->willReturn('en_US'); + + $translatableEntity->setCurrentLocale('en_US')->shouldBeCalled(); + $translatableEntity->setFallbackLocale('en_US')->shouldBeCalled(); + + $this->assignLocale($translatableEntity); + } +} diff --git a/vendor/symfony/cache/.gitignore b/vendor/symfony/cache/.gitignore new file mode 100644 index 0000000..5414c2c --- /dev/null +++ b/vendor/symfony/cache/.gitignore @@ -0,0 +1,3 @@ +composer.lock +phpunit.xml +vendor/ diff --git a/vendor/symfony/cache/Adapter/AbstractAdapter.php b/vendor/symfony/cache/Adapter/AbstractAdapter.php new file mode 100644 index 0000000..ac6a560 --- /dev/null +++ b/vendor/symfony/cache/Adapter/AbstractAdapter.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\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\AbstractTrait; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface +{ + use AbstractTrait; + + private static $apcuSupported; + private static $phpFilesSupported; + + private $createCacheItem; + private $mergeByLifetime; + + protected function __construct(string $namespace = '', int $defaultLifetime = 0) + { + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; + if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace)); + } + $this->createCacheItem = \Closure::bind( + function ($key, $value, $isHit) use ($defaultLifetime) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + $item->defaultLifetime = $defaultLifetime; + + return $item; + }, + null, + CacheItem::class + ); + $getId = \Closure::fromCallable(array($this, 'getId')); + $this->mergeByLifetime = \Closure::bind( + function ($deferred, $namespace, &$expiredIds) use ($getId) { + $byLifetime = array(); + $now = time(); + $expiredIds = array(); + + foreach ($deferred as $key => $item) { + $key = (string) $key; + if (null === $item->expiry) { + $byLifetime[0 < $item->defaultLifetime ? $item->defaultLifetime : 0][$getId($key)] = $item->value; + } elseif ($item->expiry > $now) { + $byLifetime[$item->expiry - $now][$getId($key)] = $item->value; + } else { + $expiredIds[] = $getId($key); + } + } + + return $byLifetime; + }, + null, + CacheItem::class + ); + } + + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string $version + * @param string $directory + * @param LoggerInterface|null $logger + * + * @return AdapterInterface + */ + public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null) + { + if (null === self::$apcuSupported) { + self::$apcuSupported = ApcuAdapter::isSupported(); + } + + if (!self::$apcuSupported && null === self::$phpFilesSupported) { + self::$phpFilesSupported = PhpFilesAdapter::isSupported(); + } + + if (self::$phpFilesSupported) { + $opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory); + if (null !== $logger) { + $opcache->setLogger($logger); + } + + return $opcache; + } + + $fs = new FilesystemAdapter($namespace, $defaultLifetime, $directory); + if (null !== $logger) { + $fs->setLogger($logger); + } + if (!self::$apcuSupported) { + return $fs; + } + + $apcu = new ApcuAdapter($namespace, (int) $defaultLifetime / 5, $version); + if ('cli' === \PHP_SAPI && !ini_get('apc.enable_cli')) { + $apcu->setLogger(new NullLogger()); + } elseif (null !== $logger) { + $apcu->setLogger($logger); + } + + return new ChainAdapter(array($apcu, $fs)); + } + + public static function createConnection($dsn, array $options = array()) + { + if (!\is_string($dsn)) { + throw new InvalidArgumentException(sprintf('The %s() method expect argument #1 to be string, %s given.', __METHOD__, \gettype($dsn))); + } + if (0 === strpos($dsn, 'redis://')) { + return RedisAdapter::createConnection($dsn, $options); + } + if (0 === strpos($dsn, 'memcached://')) { + return MemcachedAdapter::createConnection($dsn, $options); + } + + throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn)); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + if ($this->deferred) { + $this->commit(); + } + $id = $this->getId($key); + + $f = $this->createCacheItem; + $isHit = false; + $value = null; + + try { + foreach ($this->doFetch(array($id)) as $value) { + $isHit = true; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch key "{key}"', array('key' => $key, 'exception' => $e)); + } + + return $f($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + if ($this->deferred) { + $this->commit(); + } + $ids = array(); + + foreach ($keys as $key) { + $ids[] = $this->getId($key); + } + try { + $items = $this->doFetch($ids); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested items', array('keys' => $keys, 'exception' => $e)); + $items = array(); + } + $ids = array_combine($ids, $keys); + + return $this->generateItems($items, $ids); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return $this->commit(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $ok = true; + $byLifetime = $this->mergeByLifetime; + $byLifetime = $byLifetime($this->deferred, $this->namespace, $expiredIds); + $retry = $this->deferred = array(); + + if ($expiredIds) { + $this->doDelete($expiredIds); + } + foreach ($byLifetime as $lifetime => $values) { + try { + $e = $this->doSave($values, $lifetime); + } catch (\Exception $e) { + } + if (true === $e || array() === $e) { + continue; + } + if (\is_array($e) || 1 === \count($values)) { + foreach (\is_array($e) ? $e : array_keys($values) as $id) { + $ok = false; + $v = $values[$id]; + $type = \is_object($v) ? \get_class($v) : \gettype($v); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => substr($id, \strlen($this->namespace)), 'type' => $type, 'exception' => $e instanceof \Exception ? $e : null)); + } + } else { + foreach ($values as $id => $v) { + $retry[$lifetime][] = $id; + } + } + } + + // When bulk-save failed, retry each item individually + foreach ($retry as $lifetime => $ids) { + foreach ($ids as $id) { + try { + $v = $byLifetime[$lifetime][$id]; + $e = $this->doSave(array($id => $v), $lifetime); + } catch (\Exception $e) { + } + if (true === $e || array() === $e) { + continue; + } + $ok = false; + $type = \is_object($v) ? \get_class($v) : \gettype($v); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => substr($id, \strlen($this->namespace)), 'type' => $type, 'exception' => $e instanceof \Exception ? $e : null)); + } + } + + return $ok; + } + + public function __destruct() + { + if ($this->deferred) { + $this->commit(); + } + } + + private function generateItems($items, &$keys) + { + $f = $this->createCacheItem; + + try { + foreach ($items as $id => $value) { + if (!isset($keys[$id])) { + $id = key($keys); + } + $key = $keys[$id]; + unset($keys[$id]); + yield $key => $f($key, $value, true); + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested items', array('keys' => array_values($keys), 'exception' => $e)); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } +} diff --git a/vendor/symfony/cache/Adapter/AdapterInterface.php b/vendor/symfony/cache/Adapter/AdapterInterface.php new file mode 100644 index 0000000..41222c1 --- /dev/null +++ b/vendor/symfony/cache/Adapter/AdapterInterface.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\Cache\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; + +/** + * Interface for adapters managing instances of Symfony's CacheItem. + * + * @author Kévin Dunglas + */ +interface AdapterInterface extends CacheItemPoolInterface +{ + /** + * {@inheritdoc} + * + * @return CacheItem + */ + public function getItem($key); + + /** + * {@inheritdoc} + * + * @return \Traversable|CacheItem[] + */ + public function getItems(array $keys = array()); +} diff --git a/vendor/symfony/cache/Adapter/ApcuAdapter.php b/vendor/symfony/cache/Adapter/ApcuAdapter.php new file mode 100644 index 0000000..7db3956 --- /dev/null +++ b/vendor/symfony/cache/Adapter/ApcuAdapter.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\Cache\Adapter; + +use Symfony\Component\Cache\Traits\ApcuTrait; + +class ApcuAdapter extends AbstractAdapter +{ + use ApcuTrait; + + /** + * @throws CacheException if APCu is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null) + { + $this->init($namespace, $defaultLifetime, $version); + } +} diff --git a/vendor/symfony/cache/Adapter/ArrayAdapter.php b/vendor/symfony/cache/Adapter/ArrayAdapter.php new file mode 100644 index 0000000..830d6dd --- /dev/null +++ b/vendor/symfony/cache/Adapter/ArrayAdapter.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\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerAwareInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ArrayTrait; + +/** + * @author Nicolas Grekas + */ +class ArrayAdapter implements AdapterInterface, LoggerAwareInterface, ResettableInterface +{ + use ArrayTrait; + + private $createCacheItem; + + /** + * @param int $defaultLifetime + * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise + */ + public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true) + { + $this->storeSerialized = $storeSerialized; + $this->createCacheItem = \Closure::bind( + function ($key, $value, $isHit) use ($defaultLifetime) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + $item->defaultLifetime = $defaultLifetime; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $isHit = $this->hasItem($key); + try { + if (!$isHit) { + $this->values[$key] = $value = null; + } elseif (!$this->storeSerialized) { + $value = $this->values[$key]; + } elseif ('b:0;' === $value = $this->values[$key]) { + $value = false; + } elseif (false === $value = unserialize($value)) { + $this->values[$key] = $value = null; + $isHit = false; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e)); + $this->values[$key] = $value = null; + $isHit = false; + } + $f = $this->createCacheItem; + + return $f($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + foreach ($keys as $key) { + CacheItem::validateKey($key); + } + + return $this->generateItems($keys, time(), $this->createCacheItem); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + foreach ($keys as $key) { + $this->deleteItem($key); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $item = (array) $item; + $key = $item["\0*\0key"]; + $value = $item["\0*\0value"]; + $expiry = $item["\0*\0expiry"]; + + if (null !== $expiry && $expiry <= time()) { + $this->deleteItem($key); + + return true; + } + if ($this->storeSerialized) { + try { + $value = serialize($value); + } catch (\Exception $e) { + $type = \is_object($value) ? \get_class($value) : \gettype($value); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e)); + + return false; + } + } + if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { + $expiry = time() + $item["\0*\0defaultLifetime"]; + } + + $this->values[$key] = $value; + $this->expiries[$key] = null !== $expiry ? $expiry : PHP_INT_MAX; + + return true; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->save($item); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return true; + } +} diff --git a/vendor/symfony/cache/Adapter/ChainAdapter.php b/vendor/symfony/cache/Adapter/ChainAdapter.php new file mode 100644 index 0000000..db0da4f --- /dev/null +++ b/vendor/symfony/cache/Adapter/ChainAdapter.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\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * Chains several adapters together. + * + * Cached items are fetched from the first adapter having them in its data store. + * They are saved and deleted in all adapters at once. + * + * @author Kévin Dunglas + */ +class ChainAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + private $adapters = array(); + private $adapterCount; + private $syncItem; + + /** + * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items + * @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones + */ + public function __construct(array $adapters, int $defaultLifetime = 0) + { + if (!$adapters) { + throw new InvalidArgumentException('At least one adapter must be specified.'); + } + + foreach ($adapters as $adapter) { + if (!$adapter instanceof CacheItemPoolInterface) { + throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($adapter), CacheItemPoolInterface::class)); + } + + if ($adapter instanceof AdapterInterface) { + $this->adapters[] = $adapter; + } else { + $this->adapters[] = new ProxyAdapter($adapter); + } + } + $this->adapterCount = \count($this->adapters); + + $this->syncItem = \Closure::bind( + function ($sourceItem, $item) use ($defaultLifetime) { + $item->value = $sourceItem->value; + $item->expiry = $sourceItem->expiry; + $item->isHit = $sourceItem->isHit; + + if (0 < $sourceItem->defaultLifetime && $sourceItem->defaultLifetime < $defaultLifetime) { + $defaultLifetime = $sourceItem->defaultLifetime; + } + if (0 < $defaultLifetime && ($item->defaultLifetime <= 0 || $defaultLifetime < $item->defaultLifetime)) { + $item->defaultLifetime = $defaultLifetime; + } + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $syncItem = $this->syncItem; + $misses = array(); + + foreach ($this->adapters as $i => $adapter) { + $item = $adapter->getItem($key); + + if ($item->isHit()) { + while (0 <= --$i) { + $this->adapters[$i]->save($syncItem($item, $misses[$i])); + } + + return $item; + } + + $misses[$i] = $item; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + return $this->generateItems($this->adapters[0]->getItems($keys), 0); + } + + private function generateItems($items, $adapterIndex) + { + $missing = array(); + $misses = array(); + $nextAdapterIndex = $adapterIndex + 1; + $nextAdapter = isset($this->adapters[$nextAdapterIndex]) ? $this->adapters[$nextAdapterIndex] : null; + + foreach ($items as $k => $item) { + if (!$nextAdapter || $item->isHit()) { + yield $k => $item; + } else { + $missing[] = $k; + $misses[$k] = $item; + } + } + + if ($missing) { + $syncItem = $this->syncItem; + $adapter = $this->adapters[$adapterIndex]; + $items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex); + + foreach ($items as $k => $item) { + if ($item->isHit()) { + $adapter->save($syncItem($item, $misses[$k])); + } + + yield $k => $item; + } + } + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + foreach ($this->adapters as $adapter) { + if ($adapter->hasItem($key)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $cleared = true; + $i = $this->adapterCount; + + while ($i--) { + $cleared = $this->adapters[$i]->clear() && $cleared; + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + $deleted = true; + $i = $this->adapterCount; + + while ($i--) { + $deleted = $this->adapters[$i]->deleteItem($key) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $deleted = true; + $i = $this->adapterCount; + + while ($i--) { + $deleted = $this->adapters[$i]->deleteItems($keys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + $saved = true; + $i = $this->adapterCount; + + while ($i--) { + $saved = $this->adapters[$i]->save($item) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + $saved = true; + $i = $this->adapterCount; + + while ($i--) { + $saved = $this->adapters[$i]->saveDeferred($item) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $committed = true; + $i = $this->adapterCount; + + while ($i--) { + $committed = $this->adapters[$i]->commit() && $committed; + } + + return $committed; + } + + /** + * {@inheritdoc} + */ + public function prune() + { + $pruned = true; + + foreach ($this->adapters as $adapter) { + if ($adapter instanceof PruneableInterface) { + $pruned = $adapter->prune() && $pruned; + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + foreach ($this->adapters as $adapter) { + if ($adapter instanceof ResettableInterface) { + $adapter->reset(); + } + } + } +} diff --git a/vendor/symfony/cache/Adapter/DoctrineAdapter.php b/vendor/symfony/cache/Adapter/DoctrineAdapter.php new file mode 100644 index 0000000..75ae4cb --- /dev/null +++ b/vendor/symfony/cache/Adapter/DoctrineAdapter.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\Cache\Adapter; + +use Doctrine\Common\Cache\CacheProvider; +use Symfony\Component\Cache\Traits\DoctrineTrait; + +class DoctrineAdapter extends AbstractAdapter +{ + use DoctrineTrait; + + public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0) + { + parent::__construct('', $defaultLifetime); + $this->provider = $provider; + $provider->setNamespace($namespace); + } +} diff --git a/vendor/symfony/cache/Adapter/FilesystemAdapter.php b/vendor/symfony/cache/Adapter/FilesystemAdapter.php new file mode 100644 index 0000000..a088883 --- /dev/null +++ b/vendor/symfony/cache/Adapter/FilesystemAdapter.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\Cache\Adapter; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemTrait; + +class FilesystemAdapter extends AbstractAdapter implements PruneableInterface +{ + use FilesystemTrait; + + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) + { + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } +} diff --git a/vendor/symfony/cache/Adapter/MemcachedAdapter.php b/vendor/symfony/cache/Adapter/MemcachedAdapter.php new file mode 100644 index 0000000..65ab9ed --- /dev/null +++ b/vendor/symfony/cache/Adapter/MemcachedAdapter.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\Cache\Adapter; + +use Symfony\Component\Cache\Traits\MemcachedTrait; + +class MemcachedAdapter extends AbstractAdapter +{ + use MemcachedTrait; + + protected $maxIdLength = 250; + + /** + * Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged. + * Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that: + * - the Memcached::OPT_BINARY_PROTOCOL must be enabled + * (that's the default when using MemcachedAdapter::createConnection()); + * - tags eviction by Memcached's LRU algorithm will break by-tags invalidation; + * your Memcached memory should be large enough to never trigger LRU. + * + * Using a MemcachedAdapter as a pure items store is fine. + */ + public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0) + { + $this->init($client, $namespace, $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Adapter/NullAdapter.php b/vendor/symfony/cache/Adapter/NullAdapter.php new file mode 100644 index 0000000..f58f81e --- /dev/null +++ b/vendor/symfony/cache/Adapter/NullAdapter.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\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Titouan Galopin + */ +class NullAdapter implements AdapterInterface +{ + private $createCacheItem; + + public function __construct() + { + $this->createCacheItem = \Closure::bind( + function ($key) { + $item = new CacheItem(); + $item->key = $key; + $item->isHit = false; + + return $item; + }, + $this, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $f = $this->createCacheItem; + + return $f($key); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + return $this->generateItems($keys); + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return false; + } + + private function generateItems(array $keys) + { + $f = $this->createCacheItem; + + foreach ($keys as $key) { + yield $key => $f($key); + } + } +} diff --git a/vendor/symfony/cache/Adapter/PdoAdapter.php b/vendor/symfony/cache/Adapter/PdoAdapter.php new file mode 100644 index 0000000..32f7be8 --- /dev/null +++ b/vendor/symfony/cache/Adapter/PdoAdapter.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\Cache\Adapter; + +use Doctrine\DBAL\Connection; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PdoTrait; + +class PdoAdapter extends AbstractAdapter implements PruneableInterface +{ + use PdoTrait; + + protected $maxIdLength = 255; + + /** + * You can either pass an existing database connection as PDO instance or + * a Doctrine DBAL Connection or a DSN string that will be used to + * lazy-connect to the database when the cache is actually used. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_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()] + * + * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null + * + * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string + * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = array()) + { + $this->init($connOrDsn, $namespace, $defaultLifetime, $options); + } +} diff --git a/vendor/symfony/cache/Adapter/PhpArrayAdapter.php b/vendor/symfony/cache/Adapter/PhpArrayAdapter.php new file mode 100644 index 0000000..f22f853 --- /dev/null +++ b/vendor/symfony/cache/Adapter/PhpArrayAdapter.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\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\PhpArrayTrait; + +/** + * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. + * Warmed up items are read-only and run-time discovered items are cached using a fallback adapter. + * + * @author Titouan Galopin + * @author Nicolas Grekas + */ +class PhpArrayAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + use PhpArrayTrait; + + private $createCacheItem; + + /** + * @param string $file The PHP file were values are cached + * @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit + */ + public function __construct(string $file, AdapterInterface $fallbackPool) + { + $this->file = $file; + $this->pool = $fallbackPool; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); + $this->createCacheItem = \Closure::bind( + function ($key, $value, $isHit) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * This adapter takes advantage of how PHP stores arrays in its latest versions. + * + * @param string $file The PHP file were values are cached + * @param CacheItemPoolInterface $fallbackPool Fallback when opcache is disabled + * + * @return CacheItemPoolInterface + */ + public static function create($file, CacheItemPoolInterface $fallbackPool) + { + // Shared memory is available in PHP 7.0+ with OPCache enabled + if (ini_get('opcache.enable')) { + if (!$fallbackPool instanceof AdapterInterface) { + $fallbackPool = new ProxyAdapter($fallbackPool); + } + + return new static($file, $fallbackPool); + } + + return $fallbackPool; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + if (!isset($this->values[$key])) { + return $this->pool->getItem($key); + } + + $value = $this->values[$key]; + $isHit = true; + + if ('N;' === $value) { + $value = null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + $value = unserialize($value); + } catch (\Throwable $e) { + $value = null; + $isHit = false; + } + } + + $f = $this->createCacheItem; + + return $f($key, $value, $isHit); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + } + if (null === $this->values) { + $this->initialize(); + } + + return $this->generateItems($keys); + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return isset($this->values[$key]) || $this->pool->hasItem($key); + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$key]) && $this->pool->deleteItem($key); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $deleted = true; + $fallbackKeys = array(); + + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + + if (isset($this->values[$key])) { + $deleted = false; + } else { + $fallbackKeys[] = $key; + } + } + if (null === $this->values) { + $this->initialize(); + } + + if ($fallbackKeys) { + $deleted = $this->pool->deleteItems($fallbackKeys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$item->getKey()]) && $this->pool->save($item); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$item->getKey()]) && $this->pool->saveDeferred($item); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->pool->commit(); + } + + private function generateItems(array $keys): \Generator + { + $f = $this->createCacheItem; + $fallbackKeys = array(); + + foreach ($keys as $key) { + if (isset($this->values[$key])) { + $value = $this->values[$key]; + + if ('N;' === $value) { + yield $key => $f($key, null, true); + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + yield $key => $f($key, unserialize($value), true); + } catch (\Throwable $e) { + yield $key => $f($key, null, false); + } + } else { + yield $key => $f($key, $value, true); + } + } else { + $fallbackKeys[] = $key; + } + } + + if ($fallbackKeys) { + foreach ($this->pool->getItems($fallbackKeys) as $key => $item) { + yield $key => $item; + } + } + } + + /** + * @throws \ReflectionException When $class is not found and is required + * + * @internal + */ + public static function throwOnRequiredClass($class) + { + $e = new \ReflectionException("Class $class does not exist"); + $trace = $e->getTrace(); + $autoloadFrame = array( + 'function' => 'spl_autoload_call', + 'args' => array($class), + ); + $i = 1 + array_search($autoloadFrame, $trace, true); + + if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) { + switch ($trace[$i]['function']) { + case 'get_class_methods': + case 'get_class_vars': + case 'get_parent_class': + case 'is_a': + case 'is_subclass_of': + case 'class_exists': + case 'class_implements': + case 'class_parents': + case 'trait_exists': + case 'defined': + case 'interface_exists': + case 'method_exists': + case 'property_exists': + case 'is_callable': + return; + } + } + + throw $e; + } +} diff --git a/vendor/symfony/cache/Adapter/PhpFilesAdapter.php b/vendor/symfony/cache/Adapter/PhpFilesAdapter.php new file mode 100644 index 0000000..41879df --- /dev/null +++ b/vendor/symfony/cache/Adapter/PhpFilesAdapter.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\Cache\Adapter; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PhpFilesTrait; + +class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface +{ + use PhpFilesTrait; + + /** + * @throws CacheException if OPcache is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) + { + if (!static::isSupported()) { + throw new CacheException('OPcache is not enabled'); + } + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + + $e = new \Exception(); + $this->includeHandler = function () use ($e) { throw $e; }; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); + } +} diff --git a/vendor/symfony/cache/Adapter/ProxyAdapter.php b/vendor/symfony/cache/Adapter/ProxyAdapter.php new file mode 100644 index 0000000..0d4fcc4 --- /dev/null +++ b/vendor/symfony/cache/Adapter/ProxyAdapter.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\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class ProxyAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + private $namespace; + private $namespaceLen; + private $createCacheItem; + private $poolHash; + + public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0) + { + $this->pool = $pool; + $this->poolHash = $poolHash = spl_object_hash($pool); + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace); + $this->namespaceLen = \strlen($namespace); + $this->createCacheItem = \Closure::bind( + function ($key, $innerItem) use ($defaultLifetime, $poolHash) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $innerItem->get(); + $item->isHit = $innerItem->isHit(); + $item->defaultLifetime = $defaultLifetime; + $item->innerItem = $innerItem; + $item->poolHash = $poolHash; + $innerItem->set(null); + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $f = $this->createCacheItem; + $item = $this->pool->getItem($this->getId($key)); + + return $f($key, $item); + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + + return $this->generateItems($this->pool->getItems($keys)); + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + return $this->pool->hasItem($this->getId($key)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->pool->deleteItem($this->getId($key)); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + if ($this->namespaceLen) { + foreach ($keys as $i => $key) { + $keys[$i] = $this->getId($key); + } + } + + return $this->pool->deleteItems($keys); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + return $this->doSave($item, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + return $this->doSave($item, __FUNCTION__); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->pool->commit(); + } + + private function doSave(CacheItemInterface $item, $method) + { + if (!$item instanceof CacheItem) { + return false; + } + $item = (array) $item; + $expiry = $item["\0*\0expiry"]; + if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { + $expiry = time() + $item["\0*\0defaultLifetime"]; + } + $innerItem = $item["\0*\0poolHash"] === $this->poolHash ? $item["\0*\0innerItem"] : $this->pool->getItem($this->namespace.$item["\0*\0key"]); + $innerItem->set($item["\0*\0value"]); + $innerItem->expiresAt(null !== $expiry ? \DateTime::createFromFormat('U', $expiry) : null); + + return $this->pool->$method($innerItem); + } + + private function generateItems($items) + { + $f = $this->createCacheItem; + + foreach ($items as $key => $item) { + if ($this->namespaceLen) { + $key = substr($key, $this->namespaceLen); + } + + yield $key => $f($key, $item); + } + } + + private function getId($key) + { + CacheItem::validateKey($key); + + return $this->namespace.$key; + } +} diff --git a/vendor/symfony/cache/Adapter/RedisAdapter.php b/vendor/symfony/cache/Adapter/RedisAdapter.php new file mode 100644 index 0000000..0bb76fc --- /dev/null +++ b/vendor/symfony/cache/Adapter/RedisAdapter.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\Cache\Adapter; + +use Symfony\Component\Cache\Traits\RedisTrait; + +class RedisAdapter extends AbstractAdapter +{ + use RedisTrait; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient The redis client + * @param string $namespace The default namespace + * @param int $defaultLifetime The default lifetime + */ + public function __construct($redisClient, string $namespace = '', int $defaultLifetime = 0) + { + $this->init($redisClient, $namespace, $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Adapter/SimpleCacheAdapter.php b/vendor/symfony/cache/Adapter/SimpleCacheAdapter.php new file mode 100644 index 0000000..2e6d03a --- /dev/null +++ b/vendor/symfony/cache/Adapter/SimpleCacheAdapter.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\Cache\Adapter; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + private $miss; + + public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0) + { + parent::__construct($namespace, $defaultLifetime); + + $this->pool = $pool; + $this->miss = new \stdClass(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) { + if ($this->miss !== $value) { + yield $key => $value; + } + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return $this->pool->has($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + return $this->pool->deleteMultiple($ids); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime); + } +} diff --git a/vendor/symfony/cache/Adapter/TagAwareAdapter.php b/vendor/symfony/cache/Adapter/TagAwareAdapter.php new file mode 100644 index 0000000..9110511 --- /dev/null +++ b/vendor/symfony/cache/Adapter/TagAwareAdapter.php @@ -0,0 +1,374 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\InvalidArgumentException; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class TagAwareAdapter implements TagAwareAdapterInterface, PruneableInterface, ResettableInterface +{ + const TAGS_PREFIX = "\0tags\0"; + + use ProxyTrait; + + private $deferred = array(); + private $createCacheItem; + private $setCacheItemTags; + private $getTagsByKey; + private $invalidateTags; + private $tags; + private $knownTagVersions = array(); + private $knownTagVersionsTtl; + + public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15) + { + $this->pool = $itemsPool; + $this->tags = $tagsPool ?: $itemsPool; + $this->knownTagVersionsTtl = $knownTagVersionsTtl; + $this->createCacheItem = \Closure::bind( + function ($key, $value, CacheItem $protoItem) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->defaultLifetime = $protoItem->defaultLifetime; + $item->expiry = $protoItem->expiry; + $item->innerItem = $protoItem->innerItem; + $item->poolHash = $protoItem->poolHash; + + return $item; + }, + null, + CacheItem::class + ); + $this->setCacheItemTags = \Closure::bind( + function (CacheItem $item, $key, array &$itemTags) { + if (!$item->isHit) { + return $item; + } + if (isset($itemTags[$key])) { + foreach ($itemTags[$key] as $tag => $version) { + $item->prevTags[$tag] = $tag; + } + unset($itemTags[$key]); + } else { + $item->value = null; + $item->isHit = false; + } + + return $item; + }, + null, + CacheItem::class + ); + $this->getTagsByKey = \Closure::bind( + function ($deferred) { + $tagsByKey = array(); + foreach ($deferred as $key => $item) { + $tagsByKey[$key] = $item->tags; + } + + return $tagsByKey; + }, + null, + CacheItem::class + ); + $this->invalidateTags = \Closure::bind( + function (AdapterInterface $tagsAdapter, array $tags) { + foreach ($tags as $v) { + $v->defaultLifetime = 0; + $v->expiry = null; + $tagsAdapter->saveDeferred($v); + } + + return $tagsAdapter->commit(); + }, + null, + CacheItem::class + ); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + $ok = true; + $tagsByKey = array(); + $invalidatedTags = array(); + foreach ($tags as $tag) { + CacheItem::validateKey($tag); + $invalidatedTags[$tag] = 0; + } + + if ($this->deferred) { + $items = $this->deferred; + foreach ($items as $key => $item) { + if (!$this->pool->saveDeferred($item)) { + unset($this->deferred[$key]); + $ok = false; + } + } + + $f = $this->getTagsByKey; + $tagsByKey = $f($items); + $this->deferred = array(); + } + + $tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags); + $f = $this->createCacheItem; + + foreach ($tagsByKey as $key => $tags) { + $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key])); + } + $ok = $this->pool->commit() && $ok; + + if ($invalidatedTags) { + $f = $this->invalidateTags; + $ok = $f($this->tags, $invalidatedTags) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + if ($this->deferred) { + $this->commit(); + } + if (!$this->pool->hasItem($key)) { + return false; + } + if (!$itemTags = $this->pool->getItem(static::TAGS_PREFIX.$key)->get()) { + return true; + } + + foreach ($this->getTagVersions(array($itemTags)) as $tag => $version) { + if ($itemTags[$tag] !== $version && 1 !== $itemTags[$tag] - $version) { + return false; + } + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + foreach ($this->getItems(array($key)) as $item) { + return $item; + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + if ($this->deferred) { + $this->commit(); + } + $tagKeys = array(); + + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $key = static::TAGS_PREFIX.$key; + $tagKeys[$key] = $key; + } + } + + try { + $items = $this->pool->getItems($tagKeys + $keys); + } catch (InvalidArgumentException $e) { + $this->pool->getItems($keys); // Should throw an exception + + throw $e; + } + + return $this->generateItems($items, $tagKeys); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->deferred = array(); + + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->deleteItems(array($key)); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + foreach ($keys as $key) { + if ('' !== $key && \is_string($key)) { + $keys[] = static::TAGS_PREFIX.$key; + } + } + + return $this->pool->deleteItems($keys); + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return $this->commit(); + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + if (!$item instanceof CacheItem) { + return false; + } + $this->deferred[$item->getKey()] = $item; + + return true; + } + + /** + * {@inheritdoc} + */ + public function commit() + { + return $this->invalidateTags(array()); + } + + public function __destruct() + { + $this->commit(); + } + + private function generateItems($items, array $tagKeys) + { + $bufferedItems = $itemTags = array(); + $f = $this->setCacheItemTags; + + foreach ($items as $key => $item) { + if (!$tagKeys) { + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); + continue; + } + if (!isset($tagKeys[$key])) { + $bufferedItems[$key] = $item; + continue; + } + + unset($tagKeys[$key]); + $itemTags[$key] = $item->get() ?: array(); + + if (!$tagKeys) { + $tagVersions = $this->getTagVersions($itemTags); + + foreach ($itemTags as $key => $tags) { + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] !== $version && 1 !== $version - $tagVersions[$tag]) { + unset($itemTags[$key]); + continue 2; + } + } + } + $tagVersions = $tagKeys = null; + + foreach ($bufferedItems as $key => $item) { + yield $key => $f($item, static::TAGS_PREFIX.$key, $itemTags); + } + $bufferedItems = null; + } + } + } + + private function getTagVersions(array $tagsByKey, array &$invalidatedTags = array()) + { + $tagVersions = $invalidatedTags; + + foreach ($tagsByKey as $tags) { + $tagVersions += $tags; + } + + if (!$tagVersions) { + return array(); + } + + if (!$fetchTagVersions = 1 !== \func_num_args()) { + foreach ($tagsByKey as $tags) { + foreach ($tags as $tag => $version) { + if ($tagVersions[$tag] > $version) { + $tagVersions[$tag] = $version; + } + } + } + } + + $now = microtime(true); + $tags = array(); + foreach ($tagVersions as $tag => $version) { + $tags[$tag.static::TAGS_PREFIX] = $tag; + if ($fetchTagVersions || !isset($this->knownTagVersions[$tag])) { + $fetchTagVersions = true; + continue; + } + $version -= $this->knownTagVersions[$tag][1]; + if ((0 !== $version && 1 !== $version) || $this->knownTagVersionsTtl > $now - $this->knownTagVersions[$tag][0]) { + // reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises + $fetchTagVersions = true; + } else { + $this->knownTagVersions[$tag][1] += $version; + } + } + + if (!$fetchTagVersions) { + return $tagVersions; + } + + foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) { + $tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0; + if (isset($invalidatedTags[$tag])) { + $invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]); + } + $this->knownTagVersions[$tag] = array($now, $tagVersions[$tag]); + } + + return $tagVersions; + } +} diff --git a/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php b/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php new file mode 100644 index 0000000..340048c --- /dev/null +++ b/vendor/symfony/cache/Adapter/TagAwareAdapterInterface.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\Cache\Adapter; + +use Psr\Cache\InvalidArgumentException; + +/** + * Interface for invalidating cached items using tags. + * + * @author Nicolas Grekas + */ +interface TagAwareAdapterInterface extends AdapterInterface +{ + /** + * Invalidates cached items using tags. + * + * @param string[] $tags An array of tags to invalidate + * + * @return bool True on success + * + * @throws InvalidArgumentException When $tags is not valid + */ + public function invalidateTags(array $tags); +} diff --git a/vendor/symfony/cache/Adapter/TraceableAdapter.php b/vendor/symfony/cache/Adapter/TraceableAdapter.php new file mode 100644 index 0000000..98d0e52 --- /dev/null +++ b/vendor/symfony/cache/Adapter/TraceableAdapter.php @@ -0,0 +1,233 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Aaron Scherer + * @author Tobias Nyholm + * @author Nicolas Grekas + */ +class TraceableAdapter implements AdapterInterface, PruneableInterface, ResettableInterface +{ + protected $pool; + private $calls = array(); + + public function __construct(AdapterInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $event = $this->start(__FUNCTION__); + try { + $item = $this->pool->getItem($key); + } finally { + $event->end = microtime(true); + } + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + + return $item; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->hasItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->save($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$item->getKey()] = $this->pool->saveDeferred($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + $event = $this->start(__FUNCTION__); + try { + $result = $this->pool->getItems($keys); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event) { + $event->result = array(); + foreach ($result as $key => $item) { + if ($event->result[$key] = $item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + yield $key => $item; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $event = $this->start(__FUNCTION__); + $event->result['keys'] = $keys; + try { + return $event->result['result'] = $this->pool->deleteItems($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->commit(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function prune() + { + if (!$this->pool instanceof PruneableInterface) { + return false; + } + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->prune(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if (!$this->pool instanceof ResettableInterface) { + return; + } + $event = $this->start(__FUNCTION__); + try { + $this->pool->reset(); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + return $this->calls; + } + + public function clearCalls() + { + $this->calls = array(); + } + + protected function start($name) + { + $this->calls[] = $event = new TraceableAdapterEvent(); + $event->name = $name; + $event->start = microtime(true); + + return $event; + } +} + +class TraceableAdapterEvent +{ + public $name; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php b/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php new file mode 100644 index 0000000..de68955 --- /dev/null +++ b/vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.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\Cache\Adapter; + +/** + * @author Robin Chalas + */ +class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface +{ + public function __construct(TagAwareAdapterInterface $pool) + { + parent::__construct($pool); + } + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $tags) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->invalidateTags($tags); + } finally { + $event->end = microtime(true); + } + } +} diff --git a/vendor/symfony/cache/CHANGELOG.md b/vendor/symfony/cache/CHANGELOG.md new file mode 100644 index 0000000..11c1b93 --- /dev/null +++ b/vendor/symfony/cache/CHANGELOG.md @@ -0,0 +1,36 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added using options from Memcached DSN + * added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning + * added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, TagAwareAdapter and ChainTrait + * now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and + ChainCache implement PruneableInterface and support manual stale cache pruning + +3.3.0 +----- + + * [EXPERIMENTAL] added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any + * added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters + * added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16 + * added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16) + * added TraceableAdapter (PSR-6) and TraceableCache (PSR-16) + +3.2.0 +----- + + * added TagAwareAdapter for tags-based invalidation + * added PdoAdapter with PDO and Doctrine DBAL support + * added PhpArrayAdapter and PhpFilesAdapter for OPcache-backed shared memory storage (PHP 7+ only) + * added NullAdapter + +3.1.0 +----- + + * added the component with strict PSR-6 implementations + * added ApcuAdapter, ArrayAdapter, FilesystemAdapter and RedisAdapter + * added AbstractAdapter, ChainAdapter and ProxyAdapter + * added DoctrineAdapter and DoctrineProvider for bidirectional interoperability with Doctrine Cache diff --git a/vendor/symfony/cache/CacheItem.php b/vendor/symfony/cache/CacheItem.php new file mode 100644 index 0000000..58ecad8 --- /dev/null +++ b/vendor/symfony/cache/CacheItem.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Psr\Cache\CacheItemInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + */ +final class CacheItem implements CacheItemInterface +{ + protected $key; + protected $value; + protected $isHit = false; + protected $expiry; + protected $defaultLifetime; + protected $tags = array(); + protected $prevTags = array(); + protected $innerItem; + protected $poolHash; + + /** + * {@inheritdoc} + */ + public function getKey() + { + return $this->key; + } + + /** + * {@inheritdoc} + */ + public function get() + { + return $this->value; + } + + /** + * {@inheritdoc} + */ + public function isHit() + { + return $this->isHit; + } + + /** + * {@inheritdoc} + */ + public function set($value) + { + $this->value = $value; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function expiresAt($expiration) + { + if (null === $expiration) { + $this->expiry = $this->defaultLifetime > 0 ? time() + $this->defaultLifetime : null; + } elseif ($expiration instanceof \DateTimeInterface) { + $this->expiry = (int) $expiration->format('U'); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given', \is_object($expiration) ? \get_class($expiration) : \gettype($expiration))); + } + + return $this; + } + + /** + * {@inheritdoc} + */ + public function expiresAfter($time) + { + if (null === $time) { + $this->expiry = $this->defaultLifetime > 0 ? time() + $this->defaultLifetime : null; + } elseif ($time instanceof \DateInterval) { + $this->expiry = (int) \DateTime::createFromFormat('U', time())->add($time)->format('U'); + } elseif (\is_int($time)) { + $this->expiry = $time + time(); + } else { + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($time) ? \get_class($time) : \gettype($time))); + } + + return $this; + } + + /** + * Adds a tag to a cache item. + * + * @param string|string[] $tags A tag or array of tags + * + * @return static + * + * @throws InvalidArgumentException When $tag is not valid + */ + public function tag($tags) + { + if (!\is_array($tags)) { + $tags = array($tags); + } + foreach ($tags as $tag) { + if (!\is_string($tag)) { + throw new InvalidArgumentException(sprintf('Cache tag must be string, "%s" given', \is_object($tag) ? \get_class($tag) : \gettype($tag))); + } + if (isset($this->tags[$tag])) { + continue; + } + if ('' === $tag) { + throw new InvalidArgumentException('Cache tag length must be greater than zero'); + } + if (false !== strpbrk($tag, '{}()/\@:')) { + throw new InvalidArgumentException(sprintf('Cache tag "%s" contains reserved characters {}()/\@:', $tag)); + } + $this->tags[$tag] = $tag; + } + + return $this; + } + + /** + * Returns the list of tags bound to the value coming from the pool storage if any. + * + * @return array + */ + public function getPreviousTags() + { + return $this->prevTags; + } + + /** + * Validates a cache key according to PSR-6. + * + * @param string $key The key to validate + * + * @return string + * + * @throws InvalidArgumentException When $key is not valid + */ + public static function validateKey($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if ('' === $key) { + throw new InvalidArgumentException('Cache key length must be greater than zero'); + } + if (false !== strpbrk($key, '{}()/\@:')) { + throw new InvalidArgumentException(sprintf('Cache key "%s" contains reserved characters {}()/\@:', $key)); + } + + return $key; + } + + /** + * Internal logging helper. + * + * @internal + */ + public static function log(LoggerInterface $logger = null, $message, $context = array()) + { + if ($logger) { + $logger->warning($message, $context); + } else { + $replace = array(); + foreach ($context as $k => $v) { + if (is_scalar($v)) { + $replace['{'.$k.'}'] = $v; + } + } + @trigger_error(strtr($message, $replace), E_USER_WARNING); + } + } +} diff --git a/vendor/symfony/cache/DataCollector/CacheDataCollector.php b/vendor/symfony/cache/DataCollector/CacheDataCollector.php new file mode 100644 index 0000000..91763e5 --- /dev/null +++ b/vendor/symfony/cache/DataCollector/CacheDataCollector.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\Cache\DataCollector; + +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapterEvent; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollector; +use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; + +/** + * @author Aaron Scherer + * @author Tobias Nyholm + */ +class CacheDataCollector extends DataCollector implements LateDataCollectorInterface +{ + /** + * @var TraceableAdapter[] + */ + private $instances = array(); + + /** + * @param string $name + * @param TraceableAdapter $instance + */ + public function addInstance($name, TraceableAdapter $instance) + { + $this->instances[$name] = $instance; + } + + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + $empty = array('calls' => array(), 'config' => array(), 'options' => array(), 'statistics' => array()); + $this->data = array('instances' => $empty, 'total' => $empty); + foreach ($this->instances as $name => $instance) { + $this->data['instances']['calls'][$name] = $instance->getCalls(); + } + + $this->data['instances']['statistics'] = $this->calculateStatistics(); + $this->data['total']['statistics'] = $this->calculateTotalStatistics(); + } + + public function reset() + { + $this->data = array(); + foreach ($this->instances as $instance) { + $instance->clearCalls(); + } + } + + public function lateCollect() + { + $this->data = $this->cloneVar($this->data); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'cache'; + } + + /** + * Method returns amount of logged Cache reads: "get" calls. + * + * @return array + */ + public function getStatistics() + { + return $this->data['instances']['statistics']; + } + + /** + * Method returns the statistic totals. + * + * @return array + */ + public function getTotals() + { + return $this->data['total']['statistics']; + } + + /** + * Method returns all logged Cache call objects. + * + * @return mixed + */ + public function getCalls() + { + return $this->data['instances']['calls']; + } + + private function calculateStatistics(): array + { + $statistics = array(); + foreach ($this->data['instances']['calls'] as $name => $calls) { + $statistics[$name] = array( + 'calls' => 0, + 'time' => 0, + 'reads' => 0, + 'writes' => 0, + 'deletes' => 0, + 'hits' => 0, + 'misses' => 0, + ); + /** @var TraceableAdapterEvent $call */ + foreach ($calls as $call) { + ++$statistics[$name]['calls']; + $statistics[$name]['time'] += $call->end - $call->start; + if ('getItem' === $call->name) { + ++$statistics[$name]['reads']; + if ($call->hits) { + ++$statistics[$name]['hits']; + } else { + ++$statistics[$name]['misses']; + } + } elseif ('getItems' === $call->name) { + $statistics[$name]['reads'] += $call->hits + $call->misses; + $statistics[$name]['hits'] += $call->hits; + $statistics[$name]['misses'] += $call->misses; + } elseif ('hasItem' === $call->name) { + ++$statistics[$name]['reads']; + if (false === $call->result) { + ++$statistics[$name]['misses']; + } else { + ++$statistics[$name]['hits']; + } + } elseif ('save' === $call->name) { + ++$statistics[$name]['writes']; + } elseif ('deleteItem' === $call->name) { + ++$statistics[$name]['deletes']; + } + } + if ($statistics[$name]['reads']) { + $statistics[$name]['hit_read_ratio'] = round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2); + } else { + $statistics[$name]['hit_read_ratio'] = null; + } + } + + return $statistics; + } + + private function calculateTotalStatistics(): array + { + $statistics = $this->getStatistics(); + $totals = array( + 'calls' => 0, + 'time' => 0, + 'reads' => 0, + 'writes' => 0, + 'deletes' => 0, + 'hits' => 0, + 'misses' => 0, + ); + foreach ($statistics as $name => $values) { + foreach ($totals as $key => $value) { + $totals[$key] += $statistics[$name][$key]; + } + } + if ($totals['reads']) { + $totals['hit_read_ratio'] = round(100 * $totals['hits'] / $totals['reads'], 2); + } else { + $totals['hit_read_ratio'] = null; + } + + return $totals; + } +} diff --git a/vendor/symfony/cache/DoctrineProvider.php b/vendor/symfony/cache/DoctrineProvider.php new file mode 100644 index 0000000..cebe95f --- /dev/null +++ b/vendor/symfony/cache/DoctrineProvider.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\Cache; + +use Doctrine\Common\Cache\CacheProvider; +use Psr\Cache\CacheItemPoolInterface; + +/** + * @author Nicolas Grekas + */ +class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface +{ + private $pool; + + public function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function prune() + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResettableInterface) { + $this->pool->reset(); + } + $this->setNamespace($this->getNamespace()); + } + + /** + * {@inheritdoc} + */ + protected function doFetch($id) + { + $item = $this->pool->getItem(rawurlencode($id)); + + return $item->isHit() ? $item->get() : false; + } + + /** + * {@inheritdoc} + */ + protected function doContains($id) + { + return $this->pool->hasItem(rawurlencode($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $item = $this->pool->getItem(rawurlencode($id)); + + if (0 < $lifeTime) { + $item->expiresAfter($lifeTime); + } + + return $this->pool->save($item->set($data)); + } + + /** + * {@inheritdoc} + */ + protected function doDelete($id) + { + return $this->pool->deleteItem(rawurlencode($id)); + } + + /** + * {@inheritdoc} + */ + protected function doFlush() + { + $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + protected function doGetStats() + { + } +} diff --git a/vendor/symfony/cache/Exception/CacheException.php b/vendor/symfony/cache/Exception/CacheException.php new file mode 100644 index 0000000..e87b2db --- /dev/null +++ b/vendor/symfony/cache/Exception/CacheException.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\Cache\Exception; + +use Psr\Cache\CacheException as Psr6CacheInterface; +use Psr\SimpleCache\CacheException as SimpleCacheInterface; + +class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface +{ +} diff --git a/vendor/symfony/cache/Exception/InvalidArgumentException.php b/vendor/symfony/cache/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..828bf3e --- /dev/null +++ b/vendor/symfony/cache/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Exception; + +use Psr\Cache\InvalidArgumentException as Psr6CacheInterface; +use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface; + +class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface +{ +} diff --git a/vendor/symfony/cache/LICENSE b/vendor/symfony/cache/LICENSE new file mode 100644 index 0000000..fcd3fa7 --- /dev/null +++ b/vendor/symfony/cache/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016-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/vendor/symfony/cache/PruneableInterface.php b/vendor/symfony/cache/PruneableInterface.php new file mode 100644 index 0000000..4261525 --- /dev/null +++ b/vendor/symfony/cache/PruneableInterface.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\Cache; + +/** + * Interface extends psr-6 and psr-16 caches to allow for pruning (deletion) of all expired cache items. + */ +interface PruneableInterface +{ + /** + * @return bool + */ + public function prune(); +} diff --git a/vendor/symfony/cache/README.md b/vendor/symfony/cache/README.md new file mode 100644 index 0000000..c4ab752 --- /dev/null +++ b/vendor/symfony/cache/README.md @@ -0,0 +1,18 @@ +Symfony PSR-6 implementation for caching +======================================== + +This component provides an extended [PSR-6](http://www.php-fig.org/psr/psr-6/) +implementation for adding cache to your applications. It is designed to have a +low overhead so that caching is fastest. It ships with a few caching adapters +for the most widespread and suited to caching backends. It also provides a +`doctrine/cache` proxy adapter to cover more advanced caching needs and a proxy +adapter for greater interoperability between PSR-6 implementations. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/cache.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/cache/ResettableInterface.php b/vendor/symfony/cache/ResettableInterface.php new file mode 100644 index 0000000..6be7286 --- /dev/null +++ b/vendor/symfony/cache/ResettableInterface.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\Cache; + +/** + * Resets a pool's local state. + */ +interface ResettableInterface +{ + public function reset(); +} diff --git a/vendor/symfony/cache/Simple/AbstractCache.php b/vendor/symfony/cache/Simple/AbstractCache.php new file mode 100644 index 0000000..08456f5 --- /dev/null +++ b/vendor/symfony/cache/Simple/AbstractCache.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\Cache\Simple; + +use Psr\Log\LoggerAwareInterface; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\AbstractTrait; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use AbstractTrait { + deleteItems as private; + AbstractTrait::deleteItem as delete; + AbstractTrait::hasItem as has; + } + + private $defaultLifetime; + + protected function __construct(string $namespace = '', int $defaultLifetime = 0) + { + $this->defaultLifetime = max(0, $defaultLifetime); + $this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':'; + if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) { + throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace)); + } + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $id = $this->getId($key); + + try { + foreach ($this->doFetch(array($id)) as $value) { + return $value; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch key "{key}"', array('key' => $key, 'exception' => $e)); + } + + return $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + CacheItem::validateKey($key); + + return $this->setMultiple(array($key => $value), $ttl); + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + $ids = array(); + + foreach ($keys as $key) { + $ids[] = $this->getId($key); + } + try { + $values = $this->doFetch($ids); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested values', array('keys' => $keys, 'exception' => $e)); + $values = array(); + } + $ids = array_combine($ids, $keys); + + return $this->generateValues($values, $ids, $default); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + $valuesById = array(); + + foreach ($values as $key => $value) { + if (\is_int($key)) { + $key = (string) $key; + } + $valuesById[$this->getId($key)] = $value; + } + if (false === $ttl = $this->normalizeTtl($ttl)) { + return $this->doDelete(array_keys($valuesById)); + } + + try { + $e = $this->doSave($valuesById, $ttl); + } catch (\Exception $e) { + } + if (true === $e || array() === $e) { + return true; + } + $keys = array(); + foreach (\is_array($e) ? $e : array_keys($valuesById) as $id) { + $keys[] = substr($id, \strlen($this->namespace)); + } + CacheItem::log($this->logger, 'Failed to save values', array('keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null)); + + return false; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + return $this->deleteItems($keys); + } + + private function normalizeTtl($ttl) + { + if (null === $ttl) { + return $this->defaultLifetime; + } + if ($ttl instanceof \DateInterval) { + $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U'); + } + if (\is_int($ttl)) { + return 0 < $ttl ? $ttl : false; + } + + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl))); + } + + private function generateValues($values, &$keys, $default) + { + try { + foreach ($values as $id => $value) { + if (!isset($keys[$id])) { + $id = key($keys); + } + $key = $keys[$id]; + unset($keys[$id]); + yield $key => $value; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to fetch requested values', array('keys' => array_values($keys), 'exception' => $e)); + } + + foreach ($keys as $key) { + yield $key => $default; + } + } +} diff --git a/vendor/symfony/cache/Simple/ApcuCache.php b/vendor/symfony/cache/Simple/ApcuCache.php new file mode 100644 index 0000000..0877c39 --- /dev/null +++ b/vendor/symfony/cache/Simple/ApcuCache.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\Cache\Simple; + +use Symfony\Component\Cache\Traits\ApcuTrait; + +class ApcuCache extends AbstractCache +{ + use ApcuTrait; + + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null) + { + $this->init($namespace, $defaultLifetime, $version); + } +} diff --git a/vendor/symfony/cache/Simple/ArrayCache.php b/vendor/symfony/cache/Simple/ArrayCache.php new file mode 100644 index 0000000..aadefe2 --- /dev/null +++ b/vendor/symfony/cache/Simple/ArrayCache.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\Cache\Simple; + +use Psr\Log\LoggerAwareInterface; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ArrayTrait; + +/** + * @author Nicolas Grekas + */ +class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInterface +{ + use ArrayTrait { + ArrayTrait::deleteItem as delete; + ArrayTrait::hasItem as has; + } + + private $defaultLifetime; + + /** + * @param int $defaultLifetime + * @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise + */ + public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true) + { + $this->defaultLifetime = $defaultLifetime; + $this->storeSerialized = $storeSerialized; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + foreach ($this->getMultiple(array($key), $default) as $v) { + return $v; + } + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + foreach ($keys as $key) { + CacheItem::validateKey($key); + } + + return $this->generateItems($keys, time(), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; }); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if (!\is_array($keys) && !$keys instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + foreach ($keys as $key) { + $this->delete($key); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + CacheItem::validateKey($key); + + return $this->setMultiple(array($key => $value), $ttl); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + $valuesArray = array(); + + foreach ($values as $key => $value) { + \is_int($key) || CacheItem::validateKey($key); + $valuesArray[$key] = $value; + } + if (false === $ttl = $this->normalizeTtl($ttl)) { + return $this->deleteMultiple(array_keys($valuesArray)); + } + if ($this->storeSerialized) { + foreach ($valuesArray as $key => $value) { + try { + $valuesArray[$key] = serialize($value); + } catch (\Exception $e) { + $type = \is_object($value) ? \get_class($value) : \gettype($value); + CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e)); + + return false; + } + } + } + $expiry = 0 < $ttl ? time() + $ttl : PHP_INT_MAX; + + foreach ($valuesArray as $key => $value) { + $this->values[$key] = $value; + $this->expiries[$key] = $expiry; + } + + return true; + } + + private function normalizeTtl($ttl) + { + if (null === $ttl) { + return $this->defaultLifetime; + } + if ($ttl instanceof \DateInterval) { + $ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U'); + } + if (\is_int($ttl)) { + return 0 < $ttl ? $ttl : false; + } + + throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl))); + } +} diff --git a/vendor/symfony/cache/Simple/ChainCache.php b/vendor/symfony/cache/Simple/ChainCache.php new file mode 100644 index 0000000..38c0a9b --- /dev/null +++ b/vendor/symfony/cache/Simple/ChainCache.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * Chains several caches together. + * + * Cached items are fetched from the first cache having them in its data store. + * They are saved and deleted in all caches at once. + * + * @author Nicolas Grekas + */ +class ChainCache implements CacheInterface, PruneableInterface, ResettableInterface +{ + private $miss; + private $caches = array(); + private $defaultLifetime; + private $cacheCount; + + /** + * @param CacheInterface[] $caches The ordered list of caches used to fetch cached items + * @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones + */ + public function __construct(array $caches, int $defaultLifetime = 0) + { + if (!$caches) { + throw new InvalidArgumentException('At least one cache must be specified.'); + } + + foreach ($caches as $cache) { + if (!$cache instanceof CacheInterface) { + throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), CacheInterface::class)); + } + } + + $this->miss = new \stdClass(); + $this->caches = array_values($caches); + $this->cacheCount = \count($this->caches); + $this->defaultLifetime = 0 < $defaultLifetime ? $defaultLifetime : null; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + + foreach ($this->caches as $i => $cache) { + $value = $cache->get($key, $miss); + + if ($miss !== $value) { + while (0 <= --$i) { + $this->caches[$i]->set($key, $value, $this->defaultLifetime); + } + + return $value; + } + } + + return $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + + return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default); + } + + private function generateItems($values, $cacheIndex, $miss, $default) + { + $missing = array(); + $nextCacheIndex = $cacheIndex + 1; + $nextCache = isset($this->caches[$nextCacheIndex]) ? $this->caches[$nextCacheIndex] : null; + + foreach ($values as $k => $value) { + if ($miss !== $value) { + yield $k => $value; + } elseif (!$nextCache) { + yield $k => $default; + } else { + $missing[] = $k; + } + } + + if ($missing) { + $cache = $this->caches[$cacheIndex]; + $values = $this->generateItems($nextCache->getMultiple($missing, $miss), $nextCacheIndex, $miss, $default); + + foreach ($values as $k => $value) { + if ($miss !== $value) { + $cache->set($k, $value, $this->defaultLifetime); + yield $k => $value; + } else { + yield $k => $default; + } + } + } + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + foreach ($this->caches as $cache) { + if ($cache->has($key)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $cleared = true; + $i = $this->cacheCount; + + while ($i--) { + $cleared = $this->caches[$i]->clear() && $cleared; + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $deleted = true; + $i = $this->cacheCount; + + while ($i--) { + $deleted = $this->caches[$i]->delete($key) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } + $deleted = true; + $i = $this->cacheCount; + + while ($i--) { + $deleted = $this->caches[$i]->deleteMultiple($keys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $saved = true; + $i = $this->cacheCount; + + while ($i--) { + $saved = $this->caches[$i]->set($key, $value, $ttl) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if ($values instanceof \Traversable) { + $valuesIterator = $values; + $values = function () use ($valuesIterator, &$values) { + $generatedValues = array(); + + foreach ($valuesIterator as $key => $value) { + yield $key => $value; + $generatedValues[$key] = $value; + } + + $values = $generatedValues; + }; + $values = $values(); + } + $saved = true; + $i = $this->cacheCount; + + while ($i--) { + $saved = $this->caches[$i]->setMultiple($values, $ttl) && $saved; + } + + return $saved; + } + + /** + * {@inheritdoc} + */ + public function prune() + { + $pruned = true; + + foreach ($this->caches as $cache) { + if ($cache instanceof PruneableInterface) { + $pruned = $cache->prune() && $pruned; + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + foreach ($this->caches as $cache) { + if ($cache instanceof ResettableInterface) { + $cache->reset(); + } + } + } +} diff --git a/vendor/symfony/cache/Simple/DoctrineCache.php b/vendor/symfony/cache/Simple/DoctrineCache.php new file mode 100644 index 0000000..0ba701d --- /dev/null +++ b/vendor/symfony/cache/Simple/DoctrineCache.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\Cache\Simple; + +use Doctrine\Common\Cache\CacheProvider; +use Symfony\Component\Cache\Traits\DoctrineTrait; + +class DoctrineCache extends AbstractCache +{ + use DoctrineTrait; + + public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0) + { + parent::__construct('', $defaultLifetime); + $this->provider = $provider; + $provider->setNamespace($namespace); + } +} diff --git a/vendor/symfony/cache/Simple/FilesystemCache.php b/vendor/symfony/cache/Simple/FilesystemCache.php new file mode 100644 index 0000000..37b3d3f --- /dev/null +++ b/vendor/symfony/cache/Simple/FilesystemCache.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\Cache\Simple; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\FilesystemTrait; + +class FilesystemCache extends AbstractCache implements PruneableInterface +{ + use FilesystemTrait; + + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) + { + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + } +} diff --git a/vendor/symfony/cache/Simple/MemcachedCache.php b/vendor/symfony/cache/Simple/MemcachedCache.php new file mode 100644 index 0000000..0ff521b --- /dev/null +++ b/vendor/symfony/cache/Simple/MemcachedCache.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\Cache\Simple; + +use Symfony\Component\Cache\Traits\MemcachedTrait; + +class MemcachedCache extends AbstractCache +{ + use MemcachedTrait; + + protected $maxIdLength = 250; + + public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0) + { + $this->init($client, $namespace, $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Simple/NullCache.php b/vendor/symfony/cache/Simple/NullCache.php new file mode 100644 index 0000000..fa986ae --- /dev/null +++ b/vendor/symfony/cache/Simple/NullCache.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\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; + +/** + * @author Nicolas Grekas + */ +class NullCache implements CacheInterface +{ + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + return $default; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + foreach ($keys as $key) { + yield $key => $default; + } + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + return false; + } +} diff --git a/vendor/symfony/cache/Simple/PdoCache.php b/vendor/symfony/cache/Simple/PdoCache.php new file mode 100644 index 0000000..65b9879 --- /dev/null +++ b/vendor/symfony/cache/Simple/PdoCache.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\Cache\Simple; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PdoTrait; + +class PdoCache extends AbstractCache implements PruneableInterface +{ + use PdoTrait; + + protected $maxIdLength = 255; + + /** + * You can either pass an existing database connection as PDO instance or + * a Doctrine DBAL Connection or a DSN string that will be used to + * lazy-connect to the database when the cache is actually used. + * + * List of available options: + * * db_table: The name of the table [default: cache_items] + * * db_id_col: The column where to store the cache id [default: item_id] + * * db_data_col: The column where to store the cache data [default: item_data] + * * db_lifetime_col: The column where to store the lifetime [default: item_lifetime] + * * db_time_col: The column where to store the timestamp [default: item_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()] + * + * @param \PDO|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null + * + * @throws InvalidArgumentException When first argument is not PDO nor Connection nor string + * @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + * @throws InvalidArgumentException When namespace contains invalid characters + */ + public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = array()) + { + $this->init($connOrDsn, $namespace, $defaultLifetime, $options); + } +} diff --git a/vendor/symfony/cache/Simple/PhpArrayCache.php b/vendor/symfony/cache/Simple/PhpArrayCache.php new file mode 100644 index 0000000..5186ded --- /dev/null +++ b/vendor/symfony/cache/Simple/PhpArrayCache.php @@ -0,0 +1,252 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\PhpArrayTrait; + +/** + * Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0. + * Warmed up items are read-only and run-time discovered items are cached using a fallback adapter. + * + * @author Titouan Galopin + * @author Nicolas Grekas + */ +class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInterface +{ + use PhpArrayTrait; + + /** + * @param string $file The PHP file were values are cached + * @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit + */ + public function __construct(string $file, CacheInterface $fallbackPool) + { + $this->file = $file; + $this->pool = $fallbackPool; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); + } + + /** + * This adapter takes advantage of how PHP stores arrays in its latest versions. + * + * @param string $file The PHP file were values are cached + * + * @return CacheInterface + */ + public static function create($file, CacheInterface $fallbackPool) + { + // Shared memory is available in PHP 7.0+ with OPCache enabled + if (ini_get('opcache.enable')) { + return new static($file, $fallbackPool); + } + + return $fallbackPool; + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + if (!isset($this->values[$key])) { + return $this->pool->get($key, $default); + } + + $value = $this->values[$key]; + + if ('N;' === $value) { + return null; + } + if (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + return unserialize($value); + } catch (\Throwable $e) { + return $default; + } + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + } + if (null === $this->values) { + $this->initialize(); + } + + return $this->generateItems($keys, $default); + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return isset($this->values[$key]) || $this->pool->has($key); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$key]) && $this->pool->delete($key); + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if (!\is_array($keys) && !$keys instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + $deleted = true; + $fallbackKeys = array(); + + foreach ($keys as $key) { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + + if (isset($this->values[$key])) { + $deleted = false; + } else { + $fallbackKeys[] = $key; + } + } + if (null === $this->values) { + $this->initialize(); + } + + if ($fallbackKeys) { + $deleted = $this->pool->deleteMultiple($fallbackKeys) && $deleted; + } + + return $deleted; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + if (!\is_string($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + if (null === $this->values) { + $this->initialize(); + } + + return !isset($this->values[$key]) && $this->pool->set($key, $value, $ttl); + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + + $saved = true; + $fallbackValues = array(); + + foreach ($values as $key => $value) { + if (!\is_string($key) && !\is_int($key)) { + throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key))); + } + + if (isset($this->values[$key])) { + $saved = false; + } else { + $fallbackValues[$key] = $value; + } + } + + if ($fallbackValues) { + $saved = $this->pool->setMultiple($fallbackValues, $ttl) && $saved; + } + + return $saved; + } + + private function generateItems(array $keys, $default) + { + $fallbackKeys = array(); + + foreach ($keys as $key) { + if (isset($this->values[$key])) { + $value = $this->values[$key]; + + if ('N;' === $value) { + yield $key => null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + try { + yield $key => unserialize($value); + } catch (\Throwable $e) { + yield $key => $default; + } + } else { + yield $key => $value; + } + } else { + $fallbackKeys[] = $key; + } + } + + if ($fallbackKeys) { + foreach ($this->pool->getMultiple($fallbackKeys, $default) as $key => $item) { + yield $key => $item; + } + } + } +} diff --git a/vendor/symfony/cache/Simple/PhpFilesCache.php b/vendor/symfony/cache/Simple/PhpFilesCache.php new file mode 100644 index 0000000..77239c3 --- /dev/null +++ b/vendor/symfony/cache/Simple/PhpFilesCache.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\Cache\Simple; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Traits\PhpFilesTrait; + +class PhpFilesCache extends AbstractCache implements PruneableInterface +{ + use PhpFilesTrait; + + /** + * @throws CacheException if OPcache is not enabled + */ + public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null) + { + if (!static::isSupported()) { + throw new CacheException('OPcache is not enabled'); + } + parent::__construct('', $defaultLifetime); + $this->init($namespace, $directory); + + $e = new \Exception(); + $this->includeHandler = function () use ($e) { throw $e; }; + $this->zendDetectUnicode = ini_get('zend.detect_unicode'); + } +} diff --git a/vendor/symfony/cache/Simple/Psr6Cache.php b/vendor/symfony/cache/Simple/Psr6Cache.php new file mode 100644 index 0000000..482aa13 --- /dev/null +++ b/vendor/symfony/cache/Simple/Psr6Cache.php @@ -0,0 +1,228 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\Cache\CacheException as Psr6CacheException; +use Psr\Cache\CacheItemPoolInterface; +use Psr\SimpleCache\CacheException as SimpleCacheException; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Cache\Traits\ProxyTrait; + +/** + * @author Nicolas Grekas + */ +class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterface +{ + use ProxyTrait; + + private $createCacheItem; + + public function __construct(CacheItemPoolInterface $pool) + { + $this->pool = $pool; + + if ($pool instanceof AbstractAdapter) { + $this->createCacheItem = \Closure::bind( + function ($key, $value, $allowInt = false) { + if ($allowInt && \is_int($key)) { + $key = (string) $key; + } else { + CacheItem::validateKey($key); + } + $f = $this->createCacheItem; + + return $f($key, $value, false); + }, + $pool, + AbstractAdapter::class + ); + } + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + try { + $item = $this->pool->getItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + + return $item->isHit() ? $item->get() : $default; + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + try { + if (null !== $f = $this->createCacheItem) { + $item = $f($key, $value); + } else { + $item = $this->pool->getItem($key)->set($value); + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + + return $this->pool->save($item); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + try { + return $this->pool->deleteItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->pool->clear(); + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + try { + $items = $this->pool->getItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $values = array(); + + foreach ($items as $key => $item) { + $values[$key] = $item->isHit() ? $item->get() : $default; + } + + return $values; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $valuesIsArray = \is_array($values); + if (!$valuesIsArray && !$values instanceof \Traversable) { + throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given', \is_object($values) ? \get_class($values) : \gettype($values))); + } + $items = array(); + + try { + if (null !== $f = $this->createCacheItem) { + $valuesIsArray = false; + foreach ($values as $key => $value) { + $items[$key] = $f($key, $value, true); + } + } elseif ($valuesIsArray) { + $items = array(); + foreach ($values as $key => $value) { + $items[] = (string) $key; + } + $items = $this->pool->getItems($items); + } else { + foreach ($values as $key => $value) { + if (\is_int($key)) { + $key = (string) $key; + } + $items[$key] = $this->pool->getItem($key)->set($value); + } + } + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + $ok = true; + + foreach ($items as $key => $item) { + if ($valuesIsArray) { + $item->set($values[$key]); + } + if (null !== $ttl) { + $item->expiresAfter($ttl); + } + $ok = $this->pool->saveDeferred($item) && $ok; + } + + return $this->pool->commit() && $ok; + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + if ($keys instanceof \Traversable) { + $keys = iterator_to_array($keys, false); + } elseif (!\is_array($keys)) { + throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', \is_object($keys) ? \get_class($keys) : \gettype($keys))); + } + + try { + return $this->pool->deleteItems($keys); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + try { + return $this->pool->hasItem($key); + } catch (SimpleCacheException $e) { + throw $e; + } catch (Psr6CacheException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } + } +} diff --git a/vendor/symfony/cache/Simple/RedisCache.php b/vendor/symfony/cache/Simple/RedisCache.php new file mode 100644 index 0000000..45bb5ff --- /dev/null +++ b/vendor/symfony/cache/Simple/RedisCache.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\Cache\Simple; + +use Symfony\Component\Cache\Traits\RedisTrait; + +class RedisCache extends AbstractCache +{ + use RedisTrait; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient + * @param string $namespace + * @param int $defaultLifetime + */ + public function __construct($redisClient, string $namespace = '', int $defaultLifetime = 0) + { + $this->init($redisClient, $namespace, $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Simple/TraceableCache.php b/vendor/symfony/cache/Simple/TraceableCache.php new file mode 100644 index 0000000..181934e --- /dev/null +++ b/vendor/symfony/cache/Simple/TraceableCache.php @@ -0,0 +1,241 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Nicolas Grekas + */ +class TraceableCache implements CacheInterface, PruneableInterface, ResettableInterface +{ + private $pool; + private $miss; + private $calls = array(); + + public function __construct(CacheInterface $pool) + { + $this->pool = $pool; + $this->miss = new \stdClass(); + } + + /** + * {@inheritdoc} + */ + public function get($key, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + $event = $this->start(__FUNCTION__); + try { + $value = $this->pool->get($key, $miss); + } finally { + $event->end = microtime(true); + } + if ($event->result[$key] = $miss !== $value) { + ++$event->hits; + } else { + ++$event->misses; + $value = $default; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function has($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->has($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->delete($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function set($key, $value, $ttl = null) + { + $event = $this->start(__FUNCTION__); + try { + return $event->result[$key] = $this->pool->set($key, $value, $ttl); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function setMultiple($values, $ttl = null) + { + $event = $this->start(__FUNCTION__); + $event->result['keys'] = array(); + + if ($values instanceof \Traversable) { + $values = function () use ($values, $event) { + foreach ($values as $k => $v) { + $event->result['keys'][] = $k; + yield $k => $v; + } + }; + $values = $values(); + } elseif (\is_array($values)) { + $event->result['keys'] = array_keys($values); + } + + try { + return $event->result['result'] = $this->pool->setMultiple($values, $ttl); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getMultiple($keys, $default = null) + { + $miss = null !== $default && \is_object($default) ? $default : $this->miss; + $event = $this->start(__FUNCTION__); + try { + $result = $this->pool->getMultiple($keys, $miss); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event, $miss, $default) { + $event->result = array(); + foreach ($result as $key => $value) { + if ($event->result[$key] = $miss !== $value) { + ++$event->hits; + } else { + ++$event->misses; + $value = $default; + } + yield $key => $value; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteMultiple($keys) + { + $event = $this->start(__FUNCTION__); + if ($keys instanceof \Traversable) { + $keys = $event->result['keys'] = iterator_to_array($keys, false); + } else { + $event->result['keys'] = $keys; + } + try { + return $event->result['result'] = $this->pool->deleteMultiple($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function prune() + { + if (!$this->pool instanceof PruneableInterface) { + return false; + } + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->prune(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if (!$this->pool instanceof ResettableInterface) { + return; + } + $event = $this->start(__FUNCTION__); + try { + $this->pool->reset(); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + try { + return $this->calls; + } finally { + $this->calls = array(); + } + } + + private function start($name) + { + $this->calls[] = $event = new TraceableCacheEvent(); + $event->name = $name; + $event->start = microtime(true); + + return $event; + } +} + +class TraceableCacheEvent +{ + public $name; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/vendor/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.php new file mode 100644 index 0000000..147dfcd --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/AbstractRedisAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\RedisAdapter; + +abstract class AbstractRedisAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testExpiration' => 'Testing expiration slows down the test suite', + 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ); + + protected static $redis; + + public function createCachePool($defaultLifetime = 0) + { + return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public static function setupBeforeClass() + { + if (!\extension_loaded('redis')) { + self::markTestSkipped('Extension redis required.'); + } + if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/AdapterTestCase.php b/vendor/symfony/cache/Tests/Adapter/AdapterTestCase.php new file mode 100644 index 0000000..018d149 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/AdapterTestCase.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\Cache\Tests\Adapter; + +use Cache\IntegrationTests\CachePoolTest; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\PruneableInterface; + +abstract class AdapterTestCase extends CachePoolTest +{ + protected function setUp() + { + parent::setUp(); + + if (!array_key_exists('testPrune', $this->skippedTests) && !$this->createCachePool() instanceof PruneableInterface) { + $this->skippedTests['testPrune'] = 'Not a pruneable cache pool.'; + } + } + + public function testDefaultLifeTime() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(2); + + $item = $cache->getItem('key.dlt'); + $item->set('value'); + $cache->save($item); + sleep(1); + + $item = $cache->getItem('key.dlt'); + $this->assertTrue($item->isHit()); + + sleep(2); + $item = $cache->getItem('key.dlt'); + $this->assertFalse($item->isHit()); + } + + public function testExpiration() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(); + $cache->save($cache->getItem('k1')->set('v1')->expiresAfter(2)); + $cache->save($cache->getItem('k2')->set('v2')->expiresAfter(366 * 86400)); + + sleep(3); + $item = $cache->getItem('k1'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit() is false."); + + $item = $cache->getItem('k2'); + $this->assertTrue($item->isHit()); + $this->assertSame('v2', $item->get()); + } + + public function testNotUnserializable() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createCachePool(); + + $item = $cache->getItem('foo'); + $cache->save($item->set(new NotUnserializable())); + + $item = $cache->getItem('foo'); + $this->assertFalse($item->isHit()); + + foreach ($cache->getItems(array('foo')) as $item) { + } + $cache->save($item->set(new NotUnserializable())); + + foreach ($cache->getItems(array('foo')) as $item) { + } + $this->assertFalse($item->isHit()); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + if (!method_exists($this, 'isPruned')) { + $this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.'); + } + + /** @var PruneableInterface|CacheItemPoolInterface $cache */ + $cache = $this->createCachePool(); + + $doSet = function ($name, $value, \DateInterval $expiresAfter = null) use ($cache) { + $item = $cache->getItem($name); + $item->set($value); + + if ($expiresAfter) { + $item->expiresAfter($expiresAfter); + } + + $cache->save($item); + }; + + $doSet('foo', 'foo-val', new \DateInterval('PT05S')); + $doSet('bar', 'bar-val', new \DateInterval('PT10S')); + $doSet('baz', 'baz-val', new \DateInterval('PT15S')); + $doSet('qux', 'qux-val', new \DateInterval('PT20S')); + + sleep(30); + $cache->prune(); + $this->assertTrue($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertTrue($this->isPruned($cache, 'qux')); + + $doSet('foo', 'foo-val'); + $doSet('bar', 'bar-val', new \DateInterval('PT20S')); + $doSet('baz', 'baz-val', new \DateInterval('PT40S')); + $doSet('qux', 'qux-val', new \DateInterval('PT80S')); + + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertFalse($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'qux')); + } +} + +class NotUnserializable implements \Serializable +{ + public function serialize() + { + return serialize(123); + } + + public function unserialize($ser) + { + throw new \Exception(__CLASS__); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/ApcuAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/ApcuAdapterTest.php new file mode 100644 index 0000000..2b3c6b4 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/ApcuAdapterTest.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\Cache\Tests\Adapter; + +use Psr\Log\NullLogger; +use Symfony\Component\Cache\Adapter\ApcuAdapter; + +class ApcuAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testExpiration' => 'Testing expiration slows down the test suite', + 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ); + + public function createCachePool($defaultLifetime = 0) + { + if (!\function_exists('apcu_fetch') || !ini_get('apc.enabled')) { + $this->markTestSkipped('APCu extension is required.'); + } + if ('cli' === \PHP_SAPI && !ini_get('apc.enable_cli')) { + if ('testWithCliSapi' !== $this->getName()) { + $this->markTestSkipped('apc.enable_cli=1 is required.'); + } + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Fails transiently on Windows.'); + } + + return new ApcuAdapter(str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public function testUnserializable() + { + $pool = $this->createCachePool(); + + $item = $pool->getItem('foo'); + $item->set(function () {}); + + $this->assertFalse($pool->save($item)); + + $item = $pool->getItem('foo'); + $this->assertFalse($item->isHit()); + } + + public function testVersion() + { + $namespace = str_replace('\\', '.', \get_class($this)); + + $pool1 = new ApcuAdapter($namespace, 0, 'p1'); + + $item = $pool1->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertTrue($pool1->save($item->set('bar'))); + + $item = $pool1->getItem('foo'); + $this->assertTrue($item->isHit()); + $this->assertSame('bar', $item->get()); + + $pool2 = new ApcuAdapter($namespace, 0, 'p2'); + + $item = $pool2->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + + $item = $pool1->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + } + + public function testNamespace() + { + $namespace = str_replace('\\', '.', \get_class($this)); + + $pool1 = new ApcuAdapter($namespace.'_1', 0, 'p1'); + + $item = $pool1->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertTrue($pool1->save($item->set('bar'))); + + $item = $pool1->getItem('foo'); + $this->assertTrue($item->isHit()); + $this->assertSame('bar', $item->get()); + + $pool2 = new ApcuAdapter($namespace.'_2', 0, 'p1'); + + $item = $pool2->getItem('foo'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get()); + + $item = $pool1->getItem('foo'); + $this->assertTrue($item->isHit()); + $this->assertSame('bar', $item->get()); + } + + public function testWithCliSapi() + { + try { + // disable PHPUnit error handler to mimic a production environment + $isCalled = false; + set_error_handler(function () use (&$isCalled) { + $isCalled = true; + }); + $pool = new ApcuAdapter(str_replace('\\', '.', __CLASS__)); + $pool->setLogger(new NullLogger()); + + $item = $pool->getItem('foo'); + $item->isHit(); + $pool->save($item->set('bar')); + $this->assertFalse($isCalled); + } finally { + restore_error_handler(); + } + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/ArrayAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/ArrayAdapterTest.php new file mode 100644 index 0000000..725d790 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/ArrayAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\ArrayAdapter; + +/** + * @group time-sensitive + */ +class ArrayAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.', + 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.', + ); + + public function createCachePool($defaultLifetime = 0) + { + return new ArrayAdapter($defaultLifetime); + } + + public function testGetValuesHitAndMiss() + { + /** @var ArrayAdapter $cache */ + $cache = $this->createCachePool(); + + // Hit + $item = $cache->getItem('foo'); + $item->set('4711'); + $cache->save($item); + + $fooItem = $cache->getItem('foo'); + $this->assertTrue($fooItem->isHit()); + $this->assertEquals('4711', $fooItem->get()); + + // Miss (should be present as NULL in $values) + $cache->getItem('bar'); + + $values = $cache->getValues(); + + $this->assertCount(2, $values); + $this->assertArrayHasKey('foo', $values); + $this->assertSame(serialize('4711'), $values['foo']); + $this->assertArrayHasKey('bar', $values); + $this->assertNull($values['bar']); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/ChainAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/ChainAdapterTest.php new file mode 100644 index 0000000..a47058f --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/ChainAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ChainAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter; + +/** + * @author Kévin Dunglas + * @group time-sensitive + */ +class ChainAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new ChainAdapter(array(new ArrayAdapter($defaultLifetime), new ExternalAdapter(), new FilesystemAdapter('', $defaultLifetime)), $defaultLifetime); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage At least one adapter must be specified. + */ + public function testEmptyAdaptersException() + { + new ChainAdapter(array()); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage The class "stdClass" does not implement + */ + public function testInvalidAdapterException() + { + new ChainAdapter(array(new \stdClass())); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = new ChainAdapter(array( + $this->getPruneableMock(), + $this->getNonPruneableMock(), + $this->getPruneableMock(), + )); + $this->assertTrue($cache->prune()); + + $cache = new ChainAdapter(array( + $this->getPruneableMock(), + $this->getFailingPruneableMock(), + $this->getPruneableMock(), + )); + $this->assertFalse($cache->prune()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->will($this->returnValue(true)); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getFailingPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->will($this->returnValue(false)); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|AdapterInterface + */ + private function getNonPruneableMock() + { + return $this + ->getMockBuilder(AdapterInterface::class) + ->getMock(); + } +} + +interface PruneableCacheInterface extends PruneableInterface, AdapterInterface +{ +} diff --git a/vendor/symfony/cache/Tests/Adapter/DoctrineAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/DoctrineAdapterTest.php new file mode 100644 index 0000000..8d4dfe2 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/DoctrineAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Tests\Fixtures\ArrayCache; + +/** + * @group time-sensitive + */ +class DoctrineAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayCache is not.', + 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayCache is not.', + 'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize', + ); + + public function createCachePool($defaultLifetime = 0) + { + return new DoctrineAdapter(new ArrayCache($defaultLifetime), '', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/FilesystemAdapterTest.php new file mode 100644 index 0000000..fa83068 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/FilesystemAdapterTest.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\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; + +/** + * @group time-sensitive + */ +class FilesystemAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new FilesystemAdapter('', $defaultLifetime); + } + + public static function tearDownAfterClass() + { + self::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + + public static function rmdir($dir) + { + if (!file_exists($dir)) { + return; + } + if (!$dir || 0 !== strpos(\dirname($dir), sys_get_temp_dir())) { + throw new \Exception(__METHOD__."() operates only on subdirs of system's temp dir"); + } + $children = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + foreach ($children as $child) { + if ($child->isDir()) { + rmdir($child); + } else { + unlink($child); + } + } + rmdir($dir); + } + + protected function isPruned(CacheItemPoolInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.php new file mode 100644 index 0000000..5e301f2 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/MaxIdLengthAdapterTest.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\Cache\Tests\Adapter; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\AbstractAdapter; + +class MaxIdLengthAdapterTest extends TestCase +{ + public function testLongKey() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs(array(str_repeat('-', 10))) + ->setMethods(array('doHave', 'doFetch', 'doDelete', 'doSave', 'doClear')) + ->getMock(); + + $cache->expects($this->exactly(2)) + ->method('doHave') + ->withConsecutive( + array($this->equalTo('----------:0GTYWa9n4ed8vqNlOT2iEr:')), + array($this->equalTo('----------:---------------------------------------')) + ); + + $cache->hasItem(str_repeat('-', 40)); + $cache->hasItem(str_repeat('-', 39)); + } + + public function testLongKeyVersioning() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs(array(str_repeat('-', 26))) + ->getMock(); + + $reflectionClass = new \ReflectionClass(AbstractAdapter::class); + + $reflectionMethod = $reflectionClass->getMethod('getId'); + $reflectionMethod->setAccessible(true); + + // No versioning enabled + $this->assertEquals('--------------------------:------------', $reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12)))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12))))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 23))))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 40))))); + + $reflectionProperty = $reflectionClass->getProperty('versioningIsEnabled'); + $reflectionProperty->setAccessible(true); + $reflectionProperty->setValue($cache, true); + + // Versioning enabled + $this->assertEquals('--------------------------:1:------------', $reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12)))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 12))))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 23))))); + $this->assertLessThanOrEqual(50, \strlen($reflectionMethod->invokeArgs($cache, array(str_repeat('-', 40))))); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Namespace must be 26 chars max, 40 given ("----------------------------------------") + */ + public function testTooLongNamespace() + { + $cache = $this->getMockBuilder(MaxIdLengthAdapter::class) + ->setConstructorArgs(array(str_repeat('-', 40))) + ->getMock(); + } +} + +abstract class MaxIdLengthAdapter extends AbstractAdapter +{ + protected $maxIdLength = 50; + + public function __construct($ns) + { + parent::__construct($ns); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php new file mode 100644 index 0000000..d1f8790 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/MemcachedAdapterTest.php @@ -0,0 +1,195 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\MemcachedAdapter; + +class MemcachedAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ); + + protected static $client; + + public static function setupBeforeClass() + { + if (!MemcachedAdapter::isSupported()) { + self::markTestSkipped('Extension memcached >=2.2.0 required.'); + } + self::$client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), array('binary_protocol' => false)); + self::$client->get('foo'); + $code = self::$client->getResultCode(); + + if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) { + self::markTestSkipped('Memcached error: '.strtolower(self::$client->getResultMessage())); + } + } + + public function createCachePool($defaultLifetime = 0) + { + $client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST')) : self::$client; + + return new MemcachedAdapter($client, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public function testOptions() + { + $client = MemcachedAdapter::createConnection(array(), array( + 'libketama_compatible' => false, + 'distribution' => 'modula', + 'compression' => true, + 'serializer' => 'php', + 'hash' => 'md5', + )); + + $this->assertSame(\Memcached::SERIALIZER_PHP, $client->getOption(\Memcached::OPT_SERIALIZER)); + $this->assertSame(\Memcached::HASH_MD5, $client->getOption(\Memcached::OPT_HASH)); + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(0, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + $this->assertSame(\Memcached::DISTRIBUTION_MODULA, $client->getOption(\Memcached::OPT_DISTRIBUTION)); + } + + /** + * @dataProvider provideBadOptions + * @expectedException \ErrorException + * @expectedExceptionMessage constant(): Couldn't find constant Memcached:: + */ + public function testBadOptions($name, $value) + { + MemcachedAdapter::createConnection(array(), array($name => $value)); + } + + public function provideBadOptions() + { + return array( + array('foo', 'bar'), + array('hash', 'zyx'), + array('serializer', 'zyx'), + array('distribution', 'zyx'), + ); + } + + public function testDefaultOptions() + { + $this->assertTrue(MemcachedAdapter::isSupported()); + + $client = MemcachedAdapter::createConnection(array()); + + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_TCP_NODELAY)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\CacheException + * @expectedExceptionMessage MemcachedAdapter: "serializer" option must be "php" or "igbinary". + */ + public function testOptionSerializer() + { + if (!\Memcached::HAVE_JSON) { + $this->markTestSkipped('Memcached::HAVE_JSON required'); + } + + new MemcachedAdapter(MemcachedAdapter::createConnection(array(), array('serializer' => 'json'))); + } + + /** + * @dataProvider provideServersSetting + */ + public function testServersSetting($dsn, $host, $port) + { + $client1 = MemcachedAdapter::createConnection($dsn); + $client2 = MemcachedAdapter::createConnection(array($dsn)); + $client3 = MemcachedAdapter::createConnection(array(array($host, $port))); + $expect = array( + 'host' => $host, + 'port' => $port, + ); + + $f = function ($s) { return array('host' => $s['host'], 'port' => $s['port']); }; + $this->assertSame(array($expect), array_map($f, $client1->getServerList())); + $this->assertSame(array($expect), array_map($f, $client2->getServerList())); + $this->assertSame(array($expect), array_map($f, $client3->getServerList())); + } + + public function provideServersSetting() + { + yield array( + 'memcached://127.0.0.1/50', + '127.0.0.1', + 11211, + ); + yield array( + 'memcached://localhost:11222?weight=25', + 'localhost', + 11222, + ); + if (ini_get('memcached.use_sasl')) { + yield array( + 'memcached://user:password@127.0.0.1?weight=50', + '127.0.0.1', + 11211, + ); + } + yield array( + 'memcached:///var/run/memcached.sock?weight=25', + '/var/run/memcached.sock', + 0, + ); + yield array( + 'memcached:///var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ); + if (ini_get('memcached.use_sasl')) { + yield array( + 'memcached://user:password@/var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ); + } + } + + /** + * @dataProvider provideDsnWithOptions + */ + public function testDsnWithOptions($dsn, array $options, array $expectedOptions) + { + $client = MemcachedAdapter::createConnection($dsn, $options); + + foreach ($expectedOptions as $option => $expect) { + $this->assertSame($expect, $client->getOption($option)); + } + } + + public function provideDsnWithOptions() + { + if (!class_exists('\Memcached')) { + self::markTestSkipped('Extension memcached required.'); + } + + yield array( + 'memcached://localhost:11222?retry_timeout=10', + array(\Memcached::OPT_RETRY_TIMEOUT => 8), + array(\Memcached::OPT_RETRY_TIMEOUT => 10), + ); + yield array( + 'memcached://localhost:11222?socket_recv_size=1&socket_send_size=2', + array(\Memcached::OPT_RETRY_TIMEOUT => 8), + array(\Memcached::OPT_SOCKET_RECV_SIZE => 1, \Memcached::OPT_SOCKET_SEND_SIZE => 2, \Memcached::OPT_RETRY_TIMEOUT => 8), + ); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.php new file mode 100644 index 0000000..c271403 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/NamespacedProxyAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; + +/** + * @group time-sensitive + */ +class NamespacedProxyAdapterTest extends ProxyAdapterTest +{ + public function createCachePool($defaultLifetime = 0) + { + return new ProxyAdapter(new ArrayAdapter($defaultLifetime), 'foo', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/NullAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/NullAdapterTest.php new file mode 100644 index 0000000..73e5cad --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/NullAdapterTest.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\Cache\Tests\Adapter; + +use PHPUnit\Framework\TestCase; +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\NullAdapter; + +/** + * @group time-sensitive + */ +class NullAdapterTest extends TestCase +{ + public function createCachePool() + { + return new NullAdapter(); + } + + public function testGetItem() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + } + + public function testHasItem() + { + $this->assertFalse($this->createCachePool()->hasItem('key')); + } + + public function testGetItems() + { + $adapter = $this->createCachePool(); + + $keys = array('foo', 'bar', 'baz', 'biz'); + + /** @var CacheItemInterface[] $items */ + $items = $adapter->getItems($keys); + $count = 0; + + foreach ($items as $key => $item) { + $itemKey = $item->getKey(); + + $this->assertEquals($itemKey, $key, 'Keys must be preserved when fetching multiple items'); + $this->assertContains($key, $keys, 'Cache key can not change.'); + $this->assertFalse($item->isHit()); + + // Remove $key for $keys + foreach ($keys as $k => $v) { + if ($v === $key) { + unset($keys[$k]); + } + } + + ++$count; + } + + $this->assertSame(4, $count); + } + + public function testIsHit() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + } + + public function testClear() + { + $this->assertTrue($this->createCachePool()->clear()); + } + + public function testDeleteItem() + { + $this->assertTrue($this->createCachePool()->deleteItem('key')); + } + + public function testDeleteItems() + { + $this->assertTrue($this->createCachePool()->deleteItems(array('key', 'foo', 'bar'))); + } + + public function testSave() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + + $this->assertFalse($adapter->save($item)); + } + + public function testDeferredSave() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + + $this->assertFalse($adapter->saveDeferred($item)); + } + + public function testCommit() + { + $adapter = $this->createCachePool(); + + $item = $adapter->getItem('key'); + $this->assertFalse($item->isHit()); + $this->assertNull($item->get(), "Item's value must be null when isHit is false."); + + $this->assertFalse($adapter->saveDeferred($item)); + $this->assertFalse($this->createCachePool()->commit()); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PdoAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/PdoAdapterTest.php new file mode 100644 index 0000000..b587cf6 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PdoAdapterTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\PdoAdapter; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoAdapterTest extends AdapterTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoAdapter('sqlite:'.self::$dbFile); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createCachePool($defaultLifetime = 0) + { + return new PdoAdapter('sqlite:'.self::$dbFile, 'ns', $defaultLifetime); + } + + public function testCleanupExpiredItems() + { + $pdo = new \PDO('sqlite:'.self::$dbFile); + + $getCacheItemCount = function () use ($pdo) { + return (int) $pdo->query('SELECT COUNT(*) FROM cache_items')->fetch(\PDO::FETCH_COLUMN); + }; + + $this->assertSame(0, $getCacheItemCount()); + + $cache = $this->createCachePool(); + + $item = $cache->getItem('some_nice_key'); + $item->expiresAfter(1); + $item->set(1); + + $cache->save($item); + $this->assertSame(1, $getCacheItemCount()); + + sleep(2); + + $newItem = $cache->getItem($item->getKey()); + $this->assertFalse($newItem->isHit()); + $this->assertSame(0, $getCacheItemCount(), 'PDOAdapter must clean up expired items'); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.php new file mode 100644 index 0000000..f89a27c --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PdoDbalAdapterTest.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\Cache\Tests\Adapter; + +use Doctrine\DBAL\DriverManager; +use Symfony\Component\Cache\Adapter\PdoAdapter; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoDbalAdapterTest extends AdapterTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoAdapter(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile))); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createCachePool($defaultLifetime = 0) + { + return new PdoAdapter(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)), '', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.php new file mode 100644 index 0000000..930594f --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterTest.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\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\NullAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; + +/** + * @group time-sensitive + */ +class PhpArrayAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testBasicUsage' => 'PhpArrayAdapter is read-only.', + 'testBasicUsageWithLongKey' => 'PhpArrayAdapter is read-only.', + 'testClear' => 'PhpArrayAdapter is read-only.', + 'testClearWithDeferredItems' => 'PhpArrayAdapter is read-only.', + 'testDeleteItem' => 'PhpArrayAdapter is read-only.', + 'testSaveExpired' => 'PhpArrayAdapter is read-only.', + 'testSaveWithoutExpire' => 'PhpArrayAdapter is read-only.', + 'testDeferredSave' => 'PhpArrayAdapter is read-only.', + 'testDeferredSaveWithoutCommit' => 'PhpArrayAdapter is read-only.', + 'testDeleteItems' => 'PhpArrayAdapter is read-only.', + 'testDeleteDeferredItem' => 'PhpArrayAdapter is read-only.', + 'testCommit' => 'PhpArrayAdapter is read-only.', + 'testSaveDeferredWhenChangingValues' => 'PhpArrayAdapter is read-only.', + 'testSaveDeferredOverwrite' => 'PhpArrayAdapter is read-only.', + 'testIsHitDeferred' => 'PhpArrayAdapter is read-only.', + + 'testExpiresAt' => 'PhpArrayAdapter does not support expiration.', + 'testExpiresAtWithNull' => 'PhpArrayAdapter does not support expiration.', + 'testExpiresAfterWithNull' => 'PhpArrayAdapter does not support expiration.', + 'testDeferredExpired' => 'PhpArrayAdapter does not support expiration.', + 'testExpiration' => 'PhpArrayAdapter does not support expiration.', + + 'testGetItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testGetItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testHasItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + + 'testDefaultLifeTime' => 'PhpArrayAdapter does not allow configuring a default lifetime.', + 'testPrune' => 'PhpArrayAdapter just proxies', + ); + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createCachePool() + { + return new PhpArrayAdapterWrapper(self::$file, new NullAdapter()); + } + + public function testStore() + { + $arrayWithRefs = array(); + $arrayWithRefs[0] = 123; + $arrayWithRefs[1] = &$arrayWithRefs[0]; + + $object = (object) array( + 'foo' => 'bar', + 'foo2' => 'bar2', + ); + + $expected = array( + 'null' => null, + 'serializedString' => serialize($object), + 'arrayWithRefs' => $arrayWithRefs, + 'object' => $object, + 'arrayWithObject' => array('bar' => $object), + ); + + $adapter = $this->createCachePool(); + $adapter->warmUp($expected); + + foreach ($expected as $key => $value) { + $this->assertSame(serialize($value), serialize($adapter->getItem($key)->get()), 'Warm up should create a PHP file that OPCache can load in memory'); + } + } + + public function testStoredFile() + { + $expected = array( + 'integer' => 42, + 'float' => 42.42, + 'boolean' => true, + 'array_simple' => array('foo', 'bar'), + 'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'), + ); + + $adapter = $this->createCachePool(); + $adapter->warmUp($expected); + + $values = eval(substr(file_get_contents(self::$file), 6)); + + $this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory'); + } +} + +class PhpArrayAdapterWrapper extends PhpArrayAdapter +{ + public function save(CacheItemInterface $item) + { + \call_user_func(\Closure::bind(function () use ($item) { + $this->values[$item->getKey()] = $item->get(); + $this->warmUp($this->values); + $this->values = eval(substr(file_get_contents($this->file), 6)); + }, $this, PhpArrayAdapter::class)); + + return true; + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php b/vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php new file mode 100644 index 0000000..1a23198 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; + +/** + * @group time-sensitive + */ +class PhpArrayAdapterWithFallbackTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testGetItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testGetItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testHasItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.', + 'testPrune' => 'PhpArrayAdapter just proxies', + ); + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createCachePool($defaultLifetime = 0) + { + return new PhpArrayAdapter(self::$file, new FilesystemAdapter('php-array-fallback', $defaultLifetime)); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.php new file mode 100644 index 0000000..8e93c93 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PhpFilesAdapterTest.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\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\PhpFilesAdapter; + +/** + * @group time-sensitive + */ +class PhpFilesAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testDefaultLifeTime' => 'PhpFilesAdapter does not allow configuring a default lifetime.', + ); + + public function createCachePool() + { + if (!PhpFilesAdapter::isSupported()) { + $this->markTestSkipped('OPcache extension is not enabled.'); + } + + return new PhpFilesAdapter('sf-cache'); + } + + public static function tearDownAfterClass() + { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + + protected function isPruned(CacheItemPoolInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PredisAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/PredisAdapterTest.php new file mode 100644 index 0000000..c005d64 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PredisAdapterTest.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\Cache\Tests\Adapter; + +use Predis\Connection\StreamConnection; +use Symfony\Component\Cache\Adapter\RedisAdapter; + +class PredisAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = new \Predis\Client(array('host' => getenv('REDIS_HOST'))); + } + + public function testCreateConnection() + { + $redisHost = getenv('REDIS_HOST'); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost.'/1', array('class' => \Predis\Client::class, 'timeout' => 3)); + $this->assertInstanceOf(\Predis\Client::class, $redis); + + $connection = $redis->getConnection(); + $this->assertInstanceOf(StreamConnection::class, $connection); + + $params = array( + 'scheme' => 'tcp', + 'host' => $redisHost, + 'path' => '', + 'dbindex' => '1', + 'port' => 6379, + 'class' => 'Predis\Client', + 'timeout' => 3, + 'persistent' => 0, + 'persistent_id' => null, + 'read_timeout' => 0, + 'retry_interval' => 0, + 'lazy' => false, + 'database' => '1', + 'password' => null, + ); + $this->assertSame($params, $connection->getParameters()->toArray()); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.php new file mode 100644 index 0000000..3891539 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/PredisClusterAdapterTest.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\Cache\Tests\Adapter; + +class PredisClusterAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = new \Predis\Client(array(array('host' => getenv('REDIS_HOST')))); + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/ProxyAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/ProxyAdapterTest.php new file mode 100644 index 0000000..ff4b9d3 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/ProxyAdapterTest.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\Cache\Tests\Adapter; + +use Psr\Cache\CacheItemInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; +use Symfony\Component\Cache\CacheItem; + +/** + * @group time-sensitive + */ +class ProxyAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.', + 'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.', + 'testPrune' => 'ProxyAdapter just proxies', + ); + + public function createCachePool($defaultLifetime = 0) + { + return new ProxyAdapter(new ArrayAdapter(), '', $defaultLifetime); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage OK bar + */ + public function testProxyfiedItem() + { + $item = new CacheItem(); + $pool = new ProxyAdapter(new TestingArrayAdapter($item)); + + $proxyItem = $pool->getItem('foo'); + + $this->assertNotSame($item, $proxyItem); + $pool->save($proxyItem->set('bar')); + } +} + +class TestingArrayAdapter extends ArrayAdapter +{ + private $item; + + public function __construct(CacheItemInterface $item) + { + $this->item = $item; + } + + public function getItem($key) + { + return $this->item; + } + + public function save(CacheItemInterface $item) + { + if ($item === $this->item) { + throw new \Exception('OK '.$item->get()); + } + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/RedisAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/RedisAdapterTest.php new file mode 100644 index 0000000..28c310f --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/RedisAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Traits\RedisProxy; + +class RedisAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = AbstractAdapter::createConnection('redis://'.getenv('REDIS_HOST'), array('lazy' => true)); + } + + public function createCachePool($defaultLifetime = 0) + { + $adapter = parent::createCachePool($defaultLifetime); + $this->assertInstanceOf(RedisProxy::class, self::$redis); + + return $adapter; + } + + public function testCreateConnection() + { + $redisHost = getenv('REDIS_HOST'); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost); + $this->assertInstanceOf(\Redis::class, $redis); + $this->assertTrue($redis->isConnected()); + $this->assertSame(0, $redis->getDbNum()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost.'/2'); + $this->assertSame(2, $redis->getDbNum()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost, array('timeout' => 3)); + $this->assertEquals(3, $redis->getTimeout()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost.'?timeout=4'); + $this->assertEquals(4, $redis->getTimeout()); + + $redis = RedisAdapter::createConnection('redis://'.$redisHost, array('read_timeout' => 5)); + $this->assertEquals(5, $redis->getReadTimeout()); + } + + /** + * @dataProvider provideFailedCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Redis connection failed + */ + public function testFailedCreateConnection($dsn) + { + RedisAdapter::createConnection($dsn); + } + + public function provideFailedCreateConnection() + { + return array( + array('redis://localhost:1234'), + array('redis://foo@localhost'), + array('redis://localhost/123'), + ); + } + + /** + * @dataProvider provideInvalidCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid Redis DSN + */ + public function testInvalidCreateConnection($dsn) + { + RedisAdapter::createConnection($dsn); + } + + public function provideInvalidCreateConnection() + { + return array( + array('foo://localhost'), + array('redis://'), + ); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.php new file mode 100644 index 0000000..bef3eb8 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/RedisArrayAdapterTest.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\Cache\Tests\Adapter; + +class RedisArrayAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + if (!class_exists('RedisArray')) { + self::markTestSkipped('The RedisArray class is required.'); + } + self::$redis = new \RedisArray(array(getenv('REDIS_HOST')), array('lazy_connect' => true)); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.php new file mode 100644 index 0000000..852079c --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/RedisClusterAdapterTest.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\Cache\Tests\Adapter; + +class RedisClusterAdapterTest extends AbstractRedisAdapterTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + self::$redis = new \RedisCluster(null, explode(' ', $hosts)); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.php new file mode 100644 index 0000000..460f3b0 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/SimpleCacheAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\SimpleCacheAdapter; +use Symfony\Component\Cache\Simple\FilesystemCache; + +/** + * @group time-sensitive + */ +class SimpleCacheAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testPrune' => 'SimpleCache just proxies', + ); + + public function createCachePool($defaultLifetime = 0) + { + return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php new file mode 100644 index 0000000..7074299 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/TagAwareAdapterTest.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; + +/** + * @group time-sensitive + */ +class TagAwareAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new TagAwareAdapter(new FilesystemAdapter('', $defaultLifetime)); + } + + public static function tearDownAfterClass() + { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + + /** + * @expectedException \Psr\Cache\InvalidArgumentException + */ + public function testInvalidTag() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('foo'); + $item->tag(':'); + } + + public function testInvalidateTags() + { + $pool = $this->createCachePool(); + + $i0 = $pool->getItem('i0'); + $i1 = $pool->getItem('i1'); + $i2 = $pool->getItem('i2'); + $i3 = $pool->getItem('i3'); + $foo = $pool->getItem('foo'); + + $pool->save($i0->tag('bar')); + $pool->save($i1->tag('foo')); + $pool->save($i2->tag('foo')->tag('bar')); + $pool->save($i3->tag('foo')->tag('baz')); + $pool->save($foo); + + $pool->invalidateTags(array('bar')); + + $this->assertFalse($pool->getItem('i0')->isHit()); + $this->assertTrue($pool->getItem('i1')->isHit()); + $this->assertFalse($pool->getItem('i2')->isHit()); + $this->assertTrue($pool->getItem('i3')->isHit()); + $this->assertTrue($pool->getItem('foo')->isHit()); + + $pool->invalidateTags(array('foo')); + + $this->assertFalse($pool->getItem('i1')->isHit()); + $this->assertFalse($pool->getItem('i3')->isHit()); + $this->assertTrue($pool->getItem('foo')->isHit()); + + $anotherPoolInstance = $this->createCachePool(); + + $this->assertFalse($anotherPoolInstance->getItem('i1')->isHit()); + $this->assertFalse($anotherPoolInstance->getItem('i3')->isHit()); + $this->assertTrue($anotherPoolInstance->getItem('foo')->isHit()); + } + + public function testInvalidateCommits() + { + $pool1 = $this->createCachePool(); + + $foo = $pool1->getItem('foo'); + $foo->tag('tag'); + + $pool1->saveDeferred($foo->set('foo')); + $pool1->invalidateTags(array('tag')); + + $pool2 = $this->createCachePool(); + $foo = $pool2->getItem('foo'); + + $this->assertTrue($foo->isHit()); + } + + public function testTagsAreCleanedOnSave() + { + $pool = $this->createCachePool(); + + $i = $pool->getItem('k'); + $pool->save($i->tag('foo')); + + $i = $pool->getItem('k'); + $pool->save($i->tag('bar')); + + $pool->invalidateTags(array('foo')); + $this->assertTrue($pool->getItem('k')->isHit()); + } + + public function testTagsAreCleanedOnDelete() + { + $pool = $this->createCachePool(); + + $i = $pool->getItem('k'); + $pool->save($i->tag('foo')); + $pool->deleteItem('k'); + + $pool->save($pool->getItem('k')); + $pool->invalidateTags(array('foo')); + + $this->assertTrue($pool->getItem('k')->isHit()); + } + + public function testTagItemExpiry() + { + $pool = $this->createCachePool(10); + + $item = $pool->getItem('foo'); + $item->tag(array('baz')); + $item->expiresAfter(100); + + $pool->save($item); + $pool->invalidateTags(array('baz')); + $this->assertFalse($pool->getItem('foo')->isHit()); + + sleep(20); + + $this->assertFalse($pool->getItem('foo')->isHit()); + } + + public function testGetPreviousTags() + { + $pool = $this->createCachePool(); + + $i = $pool->getItem('k'); + $pool->save($i->tag('foo')); + + $i = $pool->getItem('k'); + $this->assertSame(array('foo' => 'foo'), $i->getPreviousTags()); + } + + public function testPrune() + { + $cache = new TagAwareAdapter($this->getPruneableMock()); + $this->assertTrue($cache->prune()); + + $cache = new TagAwareAdapter($this->getNonPruneableMock()); + $this->assertFalse($cache->prune()); + + $cache = new TagAwareAdapter($this->getFailingPruneableMock()); + $this->assertFalse($cache->prune()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->will($this->returnValue(true)); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getFailingPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->will($this->returnValue(false)); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|AdapterInterface + */ + private function getNonPruneableMock() + { + return $this + ->getMockBuilder(AdapterInterface::class) + ->getMock(); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/TraceableAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/TraceableAdapterTest.php new file mode 100644 index 0000000..3755e88 --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/TraceableAdapterTest.php @@ -0,0 +1,191 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapter; + +/** + * @group time-sensitive + */ +class TraceableAdapterTest extends AdapterTestCase +{ + protected $skippedTests = array( + 'testPrune' => 'TraceableAdapter just proxies', + ); + + public function createCachePool($defaultLifetime = 0) + { + return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime)); + } + + public function testGetItemMissTrace() + { + $pool = $this->createCachePool(); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('getItem', $call->name); + $this->assertSame(array('k' => false), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testGetItemHitTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertSame(1, $call->hits); + $this->assertSame(0, $call->misses); + } + + public function testGetItemsMissTrace() + { + $pool = $this->createCachePool(); + $arg = array('k0', 'k1'); + $items = $pool->getItems($arg); + foreach ($items as $item) { + } + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('getItems', $call->name); + $this->assertSame(array('k0' => false, 'k1' => false), $call->result); + $this->assertSame(2, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemMissTrace() + { + $pool = $this->createCachePool(); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('hasItem', $call->name); + $this->assertSame(array('k' => false), $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemHitTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertSame('hasItem', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItemTrace() + { + $pool = $this->createCachePool(); + $pool->deleteItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('deleteItem', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItemsTrace() + { + $pool = $this->createCachePool(); + $arg = array('k0', 'k1'); + $pool->deleteItems($arg); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('deleteItems', $call->name); + $this->assertSame(array('keys' => $arg, 'result' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSaveTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('save', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSaveDeferredTrace() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->saveDeferred($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('saveDeferred', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testCommitTrace() + { + $pool = $this->createCachePool(); + $pool->commit(); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('commit', $call->name); + $this->assertTrue($call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} diff --git a/vendor/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.php b/vendor/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.php new file mode 100644 index 0000000..9b50bfa --- /dev/null +++ b/vendor/symfony/cache/Tests/Adapter/TraceableTagAwareAdapterTest.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\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; +use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; + +/** + * @group time-sensitive + */ +class TraceableTagAwareAdapterTest extends TraceableAdapterTest +{ + public function testInvalidateTags() + { + $pool = new TraceableTagAwareAdapter(new TagAwareAdapter(new FilesystemAdapter())); + $pool->invalidateTags(array('foo')); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('invalidateTags', $call->name); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} diff --git a/vendor/symfony/cache/Tests/CacheItemTest.php b/vendor/symfony/cache/Tests/CacheItemTest.php new file mode 100644 index 0000000..4aae16b --- /dev/null +++ b/vendor/symfony/cache/Tests/CacheItemTest.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\Cache\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\CacheItem; + +class CacheItemTest extends TestCase +{ + public function testValidKey() + { + $this->assertSame('foo', CacheItem::validateKey('foo')); + } + + /** + * @dataProvider provideInvalidKey + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Cache key + */ + public function testInvalidKey($key) + { + CacheItem::validateKey($key); + } + + public function provideInvalidKey() + { + return array( + array(''), + array('{'), + array('}'), + array('('), + array(')'), + array('/'), + array('\\'), + array('@'), + array(':'), + array(true), + array(null), + array(1), + array(1.1), + array(array(array())), + array(new \Exception('foo')), + ); + } + + public function testTag() + { + $item = new CacheItem(); + + $this->assertSame($item, $item->tag('foo')); + $this->assertSame($item, $item->tag(array('bar', 'baz'))); + + \call_user_func(\Closure::bind(function () use ($item) { + $this->assertSame(array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'), $item->tags); + }, $this, CacheItem::class)); + } + + /** + * @dataProvider provideInvalidKey + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Cache tag + */ + public function testInvalidTag($tag) + { + $item = new CacheItem(); + $item->tag($tag); + } +} diff --git a/vendor/symfony/cache/Tests/DoctrineProviderTest.php b/vendor/symfony/cache/Tests/DoctrineProviderTest.php new file mode 100644 index 0000000..91a5516 --- /dev/null +++ b/vendor/symfony/cache/Tests/DoctrineProviderTest.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\Cache\Tests; + +use Doctrine\Common\Cache\CacheProvider; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\DoctrineProvider; + +class DoctrineProviderTest extends TestCase +{ + public function testProvider() + { + $pool = new ArrayAdapter(); + $cache = new DoctrineProvider($pool); + + $this->assertInstanceOf(CacheProvider::class, $cache); + + $key = '{}()/\@:'; + + $this->assertTrue($cache->delete($key)); + $this->assertFalse($cache->contains($key)); + + $this->assertTrue($cache->save($key, 'bar')); + $this->assertTrue($cache->contains($key)); + $this->assertSame('bar', $cache->fetch($key)); + + $this->assertTrue($cache->delete($key)); + $this->assertFalse($cache->fetch($key)); + $this->assertTrue($cache->save($key, 'bar')); + + $cache->flushAll(); + $this->assertFalse($cache->fetch($key)); + $this->assertFalse($cache->contains($key)); + } +} diff --git a/vendor/symfony/cache/Tests/Fixtures/ArrayCache.php b/vendor/symfony/cache/Tests/Fixtures/ArrayCache.php new file mode 100644 index 0000000..7cdcafd --- /dev/null +++ b/vendor/symfony/cache/Tests/Fixtures/ArrayCache.php @@ -0,0 +1,52 @@ +doContains($id) ? $this->data[$id][0] : false; + } + + protected function doContains($id) + { + if (!isset($this->data[$id])) { + return false; + } + + $expiry = $this->data[$id][1]; + + return !$expiry || time() < $expiry || !$this->doDelete($id); + } + + protected function doSave($id, $data, $lifeTime = 0) + { + $this->data[$id] = array($data, $lifeTime ? time() + $lifeTime : false); + + return true; + } + + protected function doDelete($id) + { + unset($this->data[$id]); + + return true; + } + + protected function doFlush() + { + $this->data = array(); + + return true; + } + + protected function doGetStats() + { + return null; + } +} diff --git a/vendor/symfony/cache/Tests/Fixtures/ExternalAdapter.php b/vendor/symfony/cache/Tests/Fixtures/ExternalAdapter.php new file mode 100644 index 0000000..493906e --- /dev/null +++ b/vendor/symfony/cache/Tests/Fixtures/ExternalAdapter.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\Cache\Tests\Fixtures; + +use Psr\Cache\CacheItemInterface; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; + +/** + * Adapter not implementing the {@see \Symfony\Component\Cache\Adapter\AdapterInterface}. + * + * @author Kévin Dunglas + */ +class ExternalAdapter implements CacheItemPoolInterface +{ + private $cache; + + public function __construct() + { + $this->cache = new ArrayAdapter(); + } + + public function getItem($key) + { + return $this->cache->getItem($key); + } + + public function getItems(array $keys = array()) + { + return $this->cache->getItems($keys); + } + + public function hasItem($key) + { + return $this->cache->hasItem($key); + } + + public function clear() + { + return $this->cache->clear(); + } + + public function deleteItem($key) + { + return $this->cache->deleteItem($key); + } + + public function deleteItems(array $keys) + { + return $this->cache->deleteItems($keys); + } + + public function save(CacheItemInterface $item) + { + return $this->cache->save($item); + } + + public function saveDeferred(CacheItemInterface $item) + { + return $this->cache->saveDeferred($item); + } + + public function commit() + { + return $this->cache->commit(); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/AbstractRedisCacheTest.php b/vendor/symfony/cache/Tests/Simple/AbstractRedisCacheTest.php new file mode 100644 index 0000000..3e668fd --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/AbstractRedisCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\RedisCache; + +abstract class AbstractRedisCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testSetTtl' => 'Testing expiration slows down the test suite', + 'testSetMultipleTtl' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ); + + protected static $redis; + + public function createSimpleCache($defaultLifetime = 0) + { + return new RedisCache(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public static function setupBeforeClass() + { + if (!\extension_loaded('redis')) { + self::markTestSkipped('Extension redis required.'); + } + if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) { + $e = error_get_last(); + self::markTestSkipped($e['message']); + } + } + + public static function tearDownAfterClass() + { + self::$redis = null; + } +} diff --git a/vendor/symfony/cache/Tests/Simple/ApcuCacheTest.php b/vendor/symfony/cache/Tests/Simple/ApcuCacheTest.php new file mode 100644 index 0000000..737ed4e --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/ApcuCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\ApcuCache; + +class ApcuCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testSetTtl' => 'Testing expiration slows down the test suite', + 'testSetMultipleTtl' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ); + + public function createSimpleCache($defaultLifetime = 0) + { + if (!\function_exists('apcu_fetch') || !ini_get('apc.enabled') || ('cli' === \PHP_SAPI && !ini_get('apc.enable_cli'))) { + $this->markTestSkipped('APCu extension is required.'); + } + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Fails transiently on Windows.'); + } + + return new ApcuCache(str_replace('\\', '.', __CLASS__), $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/ArrayCacheTest.php b/vendor/symfony/cache/Tests/Simple/ArrayCacheTest.php new file mode 100644 index 0000000..26c3e14 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/ArrayCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\ArrayCache; + +/** + * @group time-sensitive + */ +class ArrayCacheTest extends CacheTestCase +{ + public function createSimpleCache($defaultLifetime = 0) + { + return new ArrayCache($defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/CacheTestCase.php b/vendor/symfony/cache/Tests/Simple/CacheTestCase.php new file mode 100644 index 0000000..5b84d8b --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/CacheTestCase.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Cache\IntegrationTests\SimpleCacheTest; +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; + +abstract class CacheTestCase extends SimpleCacheTest +{ + protected function setUp() + { + parent::setUp(); + + if (!array_key_exists('testPrune', $this->skippedTests) && !$this->createSimpleCache() instanceof PruneableInterface) { + $this->skippedTests['testPrune'] = 'Not a pruneable cache pool.'; + } + } + + public static function validKeys() + { + return array_merge(parent::validKeys(), array(array("a\0b"))); + } + + public function testDefaultLifeTime() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createSimpleCache(2); + $cache->clear(); + + $cache->set('key.dlt', 'value'); + sleep(1); + + $this->assertSame('value', $cache->get('key.dlt')); + + sleep(2); + $this->assertNull($cache->get('key.dlt')); + + $cache->clear(); + } + + public function testNotUnserializable() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = $this->createSimpleCache(); + $cache->clear(); + + $cache->set('foo', new NotUnserializable()); + + $this->assertNull($cache->get('foo')); + + $cache->setMultiple(array('foo' => new NotUnserializable())); + + foreach ($cache->getMultiple(array('foo')) as $value) { + } + $this->assertNull($value); + + $cache->clear(); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + if (!method_exists($this, 'isPruned')) { + $this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.'); + } + + /** @var PruneableInterface|CacheInterface $cache */ + $cache = $this->createSimpleCache(); + $cache->clear(); + + $cache->set('foo', 'foo-val', new \DateInterval('PT05S')); + $cache->set('bar', 'bar-val', new \DateInterval('PT10S')); + $cache->set('baz', 'baz-val', new \DateInterval('PT15S')); + $cache->set('qux', 'qux-val', new \DateInterval('PT20S')); + + sleep(30); + $cache->prune(); + $this->assertTrue($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertTrue($this->isPruned($cache, 'qux')); + + $cache->set('foo', 'foo-val'); + $cache->set('bar', 'bar-val', new \DateInterval('PT20S')); + $cache->set('baz', 'baz-val', new \DateInterval('PT40S')); + $cache->set('qux', 'qux-val', new \DateInterval('PT80S')); + + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertFalse($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'bar')); + $this->assertFalse($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'baz')); + $this->assertFalse($this->isPruned($cache, 'qux')); + + sleep(30); + $cache->prune(); + $this->assertFalse($this->isPruned($cache, 'foo')); + $this->assertTrue($this->isPruned($cache, 'qux')); + + $cache->clear(); + } +} + +class NotUnserializable implements \Serializable +{ + public function serialize() + { + return serialize(123); + } + + public function unserialize($ser) + { + throw new \Exception(__CLASS__); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/ChainCacheTest.php b/vendor/symfony/cache/Tests/Simple/ChainCacheTest.php new file mode 100644 index 0000000..ab28e3b --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/ChainCacheTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\Simple\ArrayCache; +use Symfony\Component\Cache\Simple\ChainCache; +use Symfony\Component\Cache\Simple\FilesystemCache; + +/** + * @group time-sensitive + */ +class ChainCacheTest extends CacheTestCase +{ + public function createSimpleCache($defaultLifetime = 0) + { + return new ChainCache(array(new ArrayCache($defaultLifetime), new FilesystemCache('', $defaultLifetime)), $defaultLifetime); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage At least one cache must be specified. + */ + public function testEmptyCachesException() + { + new ChainCache(array()); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage The class "stdClass" does not implement + */ + public function testInvalidCacheException() + { + new ChainCache(array(new \stdClass())); + } + + public function testPrune() + { + if (isset($this->skippedTests[__FUNCTION__])) { + $this->markTestSkipped($this->skippedTests[__FUNCTION__]); + } + + $cache = new ChainCache(array( + $this->getPruneableMock(), + $this->getNonPruneableMock(), + $this->getPruneableMock(), + )); + $this->assertTrue($cache->prune()); + + $cache = new ChainCache(array( + $this->getPruneableMock(), + $this->getFailingPruneableMock(), + $this->getPruneableMock(), + )); + $this->assertFalse($cache->prune()); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->will($this->returnValue(true)); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableCacheInterface + */ + private function getFailingPruneableMock() + { + $pruneable = $this + ->getMockBuilder(PruneableCacheInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune') + ->will($this->returnValue(false)); + + return $pruneable; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|CacheInterface + */ + private function getNonPruneableMock() + { + return $this + ->getMockBuilder(CacheInterface::class) + ->getMock(); + } +} + +interface PruneableCacheInterface extends PruneableInterface, CacheInterface +{ +} diff --git a/vendor/symfony/cache/Tests/Simple/DoctrineCacheTest.php b/vendor/symfony/cache/Tests/Simple/DoctrineCacheTest.php new file mode 100644 index 0000000..127c968 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/DoctrineCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\DoctrineCache; +use Symfony\Component\Cache\Tests\Fixtures\ArrayCache; + +/** + * @group time-sensitive + */ +class DoctrineCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testObjectDoesNotChangeInCache' => 'ArrayCache does not use serialize/unserialize', + 'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize', + ); + + public function createSimpleCache($defaultLifetime = 0) + { + return new DoctrineCache(new ArrayCache($defaultLifetime), '', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/FilesystemCacheTest.php b/vendor/symfony/cache/Tests/Simple/FilesystemCacheTest.php new file mode 100644 index 0000000..620305a --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/FilesystemCacheTest.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\Cache\Tests\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Simple\FilesystemCache; + +/** + * @group time-sensitive + */ +class FilesystemCacheTest extends CacheTestCase +{ + public function createSimpleCache($defaultLifetime = 0) + { + return new FilesystemCache('', $defaultLifetime); + } + + protected function isPruned(CacheInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/MemcachedCacheTest.php b/vendor/symfony/cache/Tests/Simple/MemcachedCacheTest.php new file mode 100644 index 0000000..b46d7e4 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/MemcachedCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Simple\MemcachedCache; + +class MemcachedCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testSetTtl' => 'Testing expiration slows down the test suite', + 'testSetMultipleTtl' => 'Testing expiration slows down the test suite', + 'testDefaultLifeTime' => 'Testing expiration slows down the test suite', + ); + + protected static $client; + + public static function setupBeforeClass() + { + if (!MemcachedCache::isSupported()) { + self::markTestSkipped('Extension memcached >=2.2.0 required.'); + } + self::$client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST')); + self::$client->get('foo'); + $code = self::$client->getResultCode(); + + if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) { + self::markTestSkipped('Memcached error: '.strtolower(self::$client->getResultMessage())); + } + } + + public function createSimpleCache($defaultLifetime = 0) + { + $client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), array('binary_protocol' => false)) : self::$client; + + return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } + + public function testCreatePersistentConnectionShouldNotDupServerList() + { + $instance = MemcachedCache::createConnection('memcached://'.getenv('MEMCACHED_HOST'), array('persistent_id' => 'persistent')); + $this->assertCount(1, $instance->getServerList()); + + $instance = MemcachedCache::createConnection('memcached://'.getenv('MEMCACHED_HOST'), array('persistent_id' => 'persistent')); + $this->assertCount(1, $instance->getServerList()); + } + + public function testOptions() + { + $client = MemcachedCache::createConnection(array(), array( + 'libketama_compatible' => false, + 'distribution' => 'modula', + 'compression' => true, + 'serializer' => 'php', + 'hash' => 'md5', + )); + + $this->assertSame(\Memcached::SERIALIZER_PHP, $client->getOption(\Memcached::OPT_SERIALIZER)); + $this->assertSame(\Memcached::HASH_MD5, $client->getOption(\Memcached::OPT_HASH)); + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(0, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + $this->assertSame(\Memcached::DISTRIBUTION_MODULA, $client->getOption(\Memcached::OPT_DISTRIBUTION)); + } + + /** + * @dataProvider provideBadOptions + * @expectedException \ErrorException + * @expectedExceptionMessage constant(): Couldn't find constant Memcached:: + */ + public function testBadOptions($name, $value) + { + MemcachedCache::createConnection(array(), array($name => $value)); + } + + public function provideBadOptions() + { + return array( + array('foo', 'bar'), + array('hash', 'zyx'), + array('serializer', 'zyx'), + array('distribution', 'zyx'), + ); + } + + public function testDefaultOptions() + { + $this->assertTrue(MemcachedCache::isSupported()); + + $client = MemcachedCache::createConnection(array()); + + $this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL)); + $this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE)); + } + + /** + * @expectedException \Symfony\Component\Cache\Exception\CacheException + * @expectedExceptionMessage MemcachedAdapter: "serializer" option must be "php" or "igbinary". + */ + public function testOptionSerializer() + { + if (!\Memcached::HAVE_JSON) { + $this->markTestSkipped('Memcached::HAVE_JSON required'); + } + + new MemcachedCache(MemcachedCache::createConnection(array(), array('serializer' => 'json'))); + } + + /** + * @dataProvider provideServersSetting + */ + public function testServersSetting($dsn, $host, $port) + { + $client1 = MemcachedCache::createConnection($dsn); + $client2 = MemcachedCache::createConnection(array($dsn)); + $client3 = MemcachedCache::createConnection(array(array($host, $port))); + $expect = array( + 'host' => $host, + 'port' => $port, + ); + + $f = function ($s) { return array('host' => $s['host'], 'port' => $s['port']); }; + $this->assertSame(array($expect), array_map($f, $client1->getServerList())); + $this->assertSame(array($expect), array_map($f, $client2->getServerList())); + $this->assertSame(array($expect), array_map($f, $client3->getServerList())); + } + + public function provideServersSetting() + { + yield array( + 'memcached://127.0.0.1/50', + '127.0.0.1', + 11211, + ); + yield array( + 'memcached://localhost:11222?weight=25', + 'localhost', + 11222, + ); + if (ini_get('memcached.use_sasl')) { + yield array( + 'memcached://user:password@127.0.0.1?weight=50', + '127.0.0.1', + 11211, + ); + } + yield array( + 'memcached:///var/run/memcached.sock?weight=25', + '/var/run/memcached.sock', + 0, + ); + yield array( + 'memcached:///var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ); + if (ini_get('memcached.use_sasl')) { + yield array( + 'memcached://user:password@/var/local/run/memcached.socket?weight=25', + '/var/local/run/memcached.socket', + 0, + ); + } + } +} diff --git a/vendor/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.php b/vendor/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.php new file mode 100644 index 0000000..43cadf9 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/MemcachedCacheTextModeTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Simple\MemcachedCache; + +class MemcachedCacheTextModeTest extends MemcachedCacheTest +{ + public function createSimpleCache($defaultLifetime = 0) + { + $client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), array('binary_protocol' => false)); + + return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/NullCacheTest.php b/vendor/symfony/cache/Tests/Simple/NullCacheTest.php new file mode 100644 index 0000000..7b760fd --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/NullCacheTest.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\Cache\Tests\Simple; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Simple\NullCache; + +/** + * @group time-sensitive + */ +class NullCacheTest extends TestCase +{ + public function createCachePool() + { + return new NullCache(); + } + + public function testGetItem() + { + $cache = $this->createCachePool(); + + $this->assertNull($cache->get('key')); + } + + public function testHas() + { + $this->assertFalse($this->createCachePool()->has('key')); + } + + public function testGetMultiple() + { + $cache = $this->createCachePool(); + + $keys = array('foo', 'bar', 'baz', 'biz'); + + $default = new \stdClass(); + $items = $cache->getMultiple($keys, $default); + $count = 0; + + foreach ($items as $key => $item) { + $this->assertContains($key, $keys, 'Cache key can not change.'); + $this->assertSame($default, $item); + + // Remove $key for $keys + foreach ($keys as $k => $v) { + if ($v === $key) { + unset($keys[$k]); + } + } + + ++$count; + } + + $this->assertSame(4, $count); + } + + public function testClear() + { + $this->assertTrue($this->createCachePool()->clear()); + } + + public function testDelete() + { + $this->assertTrue($this->createCachePool()->delete('key')); + } + + public function testDeleteMultiple() + { + $this->assertTrue($this->createCachePool()->deleteMultiple(array('key', 'foo', 'bar'))); + } + + public function testSet() + { + $cache = $this->createCachePool(); + + $this->assertFalse($cache->set('key', 'val')); + $this->assertNull($cache->get('key')); + } + + public function testSetMultiple() + { + $cache = $this->createCachePool(); + + $this->assertFalse($cache->setMultiple(array('key' => 'val'))); + $this->assertNull($cache->get('key')); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/PdoCacheTest.php b/vendor/symfony/cache/Tests/Simple/PdoCacheTest.php new file mode 100644 index 0000000..665db09 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/PdoCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\PdoCache; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoCacheTest extends CacheTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoCache('sqlite:'.self::$dbFile); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createSimpleCache($defaultLifetime = 0) + { + return new PdoCache('sqlite:'.self::$dbFile, 'ns', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/PdoDbalCacheTest.php b/vendor/symfony/cache/Tests/Simple/PdoDbalCacheTest.php new file mode 100644 index 0000000..158e2c8 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/PdoDbalCacheTest.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\Cache\Tests\Simple; + +use Doctrine\DBAL\DriverManager; +use Symfony\Component\Cache\Simple\PdoCache; +use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait; + +/** + * @group time-sensitive + */ +class PdoDbalCacheTest extends CacheTestCase +{ + use PdoPruneableTrait; + + protected static $dbFile; + + public static function setupBeforeClass() + { + if (!\extension_loaded('pdo_sqlite')) { + self::markTestSkipped('Extension pdo_sqlite required.'); + } + + self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache'); + + $pool = new PdoCache(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile))); + $pool->createTable(); + } + + public static function tearDownAfterClass() + { + @unlink(self::$dbFile); + } + + public function createSimpleCache($defaultLifetime = 0) + { + return new PdoCache(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)), '', $defaultLifetime); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/PhpArrayCacheTest.php b/vendor/symfony/cache/Tests/Simple/PhpArrayCacheTest.php new file mode 100644 index 0000000..b4862c6 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/PhpArrayCacheTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\NullCache; +use Symfony\Component\Cache\Simple\PhpArrayCache; +use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest; + +/** + * @group time-sensitive + */ +class PhpArrayCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testBasicUsageWithLongKey' => 'PhpArrayCache does no writes', + + 'testDelete' => 'PhpArrayCache does no writes', + 'testDeleteMultiple' => 'PhpArrayCache does no writes', + 'testDeleteMultipleGenerator' => 'PhpArrayCache does no writes', + + 'testSetTtl' => 'PhpArrayCache does no expiration', + 'testSetMultipleTtl' => 'PhpArrayCache does no expiration', + 'testSetExpiredTtl' => 'PhpArrayCache does no expiration', + 'testSetMultipleExpiredTtl' => 'PhpArrayCache does no expiration', + + 'testGetInvalidKeys' => 'PhpArrayCache does no validation', + 'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetInvalidTtl' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation', + 'testHasInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetValidData' => 'PhpArrayCache does no validation', + + 'testDefaultLifeTime' => 'PhpArrayCache does not allow configuring a default lifetime.', + 'testPrune' => 'PhpArrayCache just proxies', + ); + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createSimpleCache() + { + return new PhpArrayCacheWrapper(self::$file, new NullCache()); + } + + public function testStore() + { + $arrayWithRefs = array(); + $arrayWithRefs[0] = 123; + $arrayWithRefs[1] = &$arrayWithRefs[0]; + + $object = (object) array( + 'foo' => 'bar', + 'foo2' => 'bar2', + ); + + $expected = array( + 'null' => null, + 'serializedString' => serialize($object), + 'arrayWithRefs' => $arrayWithRefs, + 'object' => $object, + 'arrayWithObject' => array('bar' => $object), + ); + + $cache = new PhpArrayCache(self::$file, new NullCache()); + $cache->warmUp($expected); + + foreach ($expected as $key => $value) { + $this->assertSame(serialize($value), serialize($cache->get($key)), 'Warm up should create a PHP file that OPCache can load in memory'); + } + } + + public function testStoredFile() + { + $expected = array( + 'integer' => 42, + 'float' => 42.42, + 'boolean' => true, + 'array_simple' => array('foo', 'bar'), + 'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'), + ); + + $cache = new PhpArrayCache(self::$file, new NullCache()); + $cache->warmUp($expected); + + $values = eval(substr(file_get_contents(self::$file), 6)); + + $this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory'); + } +} + +class PhpArrayCacheWrapper extends PhpArrayCache +{ + public function set($key, $value, $ttl = null) + { + \call_user_func(\Closure::bind(function () use ($key, $value) { + $this->values[$key] = $value; + $this->warmUp($this->values); + $this->values = eval(substr(file_get_contents($this->file), 6)); + }, $this, PhpArrayCache::class)); + + return true; + } + + public function setMultiple($values, $ttl = null) + { + if (!\is_array($values) && !$values instanceof \Traversable) { + return parent::setMultiple($values, $ttl); + } + \call_user_func(\Closure::bind(function () use ($values) { + foreach ($values as $key => $value) { + $this->values[$key] = $value; + } + $this->warmUp($this->values); + $this->values = eval(substr(file_get_contents($this->file), 6)); + }, $this, PhpArrayCache::class)); + + return true; + } +} diff --git a/vendor/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php b/vendor/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.php new file mode 100644 index 0000000..4b6a94f --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/PhpArrayCacheWithFallbackTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\FilesystemCache; +use Symfony\Component\Cache\Simple\PhpArrayCache; +use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest; + +/** + * @group time-sensitive + */ +class PhpArrayCacheWithFallbackTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testGetInvalidKeys' => 'PhpArrayCache does no validation', + 'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteInvalidKeys' => 'PhpArrayCache does no validation', + 'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation', + //'testSetValidData' => 'PhpArrayCache does no validation', + 'testSetInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetInvalidTtl' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation', + 'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation', + 'testHasInvalidKeys' => 'PhpArrayCache does no validation', + 'testPrune' => 'PhpArrayCache just proxies', + ); + + protected static $file; + + public static function setupBeforeClass() + { + self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php'; + } + + protected function tearDown() + { + if (file_exists(sys_get_temp_dir().'/symfony-cache')) { + FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache'); + } + } + + public function createSimpleCache($defaultLifetime = 0) + { + return new PhpArrayCache(self::$file, new FilesystemCache('php-array-fallback', $defaultLifetime)); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/PhpFilesCacheTest.php b/vendor/symfony/cache/Tests/Simple/PhpFilesCacheTest.php new file mode 100644 index 0000000..7a40268 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/PhpFilesCacheTest.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\Cache\Tests\Simple; + +use Psr\SimpleCache\CacheInterface; +use Symfony\Component\Cache\Simple\PhpFilesCache; + +/** + * @group time-sensitive + */ +class PhpFilesCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testDefaultLifeTime' => 'PhpFilesCache does not allow configuring a default lifetime.', + ); + + public function createSimpleCache() + { + if (!PhpFilesCache::isSupported()) { + $this->markTestSkipped('OPcache extension is not enabled.'); + } + + return new PhpFilesCache('sf-cache'); + } + + protected function isPruned(CacheInterface $cache, $name) + { + $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); + $getFileMethod->setAccessible(true); + + return !file_exists($getFileMethod->invoke($cache, $name)); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/Psr6CacheTest.php b/vendor/symfony/cache/Tests/Simple/Psr6CacheTest.php new file mode 100644 index 0000000..7858289 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/Psr6CacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Simple\Psr6Cache; + +/** + * @group time-sensitive + */ +class Psr6CacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testPrune' => 'Psr6Cache just proxies', + ); + + public function createSimpleCache($defaultLifetime = 0) + { + return new Psr6Cache(new FilesystemAdapter('', $defaultLifetime)); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/RedisArrayCacheTest.php b/vendor/symfony/cache/Tests/Simple/RedisArrayCacheTest.php new file mode 100644 index 0000000..3c903c8 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/RedisArrayCacheTest.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\Cache\Tests\Simple; + +class RedisArrayCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + if (!class_exists('RedisArray')) { + self::markTestSkipped('The RedisArray class is required.'); + } + self::$redis = new \RedisArray(array(getenv('REDIS_HOST')), array('lazy_connect' => true)); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/RedisCacheTest.php b/vendor/symfony/cache/Tests/Simple/RedisCacheTest.php new file mode 100644 index 0000000..d33421f --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/RedisCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\RedisCache; + +class RedisCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = RedisCache::createConnection('redis://'.getenv('REDIS_HOST')); + } + + public function testCreateConnection() + { + $redisHost = getenv('REDIS_HOST'); + + $redis = RedisCache::createConnection('redis://'.$redisHost); + $this->assertInstanceOf(\Redis::class, $redis); + $this->assertTrue($redis->isConnected()); + $this->assertSame(0, $redis->getDbNum()); + + $redis = RedisCache::createConnection('redis://'.$redisHost.'/2'); + $this->assertSame(2, $redis->getDbNum()); + + $redis = RedisCache::createConnection('redis://'.$redisHost, array('timeout' => 3)); + $this->assertEquals(3, $redis->getTimeout()); + + $redis = RedisCache::createConnection('redis://'.$redisHost.'?timeout=4'); + $this->assertEquals(4, $redis->getTimeout()); + + $redis = RedisCache::createConnection('redis://'.$redisHost, array('read_timeout' => 5)); + $this->assertEquals(5, $redis->getReadTimeout()); + } + + /** + * @dataProvider provideFailedCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Redis connection failed + */ + public function testFailedCreateConnection($dsn) + { + RedisCache::createConnection($dsn); + } + + public function provideFailedCreateConnection() + { + return array( + array('redis://localhost:1234'), + array('redis://foo@localhost'), + array('redis://localhost/123'), + ); + } + + /** + * @dataProvider provideInvalidCreateConnection + * @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid Redis DSN + */ + public function testInvalidCreateConnection($dsn) + { + RedisCache::createConnection($dsn); + } + + public function provideInvalidCreateConnection() + { + return array( + array('foo://localhost'), + array('redis://'), + ); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/RedisClusterCacheTest.php b/vendor/symfony/cache/Tests/Simple/RedisClusterCacheTest.php new file mode 100644 index 0000000..99d4e51 --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/RedisClusterCacheTest.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\Cache\Tests\Simple; + +class RedisClusterCacheTest extends AbstractRedisCacheTest +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + + self::$redis = new \RedisCluster(null, explode(' ', $hosts)); + } +} diff --git a/vendor/symfony/cache/Tests/Simple/TraceableCacheTest.php b/vendor/symfony/cache/Tests/Simple/TraceableCacheTest.php new file mode 100644 index 0000000..535f93d --- /dev/null +++ b/vendor/symfony/cache/Tests/Simple/TraceableCacheTest.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\Cache\Tests\Simple; + +use Symfony\Component\Cache\Simple\FilesystemCache; +use Symfony\Component\Cache\Simple\TraceableCache; + +/** + * @group time-sensitive + */ +class TraceableCacheTest extends CacheTestCase +{ + protected $skippedTests = array( + 'testPrune' => 'TraceableCache just proxies', + ); + + public function createSimpleCache($defaultLifetime = 0) + { + return new TraceableCache(new FilesystemCache('', $defaultLifetime)); + } + + public function testGetMissTrace() + { + $pool = $this->createSimpleCache(); + $pool->get('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('get', $call->name); + $this->assertSame(array('k' => false), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testGetHitTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k', 'foo'); + $pool->get('k'); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame(1, $call->hits); + $this->assertSame(0, $call->misses); + } + + public function testGetMultipleMissTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k1', 123); + $values = $pool->getMultiple(array('k0', 'k1')); + foreach ($values as $value) { + } + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('getMultiple', $call->name); + $this->assertSame(array('k1' => true, 'k0' => false), $call->result); + $this->assertSame(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasMissTrace() + { + $pool = $this->createSimpleCache(); + $pool->has('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('has', $call->name); + $this->assertSame(array('k' => false), $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasHitTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k', 'foo'); + $pool->has('k'); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertSame('has', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteTrace() + { + $pool = $this->createSimpleCache(); + $pool->delete('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('delete', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteMultipleTrace() + { + $pool = $this->createSimpleCache(); + $arg = array('k0', 'k1'); + $pool->deleteMultiple($arg); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('deleteMultiple', $call->name); + $this->assertSame(array('keys' => $arg, 'result' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testTraceSetTrace() + { + $pool = $this->createSimpleCache(); + $pool->set('k', 'foo'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('set', $call->name); + $this->assertSame(array('k' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSetMultipleTrace() + { + $pool = $this->createSimpleCache(); + $pool->setMultiple(array('k' => 'foo')); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertSame('setMultiple', $call->name); + $this->assertSame(array('keys' => array('k'), 'result' => true), $call->result); + $this->assertSame(0, $call->hits); + $this->assertSame(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} diff --git a/vendor/symfony/cache/Tests/Traits/PdoPruneableTrait.php b/vendor/symfony/cache/Tests/Traits/PdoPruneableTrait.php new file mode 100644 index 0000000..3b1e112 --- /dev/null +++ b/vendor/symfony/cache/Tests/Traits/PdoPruneableTrait.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\Cache\Tests\Traits; + +trait PdoPruneableTrait +{ + protected function isPruned($cache, $name) + { + $o = new \ReflectionObject($cache); + + if (!$o->hasMethod('getConnection')) { + self::fail('Cache does not have "getConnection()" method.'); + } + + $getPdoConn = $o->getMethod('getConnection'); + $getPdoConn->setAccessible(true); + + /** @var \Doctrine\DBAL\Statement $select */ + $select = $getPdoConn->invoke($cache)->prepare('SELECT 1 FROM cache_items WHERE item_id LIKE :id'); + $select->bindValue(':id', sprintf('%%%s', $name)); + $select->execute(); + + return 0 === \count($select->fetchAll(\PDO::FETCH_COLUMN)); + } +} diff --git a/vendor/symfony/cache/Traits/AbstractTrait.php b/vendor/symfony/cache/Traits/AbstractTrait.php new file mode 100644 index 0000000..87aeba9 --- /dev/null +++ b/vendor/symfony/cache/Traits/AbstractTrait.php @@ -0,0 +1,268 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait AbstractTrait +{ + use LoggerAwareTrait; + + private $namespace; + private $namespaceVersion = ''; + private $versioningIsEnabled = false; + private $deferred = array(); + + /** + * @var int|null The maximum length to enforce for identifiers or null when no limit applies + */ + protected $maxIdLength; + + /** + * Fetches several cache items. + * + * @param array $ids The cache identifiers to fetch + * + * @return array|\Traversable The corresponding values found in the cache + */ + abstract protected function doFetch(array $ids); + + /** + * Confirms if the cache contains specified cache item. + * + * @param string $id The identifier for which to check existence + * + * @return bool True if item exists in the cache, false otherwise + */ + abstract protected function doHave($id); + + /** + * Deletes all items in the pool. + * + * @param string The prefix used for all identifiers managed by this pool + * + * @return bool True if the pool was successfully cleared, false otherwise + */ + abstract protected function doClear($namespace); + + /** + * Removes multiple items from the pool. + * + * @param array $ids An array of identifiers that should be removed from the pool + * + * @return bool True if the items were successfully removed, false otherwise + */ + abstract protected function doDelete(array $ids); + + /** + * Persists several cache items immediately. + * + * @param array $values The values to cache, indexed by their cache identifier + * @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning + * + * @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not + */ + abstract protected function doSave(array $values, $lifetime); + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + $id = $this->getId($key); + + if (isset($this->deferred[$key])) { + $this->commit(); + } + + try { + return $this->doHave($id); + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached', array('key' => $key, 'exception' => $e)); + + return false; + } + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->deferred = array(); + if ($cleared = $this->versioningIsEnabled) { + $namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), ':', 5); + try { + $cleared = $this->doSave(array('@'.$this->namespace => $namespaceVersion), 0); + } catch (\Exception $e) { + $cleared = false; + } + if ($cleared = true === $cleared || array() === $cleared) { + $this->namespaceVersion = $namespaceVersion; + } + } + + try { + return $this->doClear($this->namespace) || $cleared; + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to clear the cache', array('exception' => $e)); + + return false; + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + return $this->deleteItems(array($key)); + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $ids = array(); + + foreach ($keys as $key) { + $ids[$key] = $this->getId($key); + unset($this->deferred[$key]); + } + + try { + if ($this->doDelete($ids)) { + return true; + } + } catch (\Exception $e) { + } + + $ok = true; + + // When bulk-delete failed, retry each item individually + foreach ($ids as $key => $id) { + try { + $e = null; + if ($this->doDelete(array($id))) { + continue; + } + } catch (\Exception $e) { + } + CacheItem::log($this->logger, 'Failed to delete key "{key}"', array('key' => $key, 'exception' => $e)); + $ok = false; + } + + return $ok; + } + + /** + * Enables/disables versioning of items. + * + * When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed, + * but old keys may need garbage collection and extra round-trips to the back-end are required. + * + * Calling this method also clears the memoized namespace version and thus forces a resynchonization of it. + * + * @param bool $enable + * + * @return bool the previous state of versioning + */ + public function enableVersioning($enable = true) + { + $wasEnabled = $this->versioningIsEnabled; + $this->versioningIsEnabled = (bool) $enable; + $this->namespaceVersion = ''; + + return $wasEnabled; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->deferred) { + $this->commit(); + } + $this->namespaceVersion = ''; + } + + /** + * Like the native unserialize() function but throws an exception if anything goes wrong. + * + * @param string $value + * + * @return mixed + * + * @throws \Exception + */ + protected static function unserialize($value) + { + if ('b:0;' === $value) { + return false; + } + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + if (false !== $value = unserialize($value)) { + return $value; + } + throw new \DomainException('Failed to unserialize cached value'); + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + private function getId($key) + { + CacheItem::validateKey($key); + + if ($this->versioningIsEnabled && '' === $this->namespaceVersion) { + $this->namespaceVersion = '1:'; + try { + foreach ($this->doFetch(array('@'.$this->namespace)) as $v) { + $this->namespaceVersion = $v; + } + if ('1:' === $this->namespaceVersion) { + $this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), ':', 5); + $this->doSave(array('@'.$this->namespace => $this->namespaceVersion), 0); + } + } catch (\Exception $e) { + } + } + + if (null === $this->maxIdLength) { + return $this->namespace.$this->namespaceVersion.$key; + } + if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) { + $id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -(\strlen($this->namespaceVersion) + 22)); + } + + return $id; + } + + /** + * @internal + */ + public static function handleUnserializeCallback($class) + { + throw new \DomainException('Class not found: '.$class); + } +} diff --git a/vendor/symfony/cache/Traits/ApcuTrait.php b/vendor/symfony/cache/Traits/ApcuTrait.php new file mode 100644 index 0000000..4812e80 --- /dev/null +++ b/vendor/symfony/cache/Traits/ApcuTrait.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\Cache\Traits; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\CacheException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ApcuTrait +{ + public static function isSupported() + { + return \function_exists('apcu_fetch') && ini_get('apc.enabled'); + } + + private function init($namespace, $defaultLifetime, $version) + { + if (!static::isSupported()) { + throw new CacheException('APCu is not enabled'); + } + if ('cli' === \PHP_SAPI) { + ini_set('apc.use_request_time', 0); + } + parent::__construct($namespace, $defaultLifetime); + + if (null !== $version) { + CacheItem::validateKey($version); + + if (!apcu_exists($version.'@'.$namespace)) { + $this->doClear($namespace); + apcu_add($version.'@'.$namespace, null); + } + } + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + try { + foreach (apcu_fetch($ids, $ok) ?: array() as $k => $v) { + if (null !== $v || $ok) { + yield $k => $v; + } + } + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return apcu_exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + return isset($namespace[0]) && class_exists('APCuIterator', false) && ('cli' !== \PHP_SAPI || ini_get('apc.enable_cli')) + ? apcu_delete(new \APCuIterator(sprintf('/^%s/', preg_quote($namespace, '/')), APC_ITER_KEY)) + : apcu_clear_cache(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + foreach ($ids as $id) { + apcu_delete($id); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + try { + if (false === $failures = apcu_store($values, null, $lifetime)) { + $failures = $values; + } + + return array_keys($failures); + } catch (\Throwable $e) { + if (1 === \count($values)) { + // Workaround https://github.com/krakjoe/apcu/issues/170 + apcu_delete(key($values)); + } + + throw $e; + } + } +} diff --git a/vendor/symfony/cache/Traits/ArrayTrait.php b/vendor/symfony/cache/Traits/ArrayTrait.php new file mode 100644 index 0000000..88385ed --- /dev/null +++ b/vendor/symfony/cache/Traits/ArrayTrait.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\Cache\Traits; + +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ArrayTrait +{ + use LoggerAwareTrait; + + private $storeSerialized; + private $values = array(); + private $expiries = array(); + + /** + * Returns all cached values, with cache miss as null. + * + * @return array + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + CacheItem::validateKey($key); + + return isset($this->expiries[$key]) && ($this->expiries[$key] > time() || !$this->deleteItem($key)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->values = $this->expiries = array(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + CacheItem::validateKey($key); + + unset($this->values[$key], $this->expiries[$key]); + + return true; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->clear(); + } + + private function generateItems(array $keys, $now, $f) + { + foreach ($keys as $i => $key) { + try { + if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) { + $this->values[$key] = $value = null; + } elseif (!$this->storeSerialized) { + $value = $this->values[$key]; + } elseif ('b:0;' === $value = $this->values[$key]) { + $value = false; + } elseif (false === $value = unserialize($value)) { + $this->values[$key] = $value = null; + $isHit = false; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e)); + $this->values[$key] = $value = null; + $isHit = false; + } + unset($keys[$i]); + + yield $key => $f($key, $value, $isHit); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } +} diff --git a/vendor/symfony/cache/Traits/DoctrineTrait.php b/vendor/symfony/cache/Traits/DoctrineTrait.php new file mode 100644 index 0000000..c87ecab --- /dev/null +++ b/vendor/symfony/cache/Traits/DoctrineTrait.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\Cache\Traits; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait DoctrineTrait +{ + private $provider; + + /** + * {@inheritdoc} + */ + public function reset() + { + parent::reset(); + $this->provider->setNamespace($this->provider->getNamespace()); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback'); + try { + return $this->provider->fetchMultiple($ids); + } catch (\Error $e) { + $trace = $e->getTrace(); + + if (isset($trace[0]['function']) && !isset($trace[0]['class'])) { + switch ($trace[0]['function']) { + case 'unserialize': + case 'apcu_fetch': + case 'apc_fetch': + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } + } + + throw $e; + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return $this->provider->contains($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $namespace = $this->provider->getNamespace(); + + return isset($namespace[0]) + ? $this->provider->deleteAll() + : $this->provider->flushAll(); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + foreach ($ids as $id) { + $ok = $this->provider->delete($id) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + return $this->provider->saveMultiple($values, $lifetime); + } +} diff --git a/vendor/symfony/cache/Traits/FilesystemCommonTrait.php b/vendor/symfony/cache/Traits/FilesystemCommonTrait.php new file mode 100644 index 0000000..274eb73 --- /dev/null +++ b/vendor/symfony/cache/Traits/FilesystemCommonTrait.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\Cache\Traits; + +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait FilesystemCommonTrait +{ + private $directory; + private $tmp; + + private function init($namespace, $directory) + { + if (!isset($directory[0])) { + $directory = sys_get_temp_dir().'/symfony-cache'; + } else { + $directory = realpath($directory) ?: $directory; + } + if (isset($namespace[0])) { + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + $directory .= \DIRECTORY_SEPARATOR.$namespace; + } + if (!file_exists($directory)) { + @mkdir($directory, 0777, true); + } + $directory .= \DIRECTORY_SEPARATOR; + // On Windows the whole path is limited to 258 chars + if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) { + throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory)); + } + + $this->directory = $directory; + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $ok = true; + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) { + $ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + + foreach ($ids as $id) { + $file = $this->getFile($id); + $ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + private function write($file, $data, $expiresAt = null) + { + set_error_handler(__CLASS__.'::throwError'); + try { + if (null === $this->tmp) { + $this->tmp = $this->directory.uniqid('', true); + } + file_put_contents($this->tmp, $data); + + if (null !== $expiresAt) { + touch($this->tmp, $expiresAt); + } + + return rename($this->tmp, $file); + } finally { + restore_error_handler(); + } + } + + private function getFile($id, $mkdir = false) + { + $hash = str_replace('/', '-', base64_encode(hash('sha256', static::class.$id, true))); + $dir = $this->directory.strtoupper($hash[0].\DIRECTORY_SEPARATOR.$hash[1].\DIRECTORY_SEPARATOR); + + if ($mkdir && !file_exists($dir)) { + @mkdir($dir, 0777, true); + } + + return $dir.substr($hash, 2, 20); + } + + /** + * @internal + */ + public static function throwError($type, $message, $file, $line) + { + throw new \ErrorException($message, 0, $type, $file, $line); + } + + public function __destruct() + { + if (method_exists(parent::class, '__destruct')) { + parent::__destruct(); + } + if (null !== $this->tmp && file_exists($this->tmp)) { + unlink($this->tmp); + } + } +} diff --git a/vendor/symfony/cache/Traits/FilesystemTrait.php b/vendor/symfony/cache/Traits/FilesystemTrait.php new file mode 100644 index 0000000..23974b3 --- /dev/null +++ b/vendor/symfony/cache/Traits/FilesystemTrait.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\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; + +/** + * @author Nicolas Grekas + * @author Rob Frawley 2nd + * + * @internal + */ +trait FilesystemTrait +{ + use FilesystemCommonTrait; + + /** + * @return bool + */ + public function prune() + { + $time = time(); + $pruned = true; + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if (!$h = @fopen($file, 'rb')) { + continue; + } + + if (($expiresAt = (int) fgets($h)) && $time >= $expiresAt) { + fclose($h); + $pruned = @unlink($file) && !file_exists($file) && $pruned; + } else { + fclose($h); + } + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = array(); + $now = time(); + + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!file_exists($file) || !$h = @fopen($file, 'rb')) { + continue; + } + if (($expiresAt = (int) fgets($h)) && $now >= $expiresAt) { + fclose($h); + @unlink($file); + } else { + $i = rawurldecode(rtrim(fgets($h))); + $value = stream_get_contents($h); + fclose($h); + if ($i === $id) { + $values[$id] = parent::unserialize($value); + } + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + $file = $this->getFile($id); + + return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id))); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $ok = true; + $expiresAt = $lifetime ? (time() + $lifetime) : 0; + + foreach ($values as $id => $value) { + $ok = $this->write($this->getFile($id, true), $expiresAt."\n".rawurlencode($id)."\n".serialize($value), $expiresAt) && $ok; + } + + if (!$ok && !is_writable($this->directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory)); + } + + return $ok; + } +} diff --git a/vendor/symfony/cache/Traits/MemcachedTrait.php b/vendor/symfony/cache/Traits/MemcachedTrait.php new file mode 100644 index 0000000..5983d9e --- /dev/null +++ b/vendor/symfony/cache/Traits/MemcachedTrait.php @@ -0,0 +1,296 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Rob Frawley 2nd + * @author Nicolas Grekas + * + * @internal + */ +trait MemcachedTrait +{ + private static $defaultClientOptions = array( + 'persistent_id' => null, + 'username' => null, + 'password' => null, + 'serializer' => 'php', + ); + + private $client; + private $lazyClient; + + public static function isSupported() + { + return \extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>='); + } + + private function init(\Memcached $client, $namespace, $defaultLifetime) + { + if (!static::isSupported()) { + throw new CacheException('Memcached >= 2.2.0 is required'); + } + if ('Memcached' === \get_class($client)) { + $opt = $client->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + $this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY)); + $this->client = $client; + } else { + $this->lazyClient = $client; + } + + parent::__construct($namespace, $defaultLifetime); + $this->enableVersioning(); + } + + /** + * Creates a Memcached instance. + * + * By default, the binary protocol, no block, and libketama compatible options are enabled. + * + * Examples for servers: + * - 'memcached://user:pass@localhost?weight=33' + * - array(array('localhost', 11211, 33)) + * + * @param array[]|string|string[] An array of servers, a DSN, or an array of DSNs + * @param array An array of options + * + * @return \Memcached + * + * @throws \ErrorException When invalid options or servers are provided + */ + public static function createConnection($servers, array $options = array()) + { + if (\is_string($servers)) { + $servers = array($servers); + } elseif (!\is_array($servers)) { + throw new InvalidArgumentException(sprintf('MemcachedAdapter::createClient() expects array or string as first argument, %s given.', \gettype($servers))); + } + if (!static::isSupported()) { + throw new CacheException('Memcached >= 2.2.0 is required'); + } + set_error_handler(function ($type, $msg, $file, $line) { throw new \ErrorException($msg, 0, $type, $file, $line); }); + try { + $options += static::$defaultClientOptions; + $client = new \Memcached($options['persistent_id']); + $username = $options['username']; + $password = $options['password']; + + // parse any DSN in $servers + foreach ($servers as $i => $dsn) { + if (\is_array($dsn)) { + continue; + } + if (0 !== strpos($dsn, 'memcached://')) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s does not start with "memcached://"', $dsn)); + } + $params = preg_replace_callback('#^memcached://(?:([^@]*+)@)?#', function ($m) use (&$username, &$password) { + if (!empty($m[1])) { + list($username, $password) = explode(':', $m[1], 2) + array(1 => null); + } + + return 'file://'; + }, $dsn); + if (false === $params = parse_url($params)) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn)); + } + if (!isset($params['host']) && !isset($params['path'])) { + throw new InvalidArgumentException(sprintf('Invalid Memcached DSN: %s', $dsn)); + } + if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['weight'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } + $params += array( + 'host' => isset($params['host']) ? $params['host'] : $params['path'], + 'port' => isset($params['host']) ? 11211 : null, + 'weight' => 0, + ); + if (isset($params['query'])) { + parse_str($params['query'], $query); + $params += $query; + $options = $query + $options; + } + + $servers[$i] = array($params['host'], $params['port'], $params['weight']); + } + + // set client's options + unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']); + $options = array_change_key_case($options, CASE_UPPER); + $client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true); + $client->setOption(\Memcached::OPT_NO_BLOCK, true); + $client->setOption(\Memcached::OPT_TCP_NODELAY, true); + if (!array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) { + $client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true); + } + foreach ($options as $name => $value) { + if (\is_int($name)) { + continue; + } + if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) { + $value = \constant('Memcached::'.$name.'_'.strtoupper($value)); + } + $opt = \constant('Memcached::OPT_'.$name); + + unset($options[$name]); + $options[$opt] = $value; + } + $client->setOptions($options); + + // set client's servers, taking care of persistent connections + if (!$client->isPristine()) { + $oldServers = array(); + foreach ($client->getServerList() as $server) { + $oldServers[] = array($server['host'], $server['port']); + } + + $newServers = array(); + foreach ($servers as $server) { + if (1 < \count($server)) { + $server = array_values($server); + unset($server[2]); + $server[1] = (int) $server[1]; + } + $newServers[] = $server; + } + + if ($oldServers !== $newServers) { + $client->resetServerList(); + $client->addServers($servers); + } + } else { + $client->addServers($servers); + } + + if (null !== $username || null !== $password) { + if (!method_exists($client, 'setSaslAuthData')) { + trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.'); + } + $client->setSaslAuthData($username, $password); + } + + return $client; + } finally { + restore_error_handler(); + } + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + if ($lifetime && $lifetime > 30 * 86400) { + $lifetime += time(); + } + + $encodedValues = array(); + foreach ($values as $key => $value) { + $encodedValues[rawurlencode($key)] = $value; + } + + return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + $encodedIds = array_map('rawurlencode', $ids); + + $encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds)); + + $result = array(); + foreach ($encodedResult as $key => $value) { + $result[rawurldecode($key)] = $value; + } + + return $result; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return false !== $this->getClient()->get(rawurlencode($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode()); + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + $encodedIds = array_map('rawurlencode', $ids); + foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) { + if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) { + $ok = false; + } + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + return false; + } + + private function checkResultCode($result) + { + $code = $this->client->getResultCode(); + + if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) { + return $result; + } + + throw new CacheException(sprintf('MemcachedAdapter client error: %s.', strtolower($this->client->getResultMessage()))); + } + + /** + * @return \Memcached + */ + private function getClient() + { + if ($this->client) { + return $this->client; + } + + $opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER); + if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) { + throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".'); + } + if ('' !== $prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY)) { + throw new CacheException(sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix)); + } + + return $this->client = $this->lazyClient; + } +} diff --git a/vendor/symfony/cache/Traits/PdoTrait.php b/vendor/symfony/cache/Traits/PdoTrait.php new file mode 100644 index 0000000..a22714c --- /dev/null +++ b/vendor/symfony/cache/Traits/PdoTrait.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\Cache\Traits; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; +use Doctrine\DBAL\Schema\Schema; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @internal + */ +trait PdoTrait +{ + private $conn; + private $dsn; + private $driver; + private $serverVersion; + private $table = 'cache_items'; + private $idCol = 'item_id'; + private $dataCol = 'item_data'; + private $lifetimeCol = 'item_lifetime'; + private $timeCol = 'item_time'; + private $username = ''; + private $password = ''; + private $connectionOptions = array(); + private $namespace; + + private function init($connOrDsn, $namespace, $defaultLifetime, array $options) + { + if (isset($namespace[0]) && preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0])); + } + + if ($connOrDsn instanceof \PDO) { + if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->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->conn = $connOrDsn; + } elseif ($connOrDsn instanceof Connection) { + $this->conn = $connOrDsn; + } elseif (\is_string($connOrDsn)) { + $this->dsn = $connOrDsn; + } else { + throw new InvalidArgumentException(sprintf('"%s" requires PDO or Doctrine\DBAL\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, \is_object($connOrDsn) ? \get_class($connOrDsn) : \gettype($connOrDsn))); + } + + $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->namespace = $namespace; + + parent::__construct($namespace, $defaultLifetime); + } + + /** + * Creates the table to store cache items which can be called once for setup. + * + * Cache ID are saved in a column of maximum length 255. Cache data is + * saved in a BLOB. + * + * @throws \PDOException When the table already exists + * @throws DBALException When the table already exists + * @throws \DomainException When an unsupported PDO driver is used + */ + public function createTable() + { + // connect if we are not yet + $conn = $this->getConnection(); + + if ($conn instanceof Connection) { + $types = array( + 'mysql' => 'binary', + 'sqlite' => 'text', + 'pgsql' => 'string', + 'oci' => 'string', + 'sqlsrv' => 'string', + ); + if (!isset($types[$this->driver])) { + throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + $schema = new Schema(); + $table = $schema->createTable($this->table); + $table->addColumn($this->idCol, $types[$this->driver], array('length' => 255)); + $table->addColumn($this->dataCol, 'blob', array('length' => 16777215)); + $table->addColumn($this->lifetimeCol, 'integer', array('unsigned' => true, 'notnull' => false)); + $table->addColumn($this->timeCol, 'integer', array('unsigned' => true)); + $table->setPrimaryKey(array($this->idCol)); + + foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) { + $conn->exec($sql); + } + + return; + } + + 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(255) NOT NULL PRIMARY KEY, $this->dataCol MEDIUMBLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED, $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, $this->timeCol INTEGER NOT NULL)"; + break; + case 'pgsql': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'oci': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(255) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + case 'sqlsrv': + $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(255) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER, $this->timeCol INTEGER NOT NULL)"; + break; + default: + throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver)); + } + + $conn->exec($sql); + } + + /** + * {@inheritdoc} + */ + public function prune() + { + $deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time"; + + if ('' !== $this->namespace) { + $deleteSql .= " AND $this->idCol LIKE :namespace"; + } + + $delete = $this->getConnection()->prepare($deleteSql); + $delete->bindValue(':time', time(), \PDO::PARAM_INT); + + if ('' !== $this->namespace) { + $delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), \PDO::PARAM_STR); + } + + return $delete->execute(); + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $now = time(); + $expired = array(); + + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "SELECT $this->idCol, CASE WHEN $this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > ? THEN $this->dataCol ELSE NULL END FROM $this->table WHERE $this->idCol IN ($sql)"; + $stmt = $this->getConnection()->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($ids as $id) { + $stmt->bindValue(++$i, $id); + } + $stmt->execute(); + + while ($row = $stmt->fetch(\PDO::FETCH_NUM)) { + if (null === $row[1]) { + $expired[] = $row[0]; + } else { + yield $row[0] => parent::unserialize(\is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + } + } + + if ($expired) { + $sql = str_pad('', (\count($expired) << 1) - 1, '?,'); + $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= ? AND $this->idCol IN ($sql)"; + $stmt = $this->getConnection()->prepare($sql); + $stmt->bindValue($i = 1, $now, \PDO::PARAM_INT); + foreach ($expired as $id) { + $stmt->bindValue(++$i, $id); + } + $stmt->execute(); + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + $sql = "SELECT 1 FROM $this->table WHERE $this->idCol = :id AND ($this->lifetimeCol IS NULL OR $this->lifetimeCol + $this->timeCol > :time)"; + $stmt = $this->getConnection()->prepare($sql); + + $stmt->bindValue(':id', $id); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + return (bool) $stmt->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $conn = $this->getConnection(); + + if ('' === $namespace) { + if ('sqlite' === $this->driver) { + $sql = "DELETE FROM $this->table"; + } else { + $sql = "TRUNCATE TABLE $this->table"; + } + } else { + $sql = "DELETE FROM $this->table WHERE $this->idCol LIKE '$namespace%'"; + } + + $conn->exec($sql); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $sql = str_pad('', (\count($ids) << 1) - 1, '?,'); + $sql = "DELETE FROM $this->table WHERE $this->idCol IN ($sql)"; + $stmt = $this->getConnection()->prepare($sql); + $stmt->execute(array_values($ids)); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $serialized = array(); + $failed = array(); + + foreach ($values as $id => $value) { + try { + $serialized[$id] = serialize($value); + } catch (\Exception $e) { + $failed[] = $id; + } + } + + if (!$serialized) { + return $failed; + } + + $conn = $this->getConnection(); + $driver = $this->driver; + $insertSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time)"; + + switch (true) { + case 'mysql' === $driver: + $sql = $insertSql." ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)"; + break; + case 'oci' === $driver: + // DUAL is Oracle specific dummy table + $sql = "MERGE INTO $this->table USING DUAL 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 'sqlsrv' === $driver && version_compare($this->getServerVersion(), '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 + $sql = "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' === $driver: + $sql = 'INSERT OR REPLACE'.substr($insertSql, 6); + break; + case 'pgsql' === $driver && version_compare($this->getServerVersion(), '9.5', '>='): + $sql = $insertSql." ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)"; + break; + default: + $driver = null; + $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :lifetime, $this->timeCol = :time WHERE $this->idCol = :id"; + break; + } + + $now = time(); + $lifetime = $lifetime ?: null; + $stmt = $conn->prepare($sql); + + if ('sqlsrv' === $driver || 'oci' === $driver) { + $stmt->bindParam(1, $id); + $stmt->bindParam(2, $id); + $stmt->bindParam(3, $data, \PDO::PARAM_LOB); + $stmt->bindValue(4, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(5, $now, \PDO::PARAM_INT); + $stmt->bindParam(6, $data, \PDO::PARAM_LOB); + $stmt->bindValue(7, $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(8, $now, \PDO::PARAM_INT); + } else { + $stmt->bindParam(':id', $id); + $stmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $stmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + if (null === $driver) { + $insertStmt = $conn->prepare($insertSql); + + $insertStmt->bindParam(':id', $id); + $insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB); + $insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT); + $insertStmt->bindValue(':time', $now, \PDO::PARAM_INT); + } + + foreach ($serialized as $id => $data) { + $stmt->execute(); + + if (null === $driver && !$stmt->rowCount()) { + try { + $insertStmt->execute(); + } catch (DBALException $e) { + } catch (\PDOException $e) { + // A concurrent write won, let it be + } + } + } + + return $failed; + } + + /** + * @return \PDO|Connection + */ + private function getConnection() + { + if (null === $this->conn) { + $this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions); + $this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + } + if (null === $this->driver) { + if ($this->conn instanceof \PDO) { + $this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME); + } else { + switch ($this->driver = $this->conn->getDriver()->getName()) { + case 'mysqli': + case 'pdo_mysql': + case 'drizzle_pdo_mysql': + $this->driver = 'mysql'; + break; + case 'pdo_sqlite': + $this->driver = 'sqlite'; + break; + case 'pdo_pgsql': + $this->driver = 'pgsql'; + break; + case 'oci8': + case 'pdo_oracle': + $this->driver = 'oci'; + break; + case 'pdo_sqlsrv': + $this->driver = 'sqlsrv'; + break; + } + } + } + + return $this->conn; + } + + /** + * @return string + */ + private function getServerVersion() + { + if (null === $this->serverVersion) { + $conn = $this->conn instanceof \PDO ? $this->conn : $this->conn->getWrappedConnection(); + if ($conn instanceof \PDO) { + $this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION); + } elseif ($conn instanceof ServerInfoAwareConnection) { + $this->serverVersion = $conn->getServerVersion(); + } else { + $this->serverVersion = '0'; + } + } + + return $this->serverVersion; + } +} diff --git a/vendor/symfony/cache/Traits/PhpArrayTrait.php b/vendor/symfony/cache/Traits/PhpArrayTrait.php new file mode 100644 index 0000000..65dded8 --- /dev/null +++ b/vendor/symfony/cache/Traits/PhpArrayTrait.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\Cache\Traits; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Titouan Galopin + * @author Nicolas Grekas + * + * @internal + */ +trait PhpArrayTrait +{ + use ProxyTrait; + + private $file; + private $values; + private $zendDetectUnicode; + + /** + * Store an array of cached values. + * + * @param array $values The cached values + */ + public function warmUp(array $values) + { + if (file_exists($this->file)) { + if (!is_file($this->file)) { + throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s.', $this->file)); + } + + if (!is_writable($this->file)) { + throw new InvalidArgumentException(sprintf('Cache file is not writable: %s.', $this->file)); + } + } else { + $directory = \dirname($this->file); + + if (!is_dir($directory) && !@mkdir($directory, 0777, true)) { + throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s.', $directory)); + } + + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s.', $directory)); + } + } + + $dump = <<<'EOF' + $value) { + CacheItem::validateKey(\is_int($key) ? (string) $key : $key); + + if (null === $value || \is_object($value)) { + try { + $value = serialize($value); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \get_class($value)), 0, $e); + } + } elseif (\is_array($value)) { + try { + $serialized = serialize($value); + $unserialized = unserialize($serialized); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable array value.', $key), 0, $e); + } + // Store arrays serialized if they contain any objects or references + if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) { + $value = $serialized; + } + } elseif (\is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { + $value = serialize($value); + } + } elseif (!\is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value))); + } + + $dump .= var_export($key, true).' => '.var_export($value, true).",\n"; + } + + $dump .= "\n);\n"; + $dump = str_replace("' . \"\\0\" . '", "\0", $dump); + + $tmpFile = uniqid($this->file, true); + + file_put_contents($tmpFile, $dump); + @chmod($tmpFile, 0666 & ~umask()); + unset($serialized, $unserialized, $value, $dump); + + @rename($tmpFile, $this->file); + + $this->initialize(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->values = array(); + + $cleared = @unlink($this->file) || !file_exists($this->file); + + return $this->pool->clear() && $cleared; + } + + /** + * Load the cache file. + */ + private function initialize() + { + if ($this->zendDetectUnicode) { + $zmb = ini_set('zend.detect_unicode', 0); + } + try { + $this->values = file_exists($this->file) ? (include $this->file ?: array()) : array(); + } finally { + if ($this->zendDetectUnicode) { + ini_set('zend.detect_unicode', $zmb); + } + } + } +} diff --git a/vendor/symfony/cache/Traits/PhpFilesTrait.php b/vendor/symfony/cache/Traits/PhpFilesTrait.php new file mode 100644 index 0000000..7728d17 --- /dev/null +++ b/vendor/symfony/cache/Traits/PhpFilesTrait.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Piotr Stankowski + * @author Nicolas Grekas + * @author Rob Frawley 2nd + * + * @internal + */ +trait PhpFilesTrait +{ + use FilesystemCommonTrait; + + private $includeHandler; + private $zendDetectUnicode; + + public static function isSupported() + { + return \function_exists('opcache_invalidate') && ini_get('opcache.enable'); + } + + /** + * @return bool + */ + public function prune() + { + $time = time(); + $pruned = true; + $allowCompile = 'cli' !== \PHP_SAPI || ini_get('opcache.enable_cli'); + + set_error_handler($this->includeHandler); + try { + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + list($expiresAt) = include $file; + + if ($time >= $expiresAt) { + $pruned = @unlink($file) && !file_exists($file) && $pruned; + + if ($allowCompile) { + @opcache_invalidate($file, true); + } + } + } + } finally { + restore_error_handler(); + } + + return $pruned; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = array(); + $now = time(); + + if ($this->zendDetectUnicode) { + $zmb = ini_set('zend.detect_unicode', 0); + } + set_error_handler($this->includeHandler); + try { + foreach ($ids as $id) { + try { + $file = $this->getFile($id); + list($expiresAt, $values[$id]) = include $file; + if ($now >= $expiresAt) { + unset($values[$id]); + } + } catch (\Exception $e) { + continue; + } + } + } finally { + restore_error_handler(); + if ($this->zendDetectUnicode) { + ini_set('zend.detect_unicode', $zmb); + } + } + + foreach ($values as $id => $value) { + if ('N;' === $value) { + $values[$id] = null; + } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { + $values[$id] = parent::unserialize($value); + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return (bool) $this->doFetch(array($id)); + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $ok = true; + $data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, ''); + $allowCompile = 'cli' !== \PHP_SAPI || ini_get('opcache.enable_cli'); + + foreach ($values as $key => $value) { + if (null === $value || \is_object($value)) { + $value = serialize($value); + } elseif (\is_array($value)) { + $serialized = serialize($value); + $unserialized = parent::unserialize($serialized); + // Store arrays serialized if they contain any objects or references + if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) { + $value = $serialized; + } + } elseif (\is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { + $value = serialize($value); + } + } elseif (!\is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, \gettype($value))); + } + + $data[1] = $value; + $file = $this->getFile($key, true); + $ok = $this->write($file, 'directory)) { + throw new CacheException(sprintf('Cache directory is not writable (%s)', $this->directory)); + } + + return $ok; + } +} diff --git a/vendor/symfony/cache/Traits/ProxyTrait.php b/vendor/symfony/cache/Traits/ProxyTrait.php new file mode 100644 index 0000000..d9e085b --- /dev/null +++ b/vendor/symfony/cache/Traits/ProxyTrait.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\Cache\Traits; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Cache\ResettableInterface; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ProxyTrait +{ + private $pool; + + /** + * {@inheritdoc} + */ + public function prune() + { + return $this->pool instanceof PruneableInterface && $this->pool->prune(); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + if ($this->pool instanceof ResettableInterface) { + $this->pool->reset(); + } + } +} diff --git a/vendor/symfony/cache/Traits/RedisProxy.php b/vendor/symfony/cache/Traits/RedisProxy.php new file mode 100644 index 0000000..b328f94 --- /dev/null +++ b/vendor/symfony/cache/Traits/RedisProxy.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\Cache\Traits; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class RedisProxy +{ + private $redis; + private $initializer; + private $ready = false; + + public function __construct(\Redis $redis, \Closure $initializer) + { + $this->redis = $redis; + $this->initializer = $initializer; + } + + public function __call($method, array $args) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return \call_user_func_array(array($this->redis, $method), $args); + } + + public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function scan(&$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->scan($iIterator, $strPattern, $iCount); + } + + public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount); + } + + public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null) + { + $this->ready ?: $this->ready = $this->initializer->__invoke($this->redis); + + return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount); + } +} diff --git a/vendor/symfony/cache/Traits/RedisTrait.php b/vendor/symfony/cache/Traits/RedisTrait.php new file mode 100644 index 0000000..77e3dec --- /dev/null +++ b/vendor/symfony/cache/Traits/RedisTrait.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\Cache\Traits; + +use Predis\Connection\Aggregate\ClusterInterface; +use Predis\Connection\Aggregate\PredisCluster; +use Predis\Connection\Aggregate\RedisCluster; +use Predis\Connection\Factory; +use Predis\Response\Status; +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Aurimas Niekis + * @author Nicolas Grekas + * + * @internal + */ +trait RedisTrait +{ + private static $defaultConnectionOptions = array( + 'class' => null, + 'persistent' => 0, + 'persistent_id' => null, + 'timeout' => 30, + 'read_timeout' => 0, + 'retry_interval' => 0, + 'lazy' => false, + ); + private $redis; + + /** + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient + */ + private function init($redisClient, $namespace = '', $defaultLifetime = 0) + { + parent::__construct($namespace, $defaultLifetime); + + if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) { + throw new InvalidArgumentException(sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0])); + } + if ($redisClient instanceof \RedisCluster) { + $this->enableVersioning(); + } elseif (!$redisClient instanceof \Redis && !$redisClient instanceof \RedisArray && !$redisClient instanceof \Predis\Client && !$redisClient instanceof RedisProxy) { + throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($redisClient) ? \get_class($redisClient) : \gettype($redisClient))); + } + $this->redis = $redisClient; + } + + /** + * Creates a Redis connection using a DSN configuration. + * + * Example DSN: + * - redis://localhost + * - redis://example.com:1234 + * - redis://secret@example.com/13 + * - redis:///var/run/redis.sock + * - redis://secret@/var/run/redis.sock/13 + * + * @param string $dsn + * @param array $options See self::$defaultConnectionOptions + * + * @throws InvalidArgumentException when the DSN is invalid + * + * @return \Redis|\Predis\Client According to the "class" option + */ + public static function createConnection($dsn, array $options = array()) + { + if (0 !== strpos($dsn, 'redis://')) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s does not start with "redis://"', $dsn)); + } + $params = preg_replace_callback('#^redis://(?:(?:[^:@]*+:)?([^@]*+)@)?#', function ($m) use (&$auth) { + if (isset($m[1])) { + $auth = $m[1]; + } + + return 'file://'; + }, $dsn); + if (false === $params = parse_url($params)) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); + } + if (!isset($params['host']) && !isset($params['path'])) { + throw new InvalidArgumentException(sprintf('Invalid Redis DSN: %s', $dsn)); + } + if (isset($params['path']) && preg_match('#/(\d+)$#', $params['path'], $m)) { + $params['dbindex'] = $m[1]; + $params['path'] = substr($params['path'], 0, -\strlen($m[0])); + } + if (isset($params['host'])) { + $scheme = 'tcp'; + } else { + $scheme = 'unix'; + } + $params += array( + 'host' => isset($params['host']) ? $params['host'] : $params['path'], + 'port' => isset($params['host']) ? 6379 : null, + 'dbindex' => 0, + ); + if (isset($params['query'])) { + parse_str($params['query'], $query); + $params += $query; + } + $params += $options + self::$defaultConnectionOptions; + if (null === $params['class'] && !\extension_loaded('redis') && !class_exists(\Predis\Client::class)) { + throw new CacheException(sprintf('Cannot find the "redis" extension, and "predis/predis" is not installed: %s', $dsn)); + } + $class = null === $params['class'] ? (\extension_loaded('redis') ? \Redis::class : \Predis\Client::class) : $params['class']; + + if (is_a($class, \Redis::class, true)) { + $connect = $params['persistent'] || $params['persistent_id'] ? 'pconnect' : 'connect'; + $redis = new $class(); + + $initializer = function ($redis) use ($connect, $params, $dsn, $auth) { + try { + @$redis->{$connect}($params['host'], $params['port'], $params['timeout'], $params['persistent_id'], $params['retry_interval']); + } catch (\RedisException $e) { + throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e->getMessage(), $dsn)); + } + + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $isConnected = $redis->isConnected(); + restore_error_handler(); + if (!$isConnected) { + $error = preg_match('/^Redis::p?connect\(\): (.*)/', $error, $error) ? sprintf(' (%s)', $error[1]) : ''; + throw new InvalidArgumentException(sprintf('Redis connection failed%s: %s', $error, $dsn)); + } + + if ((null !== $auth && !$redis->auth($auth)) + || ($params['dbindex'] && !$redis->select($params['dbindex'])) + || ($params['read_timeout'] && !$redis->setOption(\Redis::OPT_READ_TIMEOUT, $params['read_timeout'])) + ) { + $e = preg_replace('/^ERR /', '', $redis->getLastError()); + throw new InvalidArgumentException(sprintf('Redis connection failed (%s): %s', $e, $dsn)); + } + + return true; + }; + + if ($params['lazy']) { + $redis = new RedisProxy($redis, $initializer); + } else { + $initializer($redis); + } + } elseif (is_a($class, \Predis\Client::class, true)) { + $params['scheme'] = $scheme; + $params['database'] = $params['dbindex'] ?: null; + $params['password'] = $auth; + $redis = new $class((new Factory())->create($params)); + } elseif (class_exists($class, false)) { + throw new InvalidArgumentException(sprintf('"%s" is not a subclass of "Redis" or "Predis\Client"', $class)); + } else { + throw new InvalidArgumentException(sprintf('Class "%s" does not exist', $class)); + } + + return $redis; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + if ($ids) { + $values = $this->pipeline(function () use ($ids) { + foreach ($ids as $id) { + yield 'get' => array($id); + } + }); + foreach ($values as $id => $v) { + if ($v) { + yield $id => parent::unserialize($v); + } + } + } + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + return (bool) $this->redis->exists($id); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + // When using a native Redis cluster, clearing the cache is done by versioning in AbstractTrait::clear(). + // This means old keys are not really removed until they expire and may need garbage collection. + + $cleared = true; + $hosts = array($this->redis); + $evalArgs = array(array($namespace), 0); + + if ($this->redis instanceof \Predis\Client) { + $evalArgs = array(0, $namespace); + + $connection = $this->redis->getConnection(); + if ($connection instanceof PredisCluster) { + $hosts = array(); + foreach ($connection as $c) { + $hosts[] = new \Predis\Client($c); + } + } elseif ($connection instanceof RedisCluster) { + return false; + } + } elseif ($this->redis instanceof \RedisArray) { + $hosts = array(); + foreach ($this->redis->_hosts() as $host) { + $hosts[] = $this->redis->_instance($host); + } + } elseif ($this->redis instanceof \RedisCluster) { + return false; + } + foreach ($hosts as $host) { + if (!isset($namespace[0])) { + $cleared = $host->flushDb() && $cleared; + continue; + } + + $info = $host->info('Server'); + $info = isset($info['Server']) ? $info['Server'] : $info; + + if (!version_compare($info['redis_version'], '2.8', '>=')) { + // As documented in Redis documentation (http://redis.io/commands/keys) using KEYS + // can hang your server when it is executed against large databases (millions of items). + // Whenever you hit this scale, you should really consider upgrading to Redis 2.8 or above. + $cleared = $host->eval("local keys=redis.call('KEYS',ARGV[1]..'*') for i=1,#keys,5000 do redis.call('DEL',unpack(keys,i,math.min(i+4999,#keys))) end return 1", $evalArgs[0], $evalArgs[1]) && $cleared; + continue; + } + + $cursor = null; + do { + $keys = $host instanceof \Predis\Client ? $host->scan($cursor, 'MATCH', $namespace.'*', 'COUNT', 1000) : $host->scan($cursor, $namespace.'*', 1000); + if (isset($keys[1]) && \is_array($keys[1])) { + $cursor = $keys[0]; + $keys = $keys[1]; + } + if ($keys) { + $host->del($keys); + } + } while ($cursor = (int) $cursor); + } + + return $cleared; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + if ($ids) { + $this->redis->del($ids); + } + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $serialized = array(); + $failed = array(); + + foreach ($values as $id => $value) { + try { + $serialized[$id] = serialize($value); + } catch (\Exception $e) { + $failed[] = $id; + } + } + + if (!$serialized) { + return $failed; + } + + $results = $this->pipeline(function () use ($serialized, $lifetime) { + foreach ($serialized as $id => $value) { + if (0 >= $lifetime) { + yield 'set' => array($id, $value); + } else { + yield 'setEx' => array($id, $lifetime, $value); + } + } + }); + foreach ($results as $id => $result) { + if (true !== $result && (!$result instanceof Status || $result !== Status::get('OK'))) { + $failed[] = $id; + } + } + + return $failed; + } + + private function pipeline(\Closure $generator) + { + $ids = array(); + + if ($this->redis instanceof \Predis\Client && !$this->redis->getConnection() instanceof ClusterInterface) { + $results = $this->redis->pipeline(function ($redis) use ($generator, &$ids) { + foreach ($generator() as $command => $args) { + \call_user_func_array(array($redis, $command), $args); + $ids[] = $args[0]; + } + }); + } elseif ($this->redis instanceof \RedisArray) { + $connections = $results = $ids = array(); + foreach ($generator() as $command => $args) { + if (!isset($connections[$h = $this->redis->_target($args[0])])) { + $connections[$h] = array($this->redis->_instance($h), -1); + $connections[$h][0]->multi(\Redis::PIPELINE); + } + \call_user_func_array(array($connections[$h][0], $command), $args); + $results[] = array($h, ++$connections[$h][1]); + $ids[] = $args[0]; + } + foreach ($connections as $h => $c) { + $connections[$h] = $c[0]->exec(); + } + foreach ($results as $k => list($h, $c)) { + $results[$k] = $connections[$h][$c]; + } + } elseif ($this->redis instanceof \RedisCluster || ($this->redis instanceof \Predis\Client && $this->redis->getConnection() instanceof ClusterInterface)) { + // phpredis & predis don't support pipelining with RedisCluster + // see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining + // see https://github.com/nrk/predis/issues/267#issuecomment-123781423 + $results = array(); + foreach ($generator() as $command => $args) { + $results[] = \call_user_func_array(array($this->redis, $command), $args); + $ids[] = $args[0]; + } + } else { + $this->redis->multi(\Redis::PIPELINE); + foreach ($generator() as $command => $args) { + \call_user_func_array(array($this->redis, $command), $args); + $ids[] = $args[0]; + } + $results = $this->redis->exec(); + } + + foreach ($ids as $k => $id) { + yield $id => $results[$k]; + } + } +} diff --git a/vendor/symfony/cache/composer.json b/vendor/symfony/cache/composer.json new file mode 100644 index 0000000..9005eeb --- /dev/null +++ b/vendor/symfony/cache/composer.json @@ -0,0 +1,49 @@ +{ + "name": "symfony/cache", + "type": "library", + "description": "Symfony Cache component with PSR-6, PSR-16, and tags", + "keywords": ["caching", "psr6"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "provide": { + "psr/cache-implementation": "1.0", + "psr/simple-cache-implementation": "1.0" + }, + "require": { + "php": "^7.1.3", + "psr/cache": "~1.0", + "psr/log": "~1.0", + "psr/simple-cache": "^1.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/cache": "~1.6", + "doctrine/dbal": "~2.4", + "predis/predis": "~1.0" + }, + "conflict": { + "symfony/var-dumper": "<3.4" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Cache\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/cache/phpunit.xml.dist b/vendor/symfony/cache/phpunit.xml.dist new file mode 100644 index 0000000..9b3c30d --- /dev/null +++ b/vendor/symfony/cache/phpunit.xml.dist @@ -0,0 +1,50 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + + + + + + + + Cache\IntegrationTests + Doctrine\Common\Cache + Symfony\Component\Cache + Symfony\Component\Cache\Tests\Fixtures + Symfony\Component\Cache\Traits + + + + + + + diff --git a/vendor/symfony/class-loader/.gitignore b/vendor/symfony/class-loader/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/class-loader/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/class-loader/ApcClassLoader.php b/vendor/symfony/class-loader/ApcClassLoader.php new file mode 100644 index 0000000..0129c48 --- /dev/null +++ b/vendor/symfony/class-loader/ApcClassLoader.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\ApcClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); + +/** + * ApcClassLoader implements a wrapping autoloader cached in APC for PHP 5.3. + * + * It expects an object implementing a findFile method to find the file. This + * allows using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * use Symfony\Component\ClassLoader\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new ApcClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * + * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. + */ +class ApcClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + protected $decorated; + + /** + * @param string $prefix The APC namespace prefix to use + * @param object $decorated A class loader object that implements the findFile() method + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!\function_exists('apcu_fetch')) { + throw new \RuntimeException('Unable to use ApcClassLoader as APC is not installed.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to APC. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + $file = apcu_fetch($this->prefix.$class, $success); + + if (!$success) { + apcu_store($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return \call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/vendor/symfony/class-loader/CHANGELOG.md b/vendor/symfony/class-loader/CHANGELOG.md new file mode 100644 index 0000000..203ec9e --- /dev/null +++ b/vendor/symfony/class-loader/CHANGELOG.md @@ -0,0 +1,41 @@ +CHANGELOG +========= + +3.3.0 +----- + + * deprecated the component: use Composer instead + +3.0.0 +----- + + * The DebugClassLoader class has been removed + * The DebugUniversalClassLoader class has been removed + * The UniversalClassLoader class has been removed + * The ApcUniversalClassLoader class has been removed + +2.4.0 +----- + + * deprecated the UniversalClassLoader in favor of the ClassLoader class instead + * deprecated the ApcUniversalClassLoader in favor of the ApcClassLoader class instead + * deprecated the DebugUniversalClassLoader in favor of the DebugClassLoader class from the Debug component + * deprecated the DebugClassLoader as it has been moved to the Debug component instead + +2.3.0 +----- + + * added a WinCacheClassLoader for WinCache + +2.1.0 +----- + + * added a DebugClassLoader able to wrap any autoloader providing a findFile + method + * added a new ApcClassLoader and XcacheClassLoader using composition to wrap + other loaders + * added a new ClassLoader which does not distinguish between namespaced and + pear-like classes (as the PEAR convention is a subset of PSR-0) and + supports using Composer's namespace maps + * added a class map generator + * added support for loading globally-installed PEAR packages diff --git a/vendor/symfony/class-loader/ClassCollectionLoader.php b/vendor/symfony/class-loader/ClassCollectionLoader.php new file mode 100644 index 0000000..3fd75c2 --- /dev/null +++ b/vendor/symfony/class-loader/ClassCollectionLoader.php @@ -0,0 +1,448 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +if (\PHP_VERSION_ID >= 70000) { + @trigger_error('The '.__NAMESPACE__.'\ClassCollectionLoader class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); +} + +/** + * ClassCollectionLoader. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassCollectionLoader +{ + private static $loaded; + private static $seen; + private static $useTokenizer = true; + + /** + * Loads a list of classes and caches them in one big file. + * + * @param array $classes An array of classes to load + * @param string $cacheDir A cache directory + * @param string $name The cache name prefix + * @param bool $autoReload Whether to flush the cache when the cache is stale or not + * @param bool $adaptive Whether to remove already declared classes or not + * @param string $extension File extension of the resulting file + * + * @throws \InvalidArgumentException When class can't be loaded + */ + public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = false, $extension = '.php') + { + // each $name can only be loaded once per PHP process + if (isset(self::$loaded[$name])) { + return; + } + + self::$loaded[$name] = true; + + if ($adaptive) { + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + + // don't include already declared classes + $classes = array_diff($classes, $declared); + + // the cache is different depending on which classes are already declared + $name .= '-'.substr(hash('sha256', implode('|', $classes)), 0, 5); + } + + $classes = array_unique($classes); + + // cache the core classes + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { + throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); + } + $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.\DIRECTORY_SEPARATOR); + $cache = $cacheDir.'/'.$name.$extension; + + // auto-reload + $reload = false; + if ($autoReload) { + $metadata = $cache.'.meta'; + if (!is_file($metadata) || !is_file($cache)) { + $reload = true; + } else { + $time = filemtime($cache); + $meta = unserialize(file_get_contents($metadata)); + + sort($meta[1]); + sort($classes); + + if ($meta[1] != $classes) { + $reload = true; + } else { + foreach ($meta[0] as $resource) { + if (!is_file($resource) || filemtime($resource) > $time) { + $reload = true; + + break; + } + } + } + } + } + + if (!$reload && file_exists($cache)) { + require_once $cache; + + return; + } + if (!$adaptive) { + $declared = array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + } + + $files = self::inline($classes, $cache, $declared); + + if ($autoReload) { + // save the resources + self::writeCacheFile($metadata, serialize(array(array_values($files), $classes))); + } + } + + /** + * Generates a file where classes and their parents are inlined. + * + * @param array $classes An array of classes to load + * @param string $cache The file where classes are inlined + * @param array $excluded An array of classes that won't be inlined + * + * @return array The source map of inlined classes, with classes as keys and files as values + * + * @throws \RuntimeException When class can't be loaded + */ + public static function inline($classes, $cache, array $excluded) + { + $declared = array(); + foreach (self::getOrderedClasses($excluded) as $class) { + $declared[$class->getName()] = true; + } + + // cache the core classes + $cacheDir = \dirname($cache); + if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { + throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); + } + + $spacesRegex = '(?:\s*+(?:(?:\#|//)[^\n]*+\n|/\*(?:(?getName()])) { + continue; + } + $declared[$class->getName()] = true; + + $files[$class->getName()] = $file = $class->getFileName(); + $c = file_get_contents($file); + + if (preg_match($dontInlineRegex, $c)) { + $file = explode('/', str_replace(\DIRECTORY_SEPARATOR, '/', $file)); + + for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) { + if ($file[$i] !== $cacheDir[$i]) { + break; + } + } + if (1 >= $i) { + $file = var_export(implode('/', $file), true); + } else { + $file = \array_slice($file, $i); + $file = str_repeat('../', \count($cacheDir) - $i).implode('/', $file); + $file = '__DIR__.'.var_export('/'.$file, true); + } + + $c = "\nnamespace {require $file;}"; + } else { + $c = preg_replace(array('/^\s*<\?php/', '/\?>\s*$/'), '', $c); + + // fakes namespace declaration for global code + if (!$class->inNamespace()) { + $c = "\nnamespace\n{\n".$c."\n}\n"; + } + + $c = self::fixNamespaceDeclarations('= 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; + } + + /** + * This method is only useful for testing. + */ + public static function enableTokenizer($bool) + { + self::$useTokenizer = (bool) $bool; + } + + /** + * Strips leading & trailing ws, multiple EOL, multiple ws. + * + * @param string $code Original PHP code + * + * @return string compressed code + */ + private static function compressCode($code) + { + return preg_replace( + array('/^\s+/m', '/\s+$/m', '/([\n\r]+ *[\n\r]+)+/', '/[ \t]+/'), + array('', '', "\n", ' '), + $code + ); + } + + /** + * Writes a cache file. + * + * @param string $file Filename + * @param string $content Temporary file content + * + * @throws \RuntimeException when a cache file cannot be written + */ + private static function writeCacheFile($file, $content) + { + $dir = \dirname($file); + if (!is_writable($dir)) { + throw new \RuntimeException(sprintf('Cache directory "%s" is not writable.', $dir)); + } + + $tmpFile = tempnam($dir, 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)); + } + + /** + * Gets an ordered array of passed classes including all their dependencies. + * + * @return \ReflectionClass[] An array of sorted \ReflectionClass instances (dependencies added if needed) + * + * @throws \InvalidArgumentException When a class can't be loaded + */ + private static function getOrderedClasses(array $classes) + { + $map = array(); + self::$seen = array(); + foreach ($classes as $class) { + try { + $reflectionClass = new \ReflectionClass($class); + } catch (\ReflectionException $e) { + throw new \InvalidArgumentException(sprintf('Unable to load class "%s"', $class)); + } + + $map = array_merge($map, self::getClassHierarchy($reflectionClass)); + } + + return $map; + } + + private static function getClassHierarchy(\ReflectionClass $class) + { + if (isset(self::$seen[$class->getName()])) { + return array(); + } + + self::$seen[$class->getName()] = true; + + $classes = array($class); + $parent = $class; + while (($parent = $parent->getParentClass()) && $parent->isUserDefined() && !isset(self::$seen[$parent->getName()])) { + self::$seen[$parent->getName()] = true; + + array_unshift($classes, $parent); + } + + $traits = array(); + + foreach ($classes as $c) { + foreach (self::resolveDependencies(self::computeTraitDeps($c), $c) as $trait) { + if ($trait !== $c) { + $traits[] = $trait; + } + } + } + + return array_merge(self::getInterfaces($class), $traits, $classes); + } + + private static function getInterfaces(\ReflectionClass $class) + { + $classes = array(); + + foreach ($class->getInterfaces() as $interface) { + $classes = array_merge($classes, self::getInterfaces($interface)); + } + + if ($class->isUserDefined() && $class->isInterface() && !isset(self::$seen[$class->getName()])) { + self::$seen[$class->getName()] = true; + + $classes[] = $class; + } + + return $classes; + } + + private static function computeTraitDeps(\ReflectionClass $class) + { + $traits = $class->getTraits(); + $deps = array($class->getName() => $traits); + while ($trait = array_pop($traits)) { + if ($trait->isUserDefined() && !isset(self::$seen[$trait->getName()])) { + self::$seen[$trait->getName()] = true; + $traitDeps = $trait->getTraits(); + $deps[$trait->getName()] = $traitDeps; + $traits = array_merge($traits, $traitDeps); + } + } + + return $deps; + } + + /** + * Dependencies resolution. + * + * This function does not check for circular dependencies as it should never + * occur with PHP traits. + * + * @param array $tree The dependency tree + * @param \ReflectionClass $node The node + * @param \ArrayObject $resolved An array of already resolved dependencies + * @param \ArrayObject $unresolved An array of dependencies to be resolved + * + * @return \ArrayObject The dependencies for the given node + * + * @throws \RuntimeException if a circular dependency is detected + */ + private static function resolveDependencies(array $tree, $node, \ArrayObject $resolved = null, \ArrayObject $unresolved = null) + { + if (null === $resolved) { + $resolved = new \ArrayObject(); + } + if (null === $unresolved) { + $unresolved = new \ArrayObject(); + } + $nodeName = $node->getName(); + + if (isset($tree[$nodeName])) { + $unresolved[$nodeName] = $node; + foreach ($tree[$nodeName] as $dependency) { + if (!$resolved->offsetExists($dependency->getName())) { + self::resolveDependencies($tree, $dependency, $resolved, $unresolved); + } + } + $resolved[$nodeName] = $node; + unset($unresolved[$nodeName]); + } + + return $resolved; + } +} diff --git a/vendor/symfony/class-loader/ClassLoader.php b/vendor/symfony/class-loader/ClassLoader.php new file mode 100644 index 0000000..52cff3b --- /dev/null +++ b/vendor/symfony/class-loader/ClassLoader.php @@ -0,0 +1,207 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * ClassLoader implements an PSR-0 class loader. + * + * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md + * + * $loader = new ClassLoader(); + * + * // register classes with namespaces + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // activate the autoloader + * $loader->register(); + * + * // to enable searching the include path (e.g. for PEAR packages) + * $loader->setUseIncludePath(true); + * + * In this example, if you try to use a class in the Symfony\Component + * namespace or one of its children (Symfony\Component\Console for instance), + * the autoloader will first look for the class under the component/ + * directory, and it will then fallback to the framework/ directory if not + * found before giving up. + * + * @author Fabien Potencier + * @author Jordi Boggiano + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassLoader +{ + private $prefixes = array(); + private $fallbackDirs = array(); + private $useIncludePath = false; + + /** + * Returns prefixes. + * + * @return array + */ + public function getPrefixes() + { + return $this->prefixes; + } + + /** + * Returns fallback directories. + * + * @return array + */ + public function getFallbackDirs() + { + return $this->fallbackDirs; + } + + /** + * Adds prefixes. + * + * @param array $prefixes Prefixes to add + */ + public function addPrefixes(array $prefixes) + { + foreach ($prefixes as $prefix => $path) { + $this->addPrefix($prefix, $path); + } + } + + /** + * Registers a set of classes. + * + * @param string $prefix The classes prefix + * @param array|string $paths The location(s) of the classes + */ + public function addPrefix($prefix, $paths) + { + if (!$prefix) { + foreach ((array) $paths as $path) { + $this->fallbackDirs[] = $path; + } + + return; + } + if (isset($this->prefixes[$prefix])) { + if (\is_array($paths)) { + $this->prefixes[$prefix] = array_unique(array_merge( + $this->prefixes[$prefix], + $paths + )); + } elseif (!\in_array($paths, $this->prefixes[$prefix])) { + $this->prefixes[$prefix][] = $paths; + } + } else { + $this->prefixes[$prefix] = array_unique((array) $paths); + } + } + + /** + * Turns on searching the include for class files. + * + * @param bool $useIncludePath + */ + public function setUseIncludePath($useIncludePath) + { + $this->useIncludePath = (bool) $useIncludePath; + } + + /** + * Can be used to check if the autoloader uses the include path to check + * for classes. + * + * @return bool + */ + public function getUseIncludePath() + { + return $this->useIncludePath; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (false !== $pos = strrpos($class, '\\')) { + // namespaced class name + $classPath = str_replace('\\', \DIRECTORY_SEPARATOR, substr($class, 0, $pos)).\DIRECTORY_SEPARATOR; + $className = substr($class, $pos + 1); + } else { + // PEAR-like class name + $classPath = null; + $className = $class; + } + + $classPath .= str_replace('_', \DIRECTORY_SEPARATOR, $className).'.php'; + + foreach ($this->prefixes as $prefix => $dirs) { + if ($class === strstr($class, $prefix)) { + foreach ($dirs as $dir) { + if (file_exists($dir.\DIRECTORY_SEPARATOR.$classPath)) { + return $dir.\DIRECTORY_SEPARATOR.$classPath; + } + } + } + } + + foreach ($this->fallbackDirs as $dir) { + if (file_exists($dir.\DIRECTORY_SEPARATOR.$classPath)) { + return $dir.\DIRECTORY_SEPARATOR.$classPath; + } + } + + if ($this->useIncludePath && $file = stream_resolve_include_path($classPath)) { + return $file; + } + } +} diff --git a/vendor/symfony/class-loader/ClassMapGenerator.php b/vendor/symfony/class-loader/ClassMapGenerator.php new file mode 100644 index 0000000..6a96886 --- /dev/null +++ b/vendor/symfony/class-loader/ClassMapGenerator.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\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\ClassMapGenerator class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * ClassMapGenerator. + * + * @author Gyula Sallai + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassMapGenerator +{ + /** + * Generate a class map file. + * + * @param array|string $dirs Directories or a single path to search in + * @param string $file The name of the class map file + */ + public static function dump($dirs, $file) + { + $dirs = (array) $dirs; + $maps = array(); + + foreach ($dirs as $dir) { + $maps = array_merge($maps, static::createMap($dir)); + } + + file_put_contents($file, sprintf('isFile()) { + continue; + } + + $path = $file->getRealPath() ?: $file->getPathname(); + + if ('php' !== pathinfo($path, PATHINFO_EXTENSION)) { + continue; + } + + $classes = self::findClasses($path); + + 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(); + } + + foreach ($classes as $class) { + $map[$class] = $path; + } + } + + return $map; + } + + /** + * Extract the classes in the given file. + * + * @param string $path The file to check + * + * @return array The found classes + */ + private static function findClasses($path) + { + $contents = file_get_contents($path); + $tokens = token_get_all($contents); + + $classes = array(); + + $namespace = ''; + for ($i = 0; isset($tokens[$i]); ++$i) { + $token = $tokens[$i]; + + if (!isset($token[1])) { + continue; + } + + $class = ''; + + switch ($token[0]) { + case T_NAMESPACE: + $namespace = ''; + // If there is a namespace, extract it + while (isset($tokens[++$i][1])) { + if (\in_array($tokens[$i][0], array(T_STRING, T_NS_SEPARATOR))) { + $namespace .= $tokens[$i][1]; + } + } + $namespace .= '\\'; + break; + case T_CLASS: + case T_INTERFACE: + case T_TRAIT: + // Skip usage of ::class constant + $isClassConstant = false; + for ($j = $i - 1; $j > 0; --$j) { + if (!isset($tokens[$j][1])) { + break; + } + + if (T_DOUBLE_COLON === $tokens[$j][0]) { + $isClassConstant = true; + break; + } elseif (!\in_array($tokens[$j][0], array(T_WHITESPACE, T_DOC_COMMENT, T_COMMENT))) { + break; + } + } + + if ($isClassConstant) { + break; + } + + // Find the classname + while (isset($tokens[++$i][1])) { + $t = $tokens[$i]; + if (T_STRING === $t[0]) { + $class .= $t[1]; + } elseif ('' !== $class && T_WHITESPACE === $t[0]) { + break; + } + } + + $classes[] = ltrim($namespace.$class, '\\'); + break; + default: + break; + } + } + + return $classes; + } +} diff --git a/vendor/symfony/class-loader/LICENSE b/vendor/symfony/class-loader/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/class-loader/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/vendor/symfony/class-loader/MapClassLoader.php b/vendor/symfony/class-loader/MapClassLoader.php new file mode 100644 index 0000000..7b0dbba --- /dev/null +++ b/vendor/symfony/class-loader/MapClassLoader.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\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\MapClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * A class loader that uses a mapping file to look up paths. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class MapClassLoader +{ + private $map = array(); + + /** + * @param array $map A map where keys are classes and values the absolute file path + */ + public function __construct(array $map) + { + $this->map = $map; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + */ + public function loadClass($class) + { + if (isset($this->map[$class])) { + require $this->map[$class]; + } + } + + /** + * Finds the path to the file where the class is defined. + * + * @param string $class The name of the class + * + * @return string|null The path, if found + */ + public function findFile($class) + { + if (isset($this->map[$class])) { + return $this->map[$class]; + } + } +} diff --git a/vendor/symfony/class-loader/Psr4ClassLoader.php b/vendor/symfony/class-loader/Psr4ClassLoader.php new file mode 100644 index 0000000..d5b8d14 --- /dev/null +++ b/vendor/symfony/class-loader/Psr4ClassLoader.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\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\Psr4ClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Composer instead.', E_USER_DEPRECATED); + +/** + * A PSR-4 compatible class loader. + * + * See http://www.php-fig.org/psr/psr-4/ + * + * @author Alexander M. Turek + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class Psr4ClassLoader +{ + private $prefixes = array(); + + /** + * @param string $prefix + * @param string $baseDir + */ + public function addPrefix($prefix, $baseDir) + { + $prefix = trim($prefix, '\\').'\\'; + $baseDir = rtrim($baseDir, \DIRECTORY_SEPARATOR).\DIRECTORY_SEPARATOR; + $this->prefixes[] = array($prefix, $baseDir); + } + + /** + * @param string $class + * + * @return string|null + */ + public function findFile($class) + { + $class = ltrim($class, '\\'); + + foreach ($this->prefixes as list($currentPrefix, $currentBaseDir)) { + if (0 === strpos($class, $currentPrefix)) { + $classWithoutPrefix = substr($class, \strlen($currentPrefix)); + $file = $currentBaseDir.str_replace('\\', \DIRECTORY_SEPARATOR, $classWithoutPrefix).'.php'; + if (file_exists($file)) { + return $file; + } + } + } + } + + /** + * @param string $class + * + * @return bool + */ + public function loadClass($class) + { + $file = $this->findFile($class); + if (null !== $file) { + require $file; + + return true; + } + + return false; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Removes this instance from the registered autoloaders. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } +} diff --git a/vendor/symfony/class-loader/README.md b/vendor/symfony/class-loader/README.md new file mode 100644 index 0000000..d61992b --- /dev/null +++ b/vendor/symfony/class-loader/README.md @@ -0,0 +1,14 @@ +ClassLoader Component +===================== + +The ClassLoader component provides tools to autoload your classes and cache +their locations for performance. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/class_loader/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/vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php b/vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php new file mode 100644 index 0000000..ec50668 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/ApcClassLoaderTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ApcClassLoader; +use Symfony\Component\ClassLoader\ClassLoader; + +/** + * @group legacy + */ +class ApcClassLoaderTest extends TestCase +{ + protected function setUp() + { + if (!(ini_get('apc.enabled') && ini_get('apc.enable_cli'))) { + $this->markTestSkipped('The apc extension is not enabled.'); + } else { + apcu_clear_cache(); + } + } + + protected function tearDown() + { + if (ini_get('apc.enabled') && ini_get('apc.enable_cli')) { + apcu_clear_cache(); + } + } + + public function testConstructor() + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + + $loader = new ApcClassLoader('test.prefix.', $loader); + + $this->assertEquals($loader->findFile('\Apc\Namespaced\FooBar'), apcu_fetch('test.prefix.\Apc\Namespaced\FooBar'), '__construct() takes a prefix as its first argument'); + } + + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Apc_Pearlike_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + + $loader = new ApcClassLoader('test.prefix.', $loader); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return array( + array('\\Apc\\Namespaced\\Foo', 'Apc\\Namespaced\\Foo', '->loadClass() loads Apc\Namespaced\Foo class'), + array('Apc_Pearlike_Foo', 'Apc_Pearlike_Foo', '->loadClass() loads Apc_Pearlike_Foo class'), + ); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Apc\Namespaced', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Apc_Pearlike_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('', array(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/fallback')); + + $loader = new ApcClassLoader('test.prefix.fallback', $loader); + $loader->loadClass($testClassName); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return array( + array('\\Apc\\Namespaced\\Baz', 'Apc\\Namespaced\\Baz', '->loadClass() loads Apc\Namespaced\Baz class'), + array('Apc_Pearlike_Baz', 'Apc_Pearlike_Baz', '->loadClass() loads Apc_Pearlike_Baz class'), + array('\\Apc\\Namespaced\\FooBar', 'Apc\\Namespaced\\FooBar', '->loadClass() loads Apc\Namespaced\Baz class from fallback dir'), + array('Apc_Pearlike_FooBar', 'Apc_Pearlike_FooBar', '->loadClass() loads Apc_Pearlike_Baz class from fallback dir'), + ); + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($namespaces); + + $loader = new ApcClassLoader('test.prefix.collision.', $loader); + $loader->loadClass($className); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return array( + array( + array( + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + ), + 'Apc\NamespaceCollision\A\Foo', + '->loadClass() loads NamespaceCollision\A\Foo from alpha.', + ), + array( + array( + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + ), + 'Apc\NamespaceCollision\A\Bar', + '->loadClass() loads NamespaceCollision\A\Bar from alpha.', + ), + array( + array( + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + ), + 'Apc\NamespaceCollision\A\B\Foo', + '->loadClass() loads NamespaceCollision\A\B\Foo from beta.', + ), + array( + array( + 'Apc\\NamespaceCollision\\A\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta', + 'Apc\\NamespaceCollision\\A' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha', + ), + 'Apc\NamespaceCollision\A\B\Bar', + '->loadClass() loads NamespaceCollision\A\B\Bar from beta.', + ), + ); + } + + /** + * @dataProvider getLoadClassPrefixCollisionTests + */ + public function testLoadClassPrefixCollision($prefixes, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($prefixes); + + $loader = new ApcClassLoader('test.prefix.collision.', $loader); + $loader->loadClass($className); + + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassPrefixCollisionTests() + { + return array( + array( + array( + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + ), + 'ApcPrefixCollision_A_Foo', + '->loadClass() loads ApcPrefixCollision_A_Foo from alpha.', + ), + array( + array( + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + ), + 'ApcPrefixCollision_A_Bar', + '->loadClass() loads ApcPrefixCollision_A_Bar from alpha.', + ), + array( + array( + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + ), + 'ApcPrefixCollision_A_B_Foo', + '->loadClass() loads ApcPrefixCollision_A_B_Foo from beta.', + ), + array( + array( + 'ApcPrefixCollision_A_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/beta/Apc', + 'ApcPrefixCollision_A_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/Apc/alpha/Apc', + ), + 'ApcPrefixCollision_A_B_Bar', + '->loadClass() loads ApcPrefixCollision_A_B_Bar from beta.', + ), + ); + } +} diff --git a/vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php b/vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php new file mode 100644 index 0000000..15a1105 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/ClassCollectionLoaderTest.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\ClassLoader\Tests\Fixtures\DeclaredClass; +use Symfony\Component\ClassLoader\Tests\Fixtures\WarmedClass; + +require_once __DIR__.'/Fixtures/ClassesWithParents/GInterface.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/CInterface.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/B.php'; +require_once __DIR__.'/Fixtures/ClassesWithParents/A.php'; + +/** + * @group legacy + */ +class ClassCollectionLoaderTest extends TestCase +{ + public function testTraitDependencies() + { + require_once __DIR__.'/Fixtures/deps/traits.php'; + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, array('CTFoo')); + + $this->assertEquals( + array('TD', 'TC', 'TB', 'TA', 'TZ', 'CTFoo'), + array_map(function ($class) { return $class->getName(); }, $ordered) + ); + + $ordered = $m->invoke(null, array('CTBar')); + + $this->assertEquals( + array('TD', 'TZ', 'TC', 'TB', 'TA', 'CTBar'), + array_map(function ($class) { return $class->getName(); }, $ordered) + ); + } + + /** + * @dataProvider getDifferentOrders + */ + public function testClassReordering(array $classes) + { + $expected = array( + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrders() + { + return array( + array(array( + 'ClassesWithParents\\A', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\B', + )), + array(array( + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\CInterface', + )), + array(array( + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + )), + array(array( + 'ClassesWithParents\\A', + )), + ); + } + + /** + * @dataProvider getDifferentOrdersForTraits + */ + public function testClassWithTraitsReordering(array $classes) + { + require_once __DIR__.'/Fixtures/ClassesWithParents/ATrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/BTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/D.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/E.php'; + + $expected = array( + 'ClassesWithParents\\GInterface', + 'ClassesWithParents\\CInterface', + 'ClassesWithParents\\ATrait', + 'ClassesWithParents\\BTrait', + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\B', + 'ClassesWithParents\\A', + 'ClassesWithParents\\D', + 'ClassesWithParents\\E', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + public function getDifferentOrdersForTraits() + { + return array( + array(array( + 'ClassesWithParents\\E', + 'ClassesWithParents\\ATrait', + )), + array(array( + 'ClassesWithParents\\E', + )), + ); + } + + public function testFixClassWithTraitsOrdering() + { + require_once __DIR__.'/Fixtures/ClassesWithParents/CTrait.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/F.php'; + require_once __DIR__.'/Fixtures/ClassesWithParents/G.php'; + + $classes = array( + 'ClassesWithParents\\F', + 'ClassesWithParents\\G', + ); + + $expected = array( + 'ClassesWithParents\\CTrait', + 'ClassesWithParents\\F', + 'ClassesWithParents\\G', + ); + + $r = new \ReflectionClass('Symfony\Component\ClassLoader\ClassCollectionLoader'); + $m = $r->getMethod('getOrderedClasses'); + $m->setAccessible(true); + + $ordered = $m->invoke(null, $classes); + + $this->assertEquals($expected, array_map(function ($class) { return $class->getName(); }, $ordered)); + } + + /** + * @dataProvider getFixNamespaceDeclarationsData + */ + public function testFixNamespaceDeclarations($source, $expected) + { + $this->assertEquals('assertEquals('assertEquals(<<<'EOF' +namespace Namespaced +{ +class WithComments +{ +public static $loaded = true; +} +$string ='string should not be modified {$string}'; +$heredoc = (<<assertTrue(class_exists(WarmedClass::class, true)); + + @unlink($cache = sys_get_temp_dir().'/inline.php'); + + $classes = array(WarmedClass::class); + $excluded = array(DeclaredClass::class); + + ClassCollectionLoader::inline($classes, $cache, $excluded); + + $this->assertSame(<<<'EOTXT' + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ClassLoader; + +/** + * @group legacy + */ +class ClassLoaderTest extends TestCase +{ + public function testGetPrefixes() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Bar', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Bas', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertArrayNotHasKey('Foo1', $prefixes); + $this->assertArrayHasKey('Bar', $prefixes); + $this->assertArrayHasKey('Bas', $prefixes); + } + + public function testGetFallbackDirs() + { + $loader = new ClassLoader(); + $loader->addPrefix(null, __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix(null, __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $fallback_dirs = $loader->getFallbackDirs(); + $this->assertCount(2, $fallback_dirs); + } + + /** + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassTests() + { + return array( + array('\\Namespaced2\\Foo', 'Namespaced2\\Foo', '->loadClass() loads Namespaced2\Foo class'), + array('\\Pearlike2_Foo', 'Pearlike2_Foo', '->loadClass() loads Pearlike2_Foo class'), + ); + } + + /** + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->loadClass($testClassName); + $this->assertFalse(class_exists($className), $message); + } + + public function getLoadNonexistentClassTests() + { + return array( + array('\\Pearlike3_Bar', '\\Pearlike3_Bar', '->loadClass() loads non existing Pearlike3_Bar class with a leading slash'), + ); + } + + public function testAddPrefixSingle() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Foo', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(1, $prefixes['Foo']); + } + + public function testAddPrefixesSingle() + { + $loader = new ClassLoader(); + $loader->addPrefixes(array('Foo' => array('foo', 'foo'))); + $loader->addPrefixes(array('Foo' => array('foo'))); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(1, $prefixes['Foo'], print_r($prefixes, true)); + } + + public function testAddPrefixMulti() + { + $loader = new ClassLoader(); + $loader->addPrefix('Foo', 'foo'); + $loader->addPrefix('Foo', 'bar'); + $prefixes = $loader->getPrefixes(); + $this->assertArrayHasKey('Foo', $prefixes); + $this->assertCount(2, $prefixes['Foo']); + $this->assertContains('foo', $prefixes['Foo']); + $this->assertContains('bar', $prefixes['Foo']); + } + + public function testUseIncludePath() + { + $loader = new ClassLoader(); + $this->assertFalse($loader->getUseIncludePath()); + + $this->assertNull($loader->findFile('Foo')); + + $includePath = get_include_path(); + + $loader->setUseIncludePath(true); + $this->assertTrue($loader->getUseIncludePath()); + + set_include_path(__DIR__.'/Fixtures/includepath'.PATH_SEPARATOR.$includePath); + + $this->assertEquals(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'includepath'.\DIRECTORY_SEPARATOR.'Foo.php', $loader->findFile('Foo')); + + set_include_path($includePath); + } + + /** + * @dataProvider getLoadClassFromFallbackTests + */ + public function testLoadClassFromFallback($className, $testClassName, $message) + { + $loader = new ClassLoader(); + $loader->addPrefix('Namespaced2\\', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('Pearlike2_', __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $loader->addPrefix('', array(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures/fallback')); + $loader->loadClass($testClassName); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassFromFallbackTests() + { + return array( + array('\\Namespaced2\\Baz', 'Namespaced2\\Baz', '->loadClass() loads Namespaced2\Baz class'), + array('\\Pearlike2_Baz', 'Pearlike2_Baz', '->loadClass() loads Pearlike2_Baz class'), + array('\\Namespaced2\\FooBar', 'Namespaced2\\FooBar', '->loadClass() loads Namespaced2\Baz class from fallback dir'), + array('\\Pearlike2_FooBar', 'Pearlike2_FooBar', '->loadClass() loads Pearlike2_Baz class from fallback dir'), + ); + } + + /** + * @dataProvider getLoadClassNamespaceCollisionTests + */ + public function testLoadClassNamespaceCollision($namespaces, $className, $message) + { + $loader = new ClassLoader(); + $loader->addPrefixes($namespaces); + + $loader->loadClass($className); + $this->assertTrue(class_exists($className), $message); + } + + public function getLoadClassNamespaceCollisionTests() + { + return array( + array( + array( + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'NamespaceCollision\C\Foo', + '->loadClass() loads NamespaceCollision\C\Foo from alpha.', + ), + array( + array( + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'NamespaceCollision\C\Bar', + '->loadClass() loads NamespaceCollision\C\Bar from alpha.', + ), + array( + array( + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'NamespaceCollision\C\B\Foo', + '->loadClass() loads NamespaceCollision\C\B\Foo from beta.', + ), + array( + array( + 'NamespaceCollision\\C\\B' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'NamespaceCollision\\C' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'NamespaceCollision\C\B\Bar', + '->loadClass() loads NamespaceCollision\C\B\Bar from beta.', + ), + array( + array( + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'PrefixCollision_C_Foo', + '->loadClass() loads PrefixCollision_C_Foo from alpha.', + ), + array( + array( + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'PrefixCollision_C_Bar', + '->loadClass() loads PrefixCollision_C_Bar from alpha.', + ), + array( + array( + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + ), + 'PrefixCollision_C_B_Foo', + '->loadClass() loads PrefixCollision_C_B_Foo from beta.', + ), + array( + array( + 'PrefixCollision_C_B_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/beta', + 'PrefixCollision_C_' => __DIR__.\DIRECTORY_SEPARATOR.'Fixtures/alpha', + ), + 'PrefixCollision_C_B_Bar', + '->loadClass() loads PrefixCollision_C_B_Bar from beta.', + ), + ); + } +} diff --git a/vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php b/vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.php new file mode 100644 index 0000000..ad6a1b8 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/ClassMapGeneratorTest.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\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\ClassMapGenerator; + +/** + * @group legacy + */ +class ClassMapGeneratorTest extends TestCase +{ + /** + * @var string|null + */ + private $workspace = null; + + public function prepare_workspace() + { + $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + /** + * @param string $file + */ + private function clean($file) + { + if (is_dir($file) && !is_link($file)) { + $dir = new \FilesystemIterator($file); + foreach ($dir as $childFile) { + $this->clean($childFile); + } + + rmdir($file); + } else { + unlink($file); + } + } + + /** + * @dataProvider getTestCreateMapTests + */ + public function testDump($directory) + { + $this->prepare_workspace(); + + $file = $this->workspace.'/file'; + + $generator = new ClassMapGenerator(); + $generator->dump($directory, $file); + $this->assertFileExists($file); + + $this->clean($this->workspace); + } + + /** + * @dataProvider getTestCreateMapTests + */ + public function testCreateMap($directory, $expected) + { + $this->assertEqualsNormalized($expected, ClassMapGenerator::createMap($directory)); + } + + public function getTestCreateMapTests() + { + $data = array( + array(__DIR__.'/Fixtures/Namespaced', array( + 'Namespaced\\Bar' => realpath(__DIR__).'/Fixtures/Namespaced/Bar.php', + 'Namespaced\\Foo' => realpath(__DIR__).'/Fixtures/Namespaced/Foo.php', + 'Namespaced\\Baz' => realpath(__DIR__).'/Fixtures/Namespaced/Baz.php', + 'Namespaced\\WithComments' => realpath(__DIR__).'/Fixtures/Namespaced/WithComments.php', + 'Namespaced\\WithStrictTypes' => realpath(__DIR__).'/Fixtures/Namespaced/WithStrictTypes.php', + 'Namespaced\\WithHaltCompiler' => realpath(__DIR__).'/Fixtures/Namespaced/WithHaltCompiler.php', + 'Namespaced\\WithDirMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithDirMagic.php', + 'Namespaced\\WithFileMagic' => realpath(__DIR__).'/Fixtures/Namespaced/WithFileMagic.php', + )), + array(__DIR__.'/Fixtures/beta/NamespaceCollision', array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', + 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', + )), + array(__DIR__.'/Fixtures/Pearlike', array( + 'Pearlike_Foo' => realpath(__DIR__).'/Fixtures/Pearlike/Foo.php', + 'Pearlike_Bar' => realpath(__DIR__).'/Fixtures/Pearlike/Bar.php', + 'Pearlike_Baz' => realpath(__DIR__).'/Fixtures/Pearlike/Baz.php', + 'Pearlike_WithComments' => realpath(__DIR__).'/Fixtures/Pearlike/WithComments.php', + )), + array(__DIR__.'/Fixtures/classmap', array( + 'Foo\\Bar\\A' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'Foo\\Bar\\B' => realpath(__DIR__).'/Fixtures/classmap/sameNsMultipleClasses.php', + 'A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Alpha\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\A' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'Beta\\B' => realpath(__DIR__).'/Fixtures/classmap/multipleNs.php', + 'ClassMap\\SomeInterface' => realpath(__DIR__).'/Fixtures/classmap/SomeInterface.php', + 'ClassMap\\SomeParent' => realpath(__DIR__).'/Fixtures/classmap/SomeParent.php', + 'ClassMap\\SomeClass' => realpath(__DIR__).'/Fixtures/classmap/SomeClass.php', + )), + array(__DIR__.'/Fixtures/php5.4', array( + 'TFoo' => __DIR__.'/Fixtures/php5.4/traits.php', + 'CFoo' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\TBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\IBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\TFooBar' => __DIR__.'/Fixtures/php5.4/traits.php', + 'Foo\\CBar' => __DIR__.'/Fixtures/php5.4/traits.php', + )), + array(__DIR__.'/Fixtures/php5.5', array( + 'ClassCons\\Foo' => __DIR__.'/Fixtures/php5.5/class_cons.php', + )), + ); + + return $data; + } + + public function testCreateMapFinderSupport() + { + $finder = new \Symfony\Component\Finder\Finder(); + $finder->files()->in(__DIR__.'/Fixtures/beta/NamespaceCollision'); + + $this->assertEqualsNormalized(array( + 'NamespaceCollision\\A\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Bar.php', + 'NamespaceCollision\\A\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/A/B/Foo.php', + 'NamespaceCollision\\C\\B\\Bar' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Bar.php', + 'NamespaceCollision\\C\\B\\Foo' => realpath(__DIR__).'/Fixtures/beta/NamespaceCollision/C/B/Foo.php', + ), ClassMapGenerator::createMap($finder)); + } + + protected function assertEqualsNormalized($expected, $actual, $message = '') + { + foreach ($expected as $ns => $path) { + $expected[$ns] = str_replace('\\', '/', $path); + } + foreach ($actual as $ns => $path) { + $actual[$ns] = str_replace('\\', '/', $path); + } + $this->assertEquals($expected, $actual, $message); + } +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.php new file mode 100644 index 0000000..4259f14 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Bar.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 Apc\Namespaced; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.php new file mode 100644 index 0000000..3ddb595 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Baz.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 Apc\Namespaced; + +class Baz +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.php new file mode 100644 index 0000000..cf0a4b7 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/Foo.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 Apc\Namespaced; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.php new file mode 100644 index 0000000..bbbc815 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Namespaced/FooBar.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 Apc\Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php new file mode 100644 index 0000000..e774cb9 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/Pearlike/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.php new file mode 100644 index 0000000..184a1b1 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/alpha/Apc/NamespaceCollision/A/Foo.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 Apc\NamespaceCollision\A; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php new file mode 100644 index 0000000..3892f70 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/ApcPrefixCollision/A/B/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\NamespaceCollision\A\B; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.php new file mode 100644 index 0000000..450eeb5 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/beta/Apc/NamespaceCollision/A/B/Foo.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 Apc\NamespaceCollision\A\B; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php b/vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php new file mode 100644 index 0000000..96f2f76 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Apc/fallback/Apc/Pearlike/FooBar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Apc\Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php b/vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.php new file mode 100644 index 0000000..b0f9425 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/ClassesWithParents/A.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 Namespaced; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php b/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.php new file mode 100644 index 0000000..0b0bbd0 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Baz.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 Namespaced; + +class Baz +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php b/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.php new file mode 100644 index 0000000..df5e1f4 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/Foo.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 Namespaced; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php b/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.php new file mode 100644 index 0000000..361e53d --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Namespaced/WithComments.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 Namespaced; + +class WithComments +{ + /** @Boolean */ + public static $loaded = true; +} + +$string = 'string should not be modified {$string}'; + +$heredoc = (<< + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +class Pearlike_WithComments +{ + /** @Boolean */ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php b/vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php new file mode 100644 index 0000000..7f5f797 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/Pearlike2/Bar.php @@ -0,0 +1,6 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php b/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.php new file mode 100644 index 0000000..aee6a08 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/A/Foo.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 NamespaceCollision\A; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php b/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php new file mode 100644 index 0000000..c1b8dd6 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/alpha/NamespaceCollision/C/Bar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace NamespaceCollision\A\B; + +class Bar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php b/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.php new file mode 100644 index 0000000..f5f2d72 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/A/B/Foo.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 NamespaceCollision\A\B; + +class Foo +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php b/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php new file mode 100644 index 0000000..4bb03dc --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/beta/NamespaceCollision/C/B/Bar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ClassMap; + +class SomeClass extends SomeParent implements SomeInterface +{ +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php b/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.php new file mode 100644 index 0000000..1fe5e09 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeInterface.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 ClassMap; + +interface SomeInterface +{ +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php b/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.php new file mode 100644 index 0000000..ce2f9fc --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/classmap/SomeParent.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 ClassMap; + +abstract class SomeParent +{ +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php b/vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.php new file mode 100644 index 0000000..c7cec64 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/classmap/multipleNs.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 Foo\Bar; + +class A +{ +} +class B +{ +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/deps/traits.php b/vendor/symfony/class-loader/Tests/Fixtures/deps/traits.php new file mode 100644 index 0000000..82b30a6 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/deps/traits.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 Namespaced; + +class FooBar +{ + public static $loaded = true; +} diff --git a/vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php b/vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php new file mode 100644 index 0000000..1036d43 --- /dev/null +++ b/vendor/symfony/class-loader/Tests/Fixtures/fallback/Namespaced2/FooBar.php @@ -0,0 +1,8 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ClassLoader\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ClassLoader\Psr4ClassLoader; + +/** + * @group legacy + */ +class Psr4ClassLoaderTest extends TestCase +{ + /** + * @param string $className + * @dataProvider getLoadClassTests + */ + public function testLoadClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'psr-4' + ); + $loader->loadClass($className); + $this->assertTrue(class_exists($className), sprintf('loadClass() should load %s', $className)); + } + + /** + * @return array + */ + public function getLoadClassTests() + { + return array( + array('Acme\\DemoLib\\Foo'), + array('Acme\\DemoLib\\Class_With_Underscores'), + array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Foo'), + array('Acme\\DemoLib\\Lets\\Go\\Deeper\\Class_With_Underscores'), + ); + } + + /** + * @param string $className + * @dataProvider getLoadNonexistentClassTests + */ + public function testLoadNonexistentClass($className) + { + $loader = new Psr4ClassLoader(); + $loader->addPrefix( + 'Acme\\DemoLib', + __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'psr-4' + ); + $loader->loadClass($className); + $this->assertFalse(class_exists($className), sprintf('loadClass() should not load %s', $className)); + } + + /** + * @return array + */ + public function getLoadNonexistentClassTests() + { + return array( + array('Acme\\DemoLib\\I_Do_Not_Exist'), + array('UnknownVendor\\SomeLib\\I_Do_Not_Exist'), + ); + } +} diff --git a/vendor/symfony/class-loader/WinCacheClassLoader.php b/vendor/symfony/class-loader/WinCacheClassLoader.php new file mode 100644 index 0000000..75ebb84 --- /dev/null +++ b/vendor/symfony/class-loader/WinCacheClassLoader.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\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\WinCacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); + +/** + * WinCacheClassLoader implements a wrapping autoloader cached in WinCache. + * + * It expects an object implementing a findFile method to find the file. This + * allow using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new WinCacheClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * @author Artem Ryzhkov + * + * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. + */ +class WinCacheClassLoader +{ + private $prefix; + + /** + * A class loader object that implements the findFile() method. + * + * @var object + */ + protected $decorated; + + /** + * @param string $prefix The WinCache namespace prefix to use + * @param object $decorated A class loader object that implements the findFile() method + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!\extension_loaded('wincache')) { + throw new \RuntimeException('Unable to use WinCacheClassLoader as WinCache is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to WinCache. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + $file = wincache_ucache_get($this->prefix.$class, $success); + + if (!$success) { + wincache_ucache_set($this->prefix.$class, $file = $this->decorated->findFile($class) ?: null, 0); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return \call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/vendor/symfony/class-loader/XcacheClassLoader.php b/vendor/symfony/class-loader/XcacheClassLoader.php new file mode 100644 index 0000000..a6a9ecc --- /dev/null +++ b/vendor/symfony/class-loader/XcacheClassLoader.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\ClassLoader; + +@trigger_error('The '.__NAMESPACE__.'\XcacheClassLoader class is deprecated since Symfony 3.3 and will be removed in 4.0. Use `composer install --apcu-autoloader` instead.', E_USER_DEPRECATED); + +/** + * XcacheClassLoader implements a wrapping autoloader cached in XCache for PHP 5.3. + * + * It expects an object implementing a findFile method to find the file. This + * allows using it as a wrapper around the other loaders of the component (the + * ClassLoader for instance) but also around any other autoloaders following + * this convention (the Composer one for instance). + * + * // with a Symfony autoloader + * $loader = new ClassLoader(); + * $loader->addPrefix('Symfony\Component', __DIR__.'/component'); + * $loader->addPrefix('Symfony', __DIR__.'/framework'); + * + * // or with a Composer autoloader + * use Composer\Autoload\ClassLoader; + * + * $loader = new ClassLoader(); + * $loader->add('Symfony\Component', __DIR__.'/component'); + * $loader->add('Symfony', __DIR__.'/framework'); + * + * $cachedLoader = new XcacheClassLoader('my_prefix', $loader); + * + * // activate the cached autoloader + * $cachedLoader->register(); + * + * // eventually deactivate the non-cached loader if it was registered previously + * // to be sure to use the cached one. + * $loader->unregister(); + * + * @author Fabien Potencier + * @author Kris Wallsmith + * @author Kim Hemsø Rasmussen + * + * @deprecated since version 3.3, to be removed in 4.0. Use `composer install --apcu-autoloader` instead. + */ +class XcacheClassLoader +{ + private $prefix; + private $decorated; + + /** + * @param string $prefix The XCache namespace prefix to use + * @param object $decorated A class loader object that implements the findFile() method + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $decorated) + { + if (!\extension_loaded('xcache')) { + throw new \RuntimeException('Unable to use XcacheClassLoader as XCache is not enabled.'); + } + + if (!method_exists($decorated, 'findFile')) { + throw new \InvalidArgumentException('The class finder must implement a "findFile" method.'); + } + + $this->prefix = $prefix; + $this->decorated = $decorated; + } + + /** + * Registers this instance as an autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not + */ + public function register($prepend = false) + { + spl_autoload_register(array($this, 'loadClass'), true, $prepend); + } + + /** + * Unregisters this instance as an autoloader. + */ + public function unregister() + { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $class The name of the class + * + * @return bool|null True, if loaded + */ + public function loadClass($class) + { + if ($file = $this->findFile($class)) { + require $file; + + return true; + } + } + + /** + * Finds a file by class name while caching lookups to Xcache. + * + * @param string $class A class name to resolve to file + * + * @return string|null + */ + public function findFile($class) + { + if (xcache_isset($this->prefix.$class)) { + $file = xcache_get($this->prefix.$class); + } else { + $file = $this->decorated->findFile($class) ?: null; + xcache_set($this->prefix.$class, $file); + } + + return $file; + } + + /** + * Passes through all unknown calls onto the decorated object. + */ + public function __call($method, $args) + { + return \call_user_func_array(array($this->decorated, $method), $args); + } +} diff --git a/vendor/symfony/class-loader/composer.json b/vendor/symfony/class-loader/composer.json new file mode 100644 index 0000000..97f150f --- /dev/null +++ b/vendor/symfony/class-loader/composer.json @@ -0,0 +1,40 @@ +{ + "name": "symfony/class-loader", + "type": "library", + "description": "Symfony ClassLoader Component", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "minimum-stability": "dev", + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "require-dev": { + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/polyfill-apcu": "~1.1" + }, + "suggest": { + "symfony/polyfill-apcu": "For using ApcClassLoader on HHVM" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\ClassLoader\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/vendor/symfony/class-loader/phpunit.xml.dist b/vendor/symfony/class-loader/phpunit.xml.dist new file mode 100644 index 0000000..5158b22 --- /dev/null +++ b/vendor/symfony/class-loader/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/config/.gitignore b/vendor/symfony/config/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/config/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/config/CHANGELOG.md b/vendor/symfony/config/CHANGELOG.md new file mode 100644 index 0000000..7813ac5 --- /dev/null +++ b/vendor/symfony/config/CHANGELOG.md @@ -0,0 +1,78 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added `setDeprecated()` method to indicate a deprecated node + * added `XmlUtils::parse()` method to parse an XML string + * deprecated `ConfigCachePass` + +3.3.0 +----- + + * added `ReflectionClassResource` class + * added second `$exists` constructor argument to `ClassExistenceResource` + * made `ClassExistenceResource` work with interfaces and traits + * added `ConfigCachePass` (originally in FrameworkBundle) + * added `castToArray()` helper to turn any config value into an array + +3.0.0 +----- + + * removed `ReferenceDumper` class + * removed the `ResourceInterface::isFresh()` method + * removed `BCResourceInterfaceChecker` class + * removed `ResourceInterface::getResource()` method + +2.8.0 +----- + +The edge case of defining just one value for nodes of type Enum is now allowed: + +```php +$rootNode + ->children() + ->enumNode('variable') + ->values(array('value')) + ->end() + ->end() +; +``` + +Before: `InvalidArgumentException` (variable must contain at least two +distinct elements). +After: the code will work as expected and it will restrict the values of the +`variable` option to just `value`. + + * deprecated the `ResourceInterface::isFresh()` method. If you implement custom resource types and they + can be validated that way, make them implement the new `SelfCheckingResourceInterface`. + * deprecated the getResource() method in ResourceInterface. You can still call this method + on concrete classes implementing the interface, but it does not make sense at the interface + level as you need to know about the particular type of resource at hand to understand the + semantics of the returned value. + +2.7.0 +----- + + * added `ConfigCacheInterface`, `ConfigCacheFactoryInterface` and a basic `ConfigCacheFactory` + implementation to delegate creation of ConfigCache instances + +2.2.0 +----- + + * added `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` + to ease configuration when some sections are respectively disabled / enabled + by default. + * added a `normalizeKeys()` method for array nodes (to avoid key normalization) + * added numerical type handling for config definitions + * added convenience methods for optional configuration sections to `ArrayNodeDefinition` + * added a utils class for XML manipulations + +2.1.0 +----- + + * added a way to add documentation on configuration + * implemented `Serializable` on resources + * `LoaderResolverInterface` is now used instead of `LoaderResolver` for type + hinting diff --git a/vendor/symfony/config/ConfigCache.php b/vendor/symfony/config/ConfigCache.php new file mode 100644 index 0000000..591c89b --- /dev/null +++ b/vendor/symfony/config/ConfigCache.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config; + +use Symfony\Component\Config\Resource\SelfCheckingResourceChecker; + +/** + * ConfigCache caches arbitrary content in files on disk. + * + * When in debug mode, those metadata resources that implement + * \Symfony\Component\Config\Resource\SelfCheckingResourceInterface will + * be used to check cache freshness. + * + * @author Fabien Potencier + * @author Matthias Pigulla + */ +class ConfigCache extends ResourceCheckerConfigCache +{ + private $debug; + + /** + * @param string $file The absolute cache path + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($file, $debug) + { + $this->debug = (bool) $debug; + + $checkers = array(); + if (true === $this->debug) { + $checkers = array(new SelfCheckingResourceChecker()); + } + + parent::__construct($file, $checkers); + } + + /** + * Checks if the cache is still fresh. + * + * This implementation always returns true when debug is off and the + * cache file exists. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!$this->debug && is_file($this->getPath())) { + return true; + } + + return parent::isFresh(); + } +} diff --git a/vendor/symfony/config/ConfigCacheFactory.php b/vendor/symfony/config/ConfigCacheFactory.php new file mode 100644 index 0000000..7903cca --- /dev/null +++ b/vendor/symfony/config/ConfigCacheFactory.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\Config; + +/** + * Basic implementation of ConfigCacheFactoryInterface that + * creates an instance of the default ConfigCache. + * + * This factory and/or cache do not support cache validation + * by means of ResourceChecker instances (that is, service-based). + * + * @author Matthias Pigulla + */ +class ConfigCacheFactory implements ConfigCacheFactoryInterface +{ + private $debug; + + /** + * @param bool $debug The debug flag to pass to ConfigCache + */ + public function __construct($debug) + { + $this->debug = $debug; + } + + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!\is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); + } + + $cache = new ConfigCache($file, $this->debug); + if (!$cache->isFresh()) { + \call_user_func($callback, $cache); + } + + return $cache; + } +} diff --git a/vendor/symfony/config/ConfigCacheFactoryInterface.php b/vendor/symfony/config/ConfigCacheFactoryInterface.php new file mode 100644 index 0000000..bd614c4 --- /dev/null +++ b/vendor/symfony/config/ConfigCacheFactoryInterface.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\Config; + +/** + * Interface for a ConfigCache factory. This factory creates + * an instance of ConfigCacheInterface and initializes the + * cache if necessary. + * + * @author Matthias Pigulla + */ +interface ConfigCacheFactoryInterface +{ + /** + * Creates a cache instance and (re-)initializes it if necessary. + * + * @param string $file The absolute cache file path + * @param callable $callable The callable to be executed when the cache needs to be filled (i. e. is not fresh). The cache will be passed as the only parameter to this callback + * + * @return ConfigCacheInterface $configCache The cache instance + */ + public function cache($file, $callable); +} diff --git a/vendor/symfony/config/ConfigCacheInterface.php b/vendor/symfony/config/ConfigCacheInterface.php new file mode 100644 index 0000000..7c47ad7 --- /dev/null +++ b/vendor/symfony/config/ConfigCacheInterface.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\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ConfigCache. + * + * @author Matthias Pigulla + */ +interface ConfigCacheInterface +{ + /** + * Gets the cache file path. + * + * @return string The cache file path + */ + public function getPath(); + + /** + * Checks if the cache is still fresh. + * + * This check should take the metadata passed to the write() method into consideration. + * + * @return bool Whether the cache is still fresh + */ + public function isFresh(); + + /** + * Writes the given content into the cache file. Metadata will be stored + * independently and can be used to check cache freshness at a later time. + * + * @param string $content The content to write into the cache + * @param ResourceInterface[]|null $metadata An array of ResourceInterface instances + * + * @throws \RuntimeException When the cache file cannot be written + */ + public function write($content, array $metadata = null); +} diff --git a/vendor/symfony/config/Definition/ArrayNode.php b/vendor/symfony/config/Definition/ArrayNode.php new file mode 100644 index 0000000..17d10ff --- /dev/null +++ b/vendor/symfony/config/Definition/ArrayNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents an Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class ArrayNode extends BaseNode implements PrototypeNodeInterface +{ + protected $xmlRemappings = array(); + protected $children = array(); + protected $allowFalse = false; + protected $allowNewKeys = true; + protected $addIfNotSet = false; + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $normalizeKeys = true; + + public function setNormalizeKeys($normalizeKeys) + { + $this->normalizeKeys = (bool) $normalizeKeys; + } + + /** + * Normalizes keys between the different configuration formats. + * + * Namely, you mostly have foo_bar in YAML while you have foo-bar in XML. + * After running this method, all keys are normalized to foo_bar. + * + * If you have a mixed key like foo-bar_moo, it will not be altered. + * The key will also not be altered if the target key already exists. + * + * @param mixed $value + * + * @return array The value with normalized keys + */ + protected function preNormalize($value) + { + if (!$this->normalizeKeys || !\is_array($value)) { + return $value; + } + + $normalized = array(); + + foreach ($value as $k => $v) { + if (false !== strpos($k, '-') && false === strpos($k, '_') && !array_key_exists($normalizedKey = str_replace('-', '_', $k), $value)) { + $normalized[$normalizedKey] = $v; + } else { + $normalized[$k] = $v; + } + } + + return $normalized; + } + + /** + * Retrieves the children of this node. + * + * @return array The children + */ + public function getChildren() + { + return $this->children; + } + + /** + * Sets the xml remappings that should be performed. + * + * @param array $remappings An array of the form array(array(string, string)) + */ + public function setXmlRemappings(array $remappings) + { + $this->xmlRemappings = $remappings; + } + + /** + * Gets the xml remappings that should be performed. + * + * @return array $remappings an array of the form array(array(string, string)) + */ + public function getXmlRemappings() + { + return $this->xmlRemappings; + } + + /** + * Sets whether to add default values for this array if it has not been + * defined in any of the configuration files. + * + * @param bool $boolean + */ + public function setAddIfNotSet($boolean) + { + $this->addIfNotSet = (bool) $boolean; + } + + /** + * Sets whether false is allowed as value indicating that the array should be unset. + * + * @param bool $allow + */ + public function setAllowFalse($allow) + { + $this->allowFalse = (bool) $allow; + } + + /** + * Sets whether new keys can be defined in subsequent configurations. + * + * @param bool $allow + */ + public function setAllowNewKeys($allow) + { + $this->allowNewKeys = (bool) $allow; + } + + /** + * Sets if deep merging should occur. + * + * @param bool $boolean + */ + public function setPerformDeepMerging($boolean) + { + $this->performDeepMerging = (bool) $boolean; + } + + /** + * Whether extra keys should just be ignore without an exception. + * + * @param bool $boolean To allow extra keys + * @param bool $remove To remove extra keys + */ + public function setIgnoreExtraKeys($boolean, $remove = true) + { + $this->ignoreExtraKeys = (bool) $boolean; + $this->removeExtraKeys = $this->ignoreExtraKeys && $remove; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->addIfNotSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + if (!$this->hasDefaultValue()) { + throw new \RuntimeException(sprintf('The node at path "%s" has no default value.', $this->getPath())); + } + + $defaults = array(); + foreach ($this->children as $name => $child) { + if ($child->hasDefaultValue()) { + $defaults[$name] = $child->getDefaultValue(); + } + } + + return $defaults; + } + + /** + * Adds a child node. + * + * @throws \InvalidArgumentException when the child node has no name + * @throws \InvalidArgumentException when the child node's name is not unique + */ + public function addChild(NodeInterface $node) + { + $name = $node->getName(); + if (!\strlen($name)) { + throw new \InvalidArgumentException('Child nodes must be named.'); + } + if (isset($this->children[$name])) { + throw new \InvalidArgumentException(sprintf('A child node named "%s" already exists.', $name)); + } + + $this->children[$name] = $node; + } + + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalised value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (false === $value) { + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value))); + } + + foreach ($this->children as $name => $child) { + if (!array_key_exists($name, $value)) { + if ($child->isRequired()) { + $ex = new InvalidConfigurationException(sprintf('The child node "%s" at path "%s" must be configured.', $name, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + if ($child->hasDefaultValue()) { + $value[$name] = $child->getDefaultValue(); + } + + continue; + } + + if ($child->isDeprecated()) { + @trigger_error($child->getDeprecationMessage($name, $this->getPath()), E_USER_DEPRECATED); + } + + try { + $value[$name] = $child->finalize($value[$name]); + } catch (UnsetKeyException $e) { + unset($value[$name]); + } + } + + return $value; + } + + /** + * Validates the type of the value. + * + * @param mixed $value + * + * @throws InvalidTypeException + */ + protected function validateType($value) + { + if (!\is_array($value) && (!$this->allowFalse || false !== $value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected array, but got %s', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + */ + protected function normalizeValue($value) + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $normalized = array(); + foreach ($value as $name => $val) { + if (isset($this->children[$name])) { + $normalized[$name] = $this->children[$name]->normalize($val); + unset($value[$name]); + } elseif (!$this->removeExtraKeys) { + $normalized[$name] = $val; + } + } + + // if extra fields are present, throw exception + if (\count($value) && !$this->ignoreExtraKeys) { + $ex = new InvalidConfigurationException(sprintf('Unrecognized option%s "%s" under "%s"', 1 === \count($value) ? '' : 's', implode(', ', array_keys($value)), $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $normalized; + } + + /** + * Remaps multiple singular values to a single plural value. + * + * @param array $value The source values + * + * @return array The remapped values + */ + protected function remapXml($value) + { + foreach ($this->xmlRemappings as list($singular, $plural)) { + if (!isset($value[$singular])) { + continue; + } + + $value[$plural] = Processor::normalizeConfig($value, $singular, $plural); + unset($value[$singular]); + } + + return $value; + } + + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge + * @param mixed $rightSide The right side to merge + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // no conflict + if (!array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file. If you are trying to overwrite an element, make sure you redefine it with the same name.', $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + if (!isset($this->children[$k])) { + throw new \RuntimeException('merge() expects a normalized config array.'); + } + + $leftSide[$k] = $this->children[$k]->merge($leftSide[$k], $v); + } + + return $leftSide; + } +} diff --git a/vendor/symfony/config/Definition/BaseNode.php b/vendor/symfony/config/Definition/BaseNode.php new file mode 100644 index 0000000..9057f14 --- /dev/null +++ b/vendor/symfony/config/Definition/BaseNode.php @@ -0,0 +1,362 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * The base node class. + * + * @author Johannes M. Schmitt + */ +abstract class BaseNode implements NodeInterface +{ + protected $name; + protected $parent; + protected $normalizationClosures = array(); + protected $finalValidationClosures = array(); + protected $allowOverwrite = true; + protected $required = false; + protected $deprecationMessage = null; + protected $equivalentValues = array(); + protected $attributes = array(); + + /** + * @param string|null $name The name of the node + * @param NodeInterface|null $parent The parent of this node + * + * @throws \InvalidArgumentException if the name contains a period + */ + public function __construct($name, NodeInterface $parent = null) + { + if (false !== strpos($name = (string) $name, '.')) { + throw new \InvalidArgumentException('The name must not contain ".".'); + } + + $this->name = $name; + $this->parent = $parent; + } + + public function setAttribute($key, $value) + { + $this->attributes[$key] = $value; + } + + public function getAttribute($key, $default = null) + { + return isset($this->attributes[$key]) ? $this->attributes[$key] : $default; + } + + public function hasAttribute($key) + { + return isset($this->attributes[$key]); + } + + public function getAttributes() + { + return $this->attributes; + } + + public function setAttributes(array $attributes) + { + $this->attributes = $attributes; + } + + public function removeAttribute($key) + { + unset($this->attributes[$key]); + } + + /** + * Sets an info message. + * + * @param string $info + */ + public function setInfo($info) + { + $this->setAttribute('info', $info); + } + + /** + * Returns info message. + * + * @return string The info text + */ + public function getInfo() + { + return $this->getAttribute('info'); + } + + /** + * Sets the example configuration for this node. + * + * @param string|array $example + */ + public function setExample($example) + { + $this->setAttribute('example', $example); + } + + /** + * Retrieves the example configuration for this node. + * + * @return string|array The example + */ + public function getExample() + { + return $this->getAttribute('example'); + } + + /** + * Adds an equivalent value. + * + * @param mixed $originalValue + * @param mixed $equivalentValue + */ + public function addEquivalentValue($originalValue, $equivalentValue) + { + $this->equivalentValues[] = array($originalValue, $equivalentValue); + } + + /** + * Set this node as required. + * + * @param bool $boolean Required node + */ + public function setRequired($boolean) + { + $this->required = (bool) $boolean; + } + + /** + * Sets this node as deprecated. + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path. + * + * @param string|null $message Deprecated message + */ + public function setDeprecated($message) + { + $this->deprecationMessage = $message; + } + + /** + * Sets if this node can be overridden. + * + * @param bool $allow + */ + public function setAllowOverwrite($allow) + { + $this->allowOverwrite = (bool) $allow; + } + + /** + * Sets the closures used for normalization. + * + * @param \Closure[] $closures An array of Closures used for normalization + */ + public function setNormalizationClosures(array $closures) + { + $this->normalizationClosures = $closures; + } + + /** + * Sets the closures used for final validation. + * + * @param \Closure[] $closures An array of Closures used for final validation + */ + public function setFinalValidationClosures(array $closures) + { + $this->finalValidationClosures = $closures; + } + + /** + * {@inheritdoc} + */ + public function isRequired() + { + return $this->required; + } + + /** + * Checks if this node is deprecated. + * + * @return bool + */ + public function isDeprecated() + { + return null !== $this->deprecationMessage; + } + + /** + * Returns the deprecated message. + * + * @param string $node the configuration node name + * @param string $path the path of the node + * + * @return string + */ + public function getDeprecationMessage($node, $path) + { + return strtr($this->deprecationMessage, array('%node%' => $node, '%path%' => $path)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + $path = $this->name; + + if (null !== $this->parent) { + $path = $this->parent->getPath().'.'.$path; + } + + return $path; + } + + /** + * {@inheritdoc} + */ + final public function merge($leftSide, $rightSide) + { + if (!$this->allowOverwrite) { + throw new ForbiddenOverwriteException(sprintf('Configuration path "%s" cannot be overwritten. You have to define all options for this path, and any of its sub-paths in one configuration section.', $this->getPath())); + } + + $this->validateType($leftSide); + $this->validateType($rightSide); + + return $this->mergeValues($leftSide, $rightSide); + } + + /** + * {@inheritdoc} + */ + final public function normalize($value) + { + $value = $this->preNormalize($value); + + // run custom normalization closures + foreach ($this->normalizationClosures as $closure) { + $value = $closure($value); + } + + // replace value with their equivalent + foreach ($this->equivalentValues as $data) { + if ($data[0] === $value) { + $value = $data[1]; + } + } + + // validate type + $this->validateType($value); + + // normalize value + return $this->normalizeValue($value); + } + + /** + * Normalizes the value before any other normalization is applied. + * + * @param $value + * + * @return $value The normalized array value + */ + protected function preNormalize($value) + { + return $value; + } + + /** + * Returns parent node for this node. + * + * @return NodeInterface|null + */ + public function getParent() + { + return $this->parent; + } + + /** + * {@inheritdoc} + */ + final public function finalize($value) + { + $this->validateType($value); + + $value = $this->finalizeValue($value); + + // Perform validation on the final value if a closure has been set. + // The closure is also allowed to return another value. + foreach ($this->finalValidationClosures as $closure) { + try { + $value = $closure($value); + } catch (Exception $e) { + throw $e; + } catch (\Exception $e) { + throw new InvalidConfigurationException(sprintf('Invalid configuration for path "%s": %s', $this->getPath(), $e->getMessage()), $e->getCode(), $e); + } + } + + return $value; + } + + /** + * Validates the type of a Node. + * + * @param mixed $value The value to validate + * + * @throws InvalidTypeException when the value is invalid + */ + abstract protected function validateType($value); + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + */ + abstract protected function normalizeValue($value); + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + */ + abstract protected function mergeValues($leftSide, $rightSide); + + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + */ + abstract protected function finalizeValue($value); +} diff --git a/vendor/symfony/config/Definition/BooleanNode.php b/vendor/symfony/config/Definition/BooleanNode.php new file mode 100644 index 0000000..85f467b --- /dev/null +++ b/vendor/symfony/config/Definition/BooleanNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a Boolean value in the config tree. + * + * @author Johannes M. Schmitt + */ +class BooleanNode extends ScalarNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_bool($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected boolean, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a boolean value cannot be empty + return false; + } +} diff --git a/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php b/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php new file mode 100644 index 0000000..28a00be --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ArrayNodeDefinition.php @@ -0,0 +1,522 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * This class provides a fluent interface for defining an array node. + * + * @author Johannes M. Schmitt + */ +class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinitionInterface +{ + protected $performDeepMerging = true; + protected $ignoreExtraKeys = false; + protected $removeExtraKeys = true; + protected $children = array(); + protected $prototype; + protected $atLeastOne = false; + protected $allowNewKeys = true; + protected $key; + protected $removeKeyItem; + protected $addDefaults = false; + protected $addDefaultChildren = false; + protected $nodeBuilder; + protected $normalizeKeys = true; + + /** + * {@inheritdoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = array(); + $this->trueEquivalent = array(); + } + + /** + * {@inheritdoc} + */ + public function setBuilder(NodeBuilder $builder) + { + $this->nodeBuilder = $builder; + } + + /** + * {@inheritdoc} + */ + public function children() + { + return $this->getNodeBuilder(); + } + + /** + * Sets a prototype for child nodes. + * + * @param string $type The type of node + * + * @return NodeDefinition + */ + public function prototype($type) + { + return $this->prototype = $this->getNodeBuilder()->node(null, $type)->setParent($this); + } + + /** + * @return VariableNodeDefinition + */ + public function variablePrototype() + { + return $this->prototype('variable'); + } + + /** + * @return ScalarNodeDefinition + */ + public function scalarPrototype() + { + return $this->prototype('scalar'); + } + + /** + * @return BooleanNodeDefinition + */ + public function booleanPrototype() + { + return $this->prototype('boolean'); + } + + /** + * @return IntegerNodeDefinition + */ + public function integerPrototype() + { + return $this->prototype('integer'); + } + + /** + * @return FloatNodeDefinition + */ + public function floatPrototype() + { + return $this->prototype('float'); + } + + /** + * @return ArrayNodeDefinition + */ + public function arrayPrototype() + { + return $this->prototype('array'); + } + + /** + * @return EnumNodeDefinition + */ + public function enumPrototype() + { + return $this->prototype('enum'); + } + + /** + * Adds the default value if the node is not set in the configuration. + * + * This method is applicable to concrete nodes only (not to prototype nodes). + * If this function has been called and the node is not set during the finalization + * phase, it's default value will be derived from its children default values. + * + * @return $this + */ + public function addDefaultsIfNotSet() + { + $this->addDefaults = true; + + return $this; + } + + /** + * Adds children with a default value when none are defined. + * + * This method is applicable to prototype nodes only. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + * + * @return $this + */ + public function addDefaultChildrenIfNoneSet($children = null) + { + $this->addDefaultChildren = $children; + + return $this; + } + + /** + * Requires the node to have at least one element. + * + * This method is applicable to prototype nodes only. + * + * @return $this + */ + public function requiresAtLeastOneElement() + { + $this->atLeastOne = true; + + return $this; + } + + /** + * Disallows adding news keys in a subsequent configuration. + * + * If used all keys have to be defined in the same configuration file. + * + * @return $this + */ + public function disallowNewKeysInSubsequentConfigs() + { + $this->allowNewKeys = false; + + return $this; + } + + /** + * Sets a normalization rule for XML configurations. + * + * @param string $singular The key to remap + * @param string $plural The plural of the key for irregular plurals + * + * @return $this + */ + public function fixXmlConfig($singular, $plural = null) + { + $this->normalization()->remap($singular, $plural); + + return $this; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * array( + * array('id' => 'my_name', 'foo' => 'bar'), + * ); + * + * becomes + * + * array( + * 'my_name' => array('foo' => 'bar'), + * ); + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * This method is applicable to prototype nodes only. + * + * @param string $name The name of the key + * @param bool $removeKeyItem Whether or not the key item should be removed + * + * @return $this + */ + public function useAttributeAsKey($name, $removeKeyItem = true) + { + $this->key = $name; + $this->removeKeyItem = $removeKeyItem; + + return $this; + } + + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return $this + */ + public function canBeUnset($allow = true) + { + $this->merge()->allowUnset($allow); + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is disabled. If any configuration is specified then + * the node will be automatically enabled: + * + * enableableArrayNode: {enabled: true, ...} # The config is enabled & default values get overridden + * enableableArrayNode: ~ # The config is enabled & use the default values + * enableableArrayNode: true # The config is enabled & use the default values + * enableableArrayNode: {other: value, ...} # The config is enabled & default values get overridden + * enableableArrayNode: {enabled: false, ...} # The config is disabled + * enableableArrayNode: false # The config is disabled + * + * @return $this + */ + public function canBeEnabled() + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->beforeNormalization() + ->ifArray() + ->then(function ($v) { + $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true; + + return $v; + }) + ->end() + ->children() + ->booleanNode('enabled') + ->defaultFalse() + ; + + return $this; + } + + /** + * Adds an "enabled" boolean to enable the current section. + * + * By default, the section is enabled. + * + * @return $this + */ + public function canBeDisabled() + { + $this + ->addDefaultsIfNotSet() + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->children() + ->booleanNode('enabled') + ->defaultTrue() + ; + + return $this; + } + + /** + * Disables the deep merging of the node. + * + * @return $this + */ + public function performNoDeepMerging() + { + $this->performDeepMerging = false; + + return $this; + } + + /** + * Allows extra config keys to be specified under an array without + * throwing an exception. + * + * Those config values are simply ignored and removed from the + * resulting array. This should be used only in special cases where + * you want to send an entire configuration array through a special + * tree that processes only part of the array. + * + * @param bool $remove Whether to remove the extra keys + * + * @return $this + */ + public function ignoreExtraKeys($remove = true) + { + $this->ignoreExtraKeys = true; + $this->removeExtraKeys = $remove; + + return $this; + } + + /** + * Sets key normalization. + * + * @param bool $bool Whether to enable key normalization + * + * @return $this + */ + public function normalizeKeys($bool) + { + $this->normalizeKeys = (bool) $bool; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function append(NodeDefinition $node) + { + $this->children[$node->name] = $node->setParent($this); + + return $this; + } + + /** + * Returns a node builder to be used to add children and prototype. + * + * @return NodeBuilder The node builder + */ + protected function getNodeBuilder() + { + if (null === $this->nodeBuilder) { + $this->nodeBuilder = new NodeBuilder(); + } + + return $this->nodeBuilder->setParent($this); + } + + /** + * {@inheritdoc} + */ + protected function createNode() + { + if (null === $this->prototype) { + $node = new ArrayNode($this->name, $this->parent); + + $this->validateConcreteNode($node); + + $node->setAddIfNotSet($this->addDefaults); + + foreach ($this->children as $child) { + $child->parent = $node; + $node->addChild($child->getNode()); + } + } else { + $node = new PrototypedArrayNode($this->name, $this->parent); + + $this->validatePrototypeNode($node); + + if (null !== $this->key) { + $node->setKeyAttribute($this->key, $this->removeKeyItem); + } + + if (false === $this->allowEmptyValue) { + @trigger_error(sprintf('Using %s::cannotBeEmpty() at path "%s" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same.', __CLASS__, $node->getPath()), E_USER_DEPRECATED); + } + + if (true === $this->atLeastOne) { + $node->setMinNumberOfElements(1); + } + + if ($this->default) { + $node->setDefaultValue($this->defaultValue); + } + + if (false !== $this->addDefaultChildren) { + $node->setAddChildrenIfNoneSet($this->addDefaultChildren); + if ($this->prototype instanceof static && null === $this->prototype->prototype) { + $this->prototype->addDefaultsIfNotSet(); + } + } + + $this->prototype->parent = $node; + $node->setPrototype($this->prototype->getNode()); + } + + $node->setAllowNewKeys($this->allowNewKeys); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setPerformDeepMerging($this->performDeepMerging); + $node->setRequired($this->required); + $node->setDeprecated($this->deprecationMessage); + $node->setIgnoreExtraKeys($this->ignoreExtraKeys, $this->removeExtraKeys); + $node->setNormalizeKeys($this->normalizeKeys); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + $node->setXmlRemappings($this->normalization->remappings); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + $node->setAllowFalse($this->merge->allowFalse); + } + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } + + /** + * Validate the configuration of a concrete node. + * + * @throws InvalidDefinitionException + */ + protected function validateConcreteNode(ArrayNode $node) + { + $path = $node->getPath(); + + if (null !== $this->key) { + throw new InvalidDefinitionException(sprintf('->useAttributeAsKey() is not applicable to concrete nodes at path "%s"', $path)); + } + + if (false === $this->allowEmptyValue) { + @trigger_error(sprintf('->cannotBeEmpty() is not applicable to concrete nodes at path "%s". In 4.0 it will throw an exception.', $path), E_USER_DEPRECATED); + } + + if (true === $this->atLeastOne) { + throw new InvalidDefinitionException(sprintf('->requiresAtLeastOneElement() is not applicable to concrete nodes at path "%s"', $path)); + } + + if ($this->default) { + throw new InvalidDefinitionException(sprintf('->defaultValue() is not applicable to concrete nodes at path "%s"', $path)); + } + + if (false !== $this->addDefaultChildren) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() is not applicable to concrete nodes at path "%s"', $path)); + } + } + + /** + * Validate the configuration of a prototype node. + * + * @throws InvalidDefinitionException + */ + protected function validatePrototypeNode(PrototypedArrayNode $node) + { + $path = $node->getPath(); + + if ($this->addDefaults) { + throw new InvalidDefinitionException(sprintf('->addDefaultsIfNotSet() is not applicable to prototype nodes at path "%s"', $path)); + } + + if (false !== $this->addDefaultChildren) { + if ($this->default) { + throw new InvalidDefinitionException(sprintf('A default value and default children might not be used together at path "%s"', $path)); + } + + if (null !== $this->key && (null === $this->addDefaultChildren || \is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path)); + } + + if (null === $this->key && (\is_string($this->addDefaultChildren) || \is_array($this->addDefaultChildren))) { + throw new InvalidDefinitionException(sprintf('->addDefaultChildrenIfNoneSet() might not set default children names as ->useAttributeAsKey() is not used at path "%s"', $path)); + } + } + } +} diff --git a/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php b/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.php new file mode 100644 index 0000000..28e5657 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/BooleanNodeDefinition.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\BooleanNode; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class BooleanNodeDefinition extends ScalarNodeDefinition +{ + /** + * {@inheritdoc} + */ + public function __construct($name, NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = true; + } + + /** + * Instantiate a Node. + * + * @return BooleanNode The node + */ + protected function instantiateNode() + { + return new BooleanNode($this->name, $this->parent); + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to BooleanNodeDefinition.'); + } +} diff --git a/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php b/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.php new file mode 100644 index 0000000..817906f --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/EnumNodeDefinition.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\EnumNode; + +/** + * Enum Node Definition. + * + * @author Johannes M. Schmitt + */ +class EnumNodeDefinition extends ScalarNodeDefinition +{ + private $values; + + /** + * @return $this + */ + public function values(array $values) + { + $values = array_unique($values); + + if (empty($values)) { + throw new \InvalidArgumentException('->values() must be called with at least one value.'); + } + + $this->values = $values; + + return $this; + } + + /** + * Instantiate a Node. + * + * @return EnumNode The node + * + * @throws \RuntimeException + */ + protected function instantiateNode() + { + if (null === $this->values) { + throw new \RuntimeException('You must call ->values() on enum nodes.'); + } + + return new EnumNode($this->name, $this->parent, $this->values); + } +} diff --git a/vendor/symfony/config/Definition/Builder/ExprBuilder.php b/vendor/symfony/config/Definition/Builder/ExprBuilder.php new file mode 100644 index 0000000..5c070be --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ExprBuilder.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * This class builds an if expression. + * + * @author Johannes M. Schmitt + * @author Christophe Coevoet + */ +class ExprBuilder +{ + protected $node; + public $ifPart; + public $thenPart; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Marks the expression as being always used. + * + * @return $this + */ + public function always(\Closure $then = null) + { + $this->ifPart = function ($v) { return true; }; + + if (null !== $then) { + $this->thenPart = $then; + } + + return $this; + } + + /** + * Sets a closure to use as tests. + * + * The default one tests if the value is true. + * + * @return $this + */ + public function ifTrue(\Closure $closure = null) + { + if (null === $closure) { + $closure = function ($v) { return true === $v; }; + } + + $this->ifPart = $closure; + + return $this; + } + + /** + * Tests if the value is a string. + * + * @return $this + */ + public function ifString() + { + $this->ifPart = function ($v) { return \is_string($v); }; + + return $this; + } + + /** + * Tests if the value is null. + * + * @return $this + */ + public function ifNull() + { + $this->ifPart = function ($v) { return null === $v; }; + + return $this; + } + + /** + * Tests if the value is empty. + * + * @return ExprBuilder + */ + public function ifEmpty() + { + $this->ifPart = function ($v) { return empty($v); }; + + return $this; + } + + /** + * Tests if the value is an array. + * + * @return $this + */ + public function ifArray() + { + $this->ifPart = function ($v) { return \is_array($v); }; + + return $this; + } + + /** + * Tests if the value is in an array. + * + * @return $this + */ + public function ifInArray(array $array) + { + $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); }; + + return $this; + } + + /** + * Tests if the value is not in an array. + * + * @return $this + */ + public function ifNotInArray(array $array) + { + $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); }; + + return $this; + } + + /** + * Transforms variables of any type into an array. + * + * @return $this + */ + public function castToArray() + { + $this->ifPart = function ($v) { return !\is_array($v); }; + $this->thenPart = function ($v) { return array($v); }; + + return $this; + } + + /** + * Sets the closure to run if the test pass. + * + * @return $this + */ + public function then(\Closure $closure) + { + $this->thenPart = $closure; + + return $this; + } + + /** + * Sets a closure returning an empty array. + * + * @return $this + */ + public function thenEmptyArray() + { + $this->thenPart = function ($v) { return array(); }; + + return $this; + } + + /** + * Sets a closure marking the value as invalid at validation time. + * + * if you want to add the value of the node in your message just use a %s placeholder. + * + * @param string $message + * + * @return $this + * + * @throws \InvalidArgumentException + */ + public function thenInvalid($message) + { + $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + + return $this; + } + + /** + * Sets a closure unsetting this key of the array at validation time. + * + * @return $this + * + * @throws UnsetKeyException + */ + public function thenUnset() + { + $this->thenPart = function ($v) { throw new UnsetKeyException('Unsetting key'); }; + + return $this; + } + + /** + * Returns the related node. + * + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + * + * @throws \RuntimeException + */ + public function end() + { + if (null === $this->ifPart) { + throw new \RuntimeException('You must specify an if part.'); + } + if (null === $this->thenPart) { + throw new \RuntimeException('You must specify a then part.'); + } + + return $this->node; + } + + /** + * Builds the expressions. + * + * @param ExprBuilder[] $expressions An array of ExprBuilder instances to build + * + * @return array + */ + public static function buildExpressions(array $expressions) + { + foreach ($expressions as $k => $expr) { + if ($expr instanceof self) { + $if = $expr->ifPart; + $then = $expr->thenPart; + $expressions[$k] = function ($v) use ($if, $then) { + return $if($v) ? $then($v) : $v; + }; + } + } + + return $expressions; + } +} diff --git a/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php b/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.php new file mode 100644 index 0000000..c0bed46 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/FloatNodeDefinition.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\FloatNode; + +/** + * This class provides a fluent interface for defining a float node. + * + * @author Jeanmonod David + */ +class FloatNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return FloatNode The node + */ + protected function instantiateNode() + { + return new FloatNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php b/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.php new file mode 100644 index 0000000..f6c3c14 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/IntegerNodeDefinition.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\IntegerNode; + +/** + * This class provides a fluent interface for defining an integer node. + * + * @author Jeanmonod David + */ +class IntegerNodeDefinition extends NumericNodeDefinition +{ + /** + * Instantiates a Node. + * + * @return IntegerNode The node + */ + protected function instantiateNode() + { + return new IntegerNode($this->name, $this->parent, $this->min, $this->max); + } +} diff --git a/vendor/symfony/config/Definition/Builder/MergeBuilder.php b/vendor/symfony/config/Definition/Builder/MergeBuilder.php new file mode 100644 index 0000000..105e2d6 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/MergeBuilder.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\Config\Definition\Builder; + +/** + * This class builds merge conditions. + * + * @author Johannes M. Schmitt + */ +class MergeBuilder +{ + protected $node; + public $allowFalse = false; + public $allowOverwrite = true; + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Sets whether the node can be unset. + * + * @param bool $allow + * + * @return $this + */ + public function allowUnset($allow = true) + { + $this->allowFalse = $allow; + + return $this; + } + + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return $this + */ + public function denyOverwrite($deny = true) + { + $this->allowOverwrite = !$deny; + + return $this; + } + + /** + * Returns the related node. + * + * @return NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition + */ + public function end() + { + return $this->node; + } +} diff --git a/vendor/symfony/config/Definition/Builder/NodeBuilder.php b/vendor/symfony/config/Definition/Builder/NodeBuilder.php new file mode 100644 index 0000000..1fac66f --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NodeBuilder.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\Config\Definition\Builder; + +/** + * This class provides a fluent interface for building a node. + * + * @author Johannes M. Schmitt + */ +class NodeBuilder implements NodeParentInterface +{ + protected $parent; + protected $nodeMapping; + + public function __construct() + { + $this->nodeMapping = array( + 'variable' => __NAMESPACE__.'\\VariableNodeDefinition', + 'scalar' => __NAMESPACE__.'\\ScalarNodeDefinition', + 'boolean' => __NAMESPACE__.'\\BooleanNodeDefinition', + 'integer' => __NAMESPACE__.'\\IntegerNodeDefinition', + 'float' => __NAMESPACE__.'\\FloatNodeDefinition', + 'array' => __NAMESPACE__.'\\ArrayNodeDefinition', + 'enum' => __NAMESPACE__.'\\EnumNodeDefinition', + ); + } + + /** + * Set the parent node. + * + * @return $this + */ + public function setParent(ParentNodeDefinitionInterface $parent = null) + { + $this->parent = $parent; + + return $this; + } + + /** + * Creates a child array node. + * + * @param string $name The name of the node + * + * @return ArrayNodeDefinition The child node + */ + public function arrayNode($name) + { + return $this->node($name, 'array'); + } + + /** + * Creates a child scalar node. + * + * @param string $name The name of the node + * + * @return ScalarNodeDefinition The child node + */ + public function scalarNode($name) + { + return $this->node($name, 'scalar'); + } + + /** + * Creates a child Boolean node. + * + * @param string $name The name of the node + * + * @return BooleanNodeDefinition The child node + */ + public function booleanNode($name) + { + return $this->node($name, 'boolean'); + } + + /** + * Creates a child integer node. + * + * @param string $name The name of the node + * + * @return IntegerNodeDefinition The child node + */ + public function integerNode($name) + { + return $this->node($name, 'integer'); + } + + /** + * Creates a child float node. + * + * @param string $name The name of the node + * + * @return FloatNodeDefinition The child node + */ + public function floatNode($name) + { + return $this->node($name, 'float'); + } + + /** + * Creates a child EnumNode. + * + * @param string $name + * + * @return EnumNodeDefinition + */ + public function enumNode($name) + { + return $this->node($name, 'enum'); + } + + /** + * Creates a child variable node. + * + * @param string $name The name of the node + * + * @return VariableNodeDefinition The builder of the child node + */ + public function variableNode($name) + { + return $this->node($name, 'variable'); + } + + /** + * Returns the parent node. + * + * @return ParentNodeDefinitionInterface|NodeDefinition The parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates a child node. + * + * @param string|null $name The name of the node + * @param string $type The type of the node + * + * @return NodeDefinition The child node + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + public function node($name, $type) + { + $class = $this->getNodeClass($type); + + $node = new $class($name); + + $this->append($node); + + return $node; + } + + /** + * Appends a node definition. + * + * Usage: + * + * $node = new ArrayNodeDefinition('name') + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(NodeDefinition $node) + { + if ($node instanceof ParentNodeDefinitionInterface) { + $builder = clone $this; + $builder->setParent(null); + $node->setBuilder($builder); + } + + if (null !== $this->parent) { + $this->parent->append($node); + // Make this builder the node parent to allow for a fluid interface + $node->setParent($this); + } + + return $this; + } + + /** + * Adds or overrides a node Type. + * + * @param string $type The name of the type + * @param string $class The fully qualified name the node definition class + * + * @return $this + */ + public function setNodeClass($type, $class) + { + $this->nodeMapping[strtolower($type)] = $class; + + return $this; + } + + /** + * Returns the class name of the node definition. + * + * @param string $type The node type + * + * @return string The node definition class name + * + * @throws \RuntimeException When the node type is not registered + * @throws \RuntimeException When the node class is not found + */ + protected function getNodeClass($type) + { + $type = strtolower($type); + + if (!isset($this->nodeMapping[$type])) { + throw new \RuntimeException(sprintf('The node type "%s" is not registered.', $type)); + } + + $class = $this->nodeMapping[$type]; + + if (!class_exists($class)) { + throw new \RuntimeException(sprintf('The node class "%s" does not exist.', $class)); + } + + return $class; + } +} diff --git a/vendor/symfony/config/Definition/Builder/NodeDefinition.php b/vendor/symfony/config/Definition/Builder/NodeDefinition.php new file mode 100644 index 0000000..7486e8c --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NodeDefinition.php @@ -0,0 +1,353 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +abstract class NodeDefinition implements NodeParentInterface +{ + protected $name; + protected $normalization; + protected $validation; + protected $defaultValue; + protected $default = false; + protected $required = false; + protected $deprecationMessage = null; + protected $merge; + protected $allowEmptyValue = true; + protected $nullEquivalent; + protected $trueEquivalent = true; + protected $falseEquivalent = false; + protected $parent; + protected $attributes = array(); + + /** + * @param string|null $name The name of the node + * @param NodeParentInterface|null $parent The parent + */ + public function __construct($name, NodeParentInterface $parent = null) + { + $this->parent = $parent; + $this->name = $name; + } + + /** + * Sets the parent node. + * + * @return $this + */ + public function setParent(NodeParentInterface $parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Sets info message. + * + * @param string $info The info text + * + * @return $this + */ + public function info($info) + { + return $this->attribute('info', $info); + } + + /** + * Sets example configuration. + * + * @param string|array $example + * + * @return $this + */ + public function example($example) + { + return $this->attribute('example', $example); + } + + /** + * Sets an attribute on the node. + * + * @param string $key + * @param mixed $value + * + * @return $this + */ + public function attribute($key, $value) + { + $this->attributes[$key] = $value; + + return $this; + } + + /** + * Returns the parent node. + * + * @return NodeParentInterface|NodeBuilder|NodeDefinition|ArrayNodeDefinition|VariableNodeDefinition|null The builder of the parent node + */ + public function end() + { + return $this->parent; + } + + /** + * Creates the node. + * + * @param bool $forceRootNode Whether to force this node as the root node + * + * @return NodeInterface + */ + public function getNode($forceRootNode = false) + { + if ($forceRootNode) { + $this->parent = null; + } + + if (null !== $this->normalization) { + $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); + } + + if (null !== $this->validation) { + $this->validation->rules = ExprBuilder::buildExpressions($this->validation->rules); + } + + $node = $this->createNode(); + $node->setAttributes($this->attributes); + + return $node; + } + + /** + * Sets the default value. + * + * @param mixed $value The default value + * + * @return $this + */ + public function defaultValue($value) + { + $this->default = true; + $this->defaultValue = $value; + + return $this; + } + + /** + * Sets the node as required. + * + * @return $this + */ + public function isRequired() + { + $this->required = true; + + return $this; + } + + /** + * Sets the node as deprecated. + * + * You can use %node% and %path% placeholders in your message to display, + * respectively, the node name and its complete path. + * + * @param string $message Deprecation message + * + * @return $this + */ + public function setDeprecated($message = 'The child node "%node%" at path "%path%" is deprecated.') + { + $this->deprecationMessage = $message; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains null. + * + * @param mixed $value + * + * @return $this + */ + public function treatNullLike($value) + { + $this->nullEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains true. + * + * @param mixed $value + * + * @return $this + */ + public function treatTrueLike($value) + { + $this->trueEquivalent = $value; + + return $this; + } + + /** + * Sets the equivalent value used when the node contains false. + * + * @param mixed $value + * + * @return $this + */ + public function treatFalseLike($value) + { + $this->falseEquivalent = $value; + + return $this; + } + + /** + * Sets null as the default value. + * + * @return $this + */ + public function defaultNull() + { + return $this->defaultValue(null); + } + + /** + * Sets true as the default value. + * + * @return $this + */ + public function defaultTrue() + { + return $this->defaultValue(true); + } + + /** + * Sets false as the default value. + * + * @return $this + */ + public function defaultFalse() + { + return $this->defaultValue(false); + } + + /** + * Sets an expression to run before the normalization. + * + * @return ExprBuilder + */ + public function beforeNormalization() + { + return $this->normalization()->before(); + } + + /** + * Denies the node value being empty. + * + * @return $this + */ + public function cannotBeEmpty() + { + $this->allowEmptyValue = false; + + return $this; + } + + /** + * Sets an expression to run for the validation. + * + * The expression receives the value of the node and must return it. It can + * modify it. + * An exception should be thrown when the node is not valid. + * + * @return ExprBuilder + */ + public function validate() + { + return $this->validation()->rule(); + } + + /** + * Sets whether the node can be overwritten. + * + * @param bool $deny Whether the overwriting is forbidden or not + * + * @return $this + */ + public function cannotBeOverwritten($deny = true) + { + $this->merge()->denyOverwrite($deny); + + return $this; + } + + /** + * Gets the builder for validation rules. + * + * @return ValidationBuilder + */ + protected function validation() + { + if (null === $this->validation) { + $this->validation = new ValidationBuilder($this); + } + + return $this->validation; + } + + /** + * Gets the builder for merging rules. + * + * @return MergeBuilder + */ + protected function merge() + { + if (null === $this->merge) { + $this->merge = new MergeBuilder($this); + } + + return $this->merge; + } + + /** + * Gets the builder for normalization rules. + * + * @return NormalizationBuilder + */ + protected function normalization() + { + if (null === $this->normalization) { + $this->normalization = new NormalizationBuilder($this); + } + + return $this->normalization; + } + + /** + * Instantiate and configure the node according to this definition. + * + * @return NodeInterface $node The node instance + * + * @throws InvalidDefinitionException When the definition is invalid + */ + abstract protected function createNode(); +} diff --git a/vendor/symfony/config/Definition/Builder/NodeParentInterface.php b/vendor/symfony/config/Definition/Builder/NodeParentInterface.php new file mode 100644 index 0000000..305e993 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NodeParentInterface.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\Config\Definition\Builder; + +/** + * An interface that must be implemented by all node parents. + * + * @author Victor Berchet + */ +interface NodeParentInterface +{ +} diff --git a/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php b/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php new file mode 100644 index 0000000..35e3048 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NormalizationBuilder.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +/** + * This class builds normalization conditions. + * + * @author Johannes M. Schmitt + */ +class NormalizationBuilder +{ + protected $node; + public $before = array(); + public $remappings = array(); + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a key to remap to its plural form. + * + * @param string $key The key to remap + * @param string $plural The plural of the key in case of irregular plural + * + * @return $this + */ + public function remap($key, $plural = null) + { + $this->remappings[] = array($key, null === $plural ? $key.'s' : $plural); + + return $this; + } + + /** + * Registers a closure to run before the normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function before(\Closure $closure = null) + { + if (null !== $closure) { + $this->before[] = $closure; + + return $this; + } + + return $this->before[] = new ExprBuilder($this->node); + } +} diff --git a/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php b/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php new file mode 100644 index 0000000..0d0207c --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/NumericNodeDefinition.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; + +/** + * Abstract class that contains common code of integer and float node definitions. + * + * @author David Jeanmonod + */ +abstract class NumericNodeDefinition extends ScalarNodeDefinition +{ + protected $min; + protected $max; + + /** + * Ensures that the value is smaller than the given reference. + * + * @param mixed $max + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function max($max) + { + if (isset($this->min) && $this->min > $max) { + throw new \InvalidArgumentException(sprintf('You cannot define a max(%s) as you already have a min(%s)', $max, $this->min)); + } + $this->max = $max; + + return $this; + } + + /** + * Ensures that the value is bigger than the given reference. + * + * @param mixed $min + * + * @return $this + * + * @throws \InvalidArgumentException when the constraint is inconsistent + */ + public function min($min) + { + if (isset($this->max) && $this->max < $min) { + throw new \InvalidArgumentException(sprintf('You cannot define a min(%s) as you already have a max(%s)', $min, $this->max)); + } + $this->min = $min; + + return $this; + } + + /** + * {@inheritdoc} + * + * @throws InvalidDefinitionException + */ + public function cannotBeEmpty() + { + throw new InvalidDefinitionException('->cannotBeEmpty() is not applicable to NumericNodeDefinition.'); + } +} diff --git a/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php b/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php new file mode 100644 index 0000000..1bf2ad4 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.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\Config\Definition\Builder; + +/** + * An interface that must be implemented by nodes which can have children. + * + * @author Victor Berchet + */ +interface ParentNodeDefinitionInterface +{ + /** + * Returns a builder to add children nodes. + * + * @return NodeBuilder + */ + public function children(); + + /** + * Appends a node definition. + * + * Usage: + * + * $node = $parentNode + * ->children() + * ->scalarNode('foo')->end() + * ->scalarNode('baz')->end() + * ->append($this->getBarNodeDefinition()) + * ->end() + * ; + * + * @return $this + */ + public function append(NodeDefinition $node); + + /** + * Sets a custom children builder. + */ + public function setBuilder(NodeBuilder $builder); +} diff --git a/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php b/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.php new file mode 100644 index 0000000..6170555 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ScalarNodeDefinition.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\ScalarNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class ScalarNodeDefinition extends VariableNodeDefinition +{ + /** + * Instantiate a Node. + * + * @return ScalarNode The node + */ + protected function instantiateNode() + { + return new ScalarNode($this->name, $this->parent); + } +} diff --git a/vendor/symfony/config/Definition/Builder/TreeBuilder.php b/vendor/symfony/config/Definition/Builder/TreeBuilder.php new file mode 100644 index 0000000..384477c --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/TreeBuilder.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\NodeInterface; + +/** + * This is the entry class for building a config tree. + * + * @author Johannes M. Schmitt + */ +class TreeBuilder implements NodeParentInterface +{ + protected $tree; + protected $root; + + /** + * @deprecated since 3.4. To be removed in 4.0 + */ + protected $builder; + + /** + * Creates the root node. + * + * @param string $name The name of the root node + * @param string $type The type of the root node + * @param NodeBuilder $builder A custom node builder instance + * + * @return ArrayNodeDefinition|NodeDefinition The root node (as an ArrayNodeDefinition when the type is 'array') + * + * @throws \RuntimeException When the node type is not supported + */ + public function root($name, $type = 'array', NodeBuilder $builder = null) + { + $builder = $builder ?: new NodeBuilder(); + + return $this->root = $builder->node($name, $type)->setParent($this); + } + + /** + * Builds the tree. + * + * @return NodeInterface + * + * @throws \RuntimeException + */ + public function buildTree() + { + if (null === $this->root) { + throw new \RuntimeException('The configuration tree has no root node.'); + } + if (null !== $this->tree) { + return $this->tree; + } + + return $this->tree = $this->root->getNode(true); + } +} diff --git a/vendor/symfony/config/Definition/Builder/ValidationBuilder.php b/vendor/symfony/config/Definition/Builder/ValidationBuilder.php new file mode 100644 index 0000000..bb2b9eb --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/ValidationBuilder.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\Config\Definition\Builder; + +/** + * This class builds validation conditions. + * + * @author Christophe Coevoet + */ +class ValidationBuilder +{ + protected $node; + public $rules = array(); + + public function __construct(NodeDefinition $node) + { + $this->node = $node; + } + + /** + * Registers a closure to run as normalization or an expression builder to build it if null is provided. + * + * @return ExprBuilder|$this + */ + public function rule(\Closure $closure = null) + { + if (null !== $closure) { + $this->rules[] = $closure; + + return $this; + } + + return $this->rules[] = new ExprBuilder($this->node); + } +} diff --git a/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php b/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.php new file mode 100644 index 0000000..26565e1 --- /dev/null +++ b/vendor/symfony/config/Definition/Builder/VariableNodeDefinition.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\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\VariableNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Johannes M. Schmitt + */ +class VariableNodeDefinition extends NodeDefinition +{ + /** + * Instantiate a Node. + * + * @return VariableNode The node + */ + protected function instantiateNode() + { + return new VariableNode($this->name, $this->parent); + } + + /** + * {@inheritdoc} + */ + protected function createNode() + { + $node = $this->instantiateNode(); + + if (null !== $this->normalization) { + $node->setNormalizationClosures($this->normalization->before); + } + + if (null !== $this->merge) { + $node->setAllowOverwrite($this->merge->allowOverwrite); + } + + if (true === $this->default) { + $node->setDefaultValue($this->defaultValue); + } + + $node->setAllowEmptyValue($this->allowEmptyValue); + $node->addEquivalentValue(null, $this->nullEquivalent); + $node->addEquivalentValue(true, $this->trueEquivalent); + $node->addEquivalentValue(false, $this->falseEquivalent); + $node->setRequired($this->required); + $node->setDeprecated($this->deprecationMessage); + + if (null !== $this->validation) { + $node->setFinalValidationClosures($this->validation->rules); + } + + return $node; + } +} diff --git a/vendor/symfony/config/Definition/ConfigurationInterface.php b/vendor/symfony/config/Definition/ConfigurationInterface.php new file mode 100644 index 0000000..d6456ed --- /dev/null +++ b/vendor/symfony/config/Definition/ConfigurationInterface.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\Config\Definition; + +/** + * Configuration interface. + * + * @author Victor Berchet + */ +interface ConfigurationInterface +{ + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder(); +} diff --git a/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php b/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php new file mode 100644 index 0000000..881457c --- /dev/null +++ b/vendor/symfony/config/Definition/Dumper/XmlReferenceDumper.php @@ -0,0 +1,311 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; + +/** + * Dumps a XML reference configuration for the given configuration/node instance. + * + * @author Wouter J + */ +class XmlReferenceDumper +{ + private $reference; + + public function dump(ConfigurationInterface $configuration, $namespace = null) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace); + } + + public function dumpNode(NodeInterface $node, $namespace = null) + { + $this->reference = ''; + $this->writeNode($node, 0, true, $namespace); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + /** + * @param NodeInterface $node + * @param int $depth + * @param bool $root If the node is the root node + * @param string $namespace The namespace of the node + */ + private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null) + { + $rootName = ($root ? 'config' : $node->getName()); + $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null)); + + // xml remapping + if ($node->getParent()) { + $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) { + return $rootName === $mapping[1]; + }); + + if (\count($remapping)) { + list($singular) = current($remapping); + $rootName = $singular; + } + } + $rootName = str_replace('_', '-', $rootName); + + $rootAttributes = array(); + $rootAttributeComments = array(); + $rootChildren = array(); + $rootComments = array(); + + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + // comments about the root node + if ($rootInfo = $node->getInfo()) { + $rootComments[] = $rootInfo; + } + + if ($rootNamespace) { + $rootComments[] = 'Namespace: '.$rootNamespace; + } + + // render prototyped nodes + if ($node instanceof PrototypedArrayNode) { + $prototype = $node->getPrototype(); + + $info = 'prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + array_unshift($rootComments, $info); + + if ($key = $node->getKeyAttribute()) { + $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key; + } + + if ($prototype instanceof PrototypedArrayNode) { + $prototype->setName($key); + $children = array($key => $prototype); + } elseif ($prototype instanceof ArrayNode) { + $children = $prototype->getChildren(); + } else { + if ($prototype->hasDefaultValue()) { + $prototypeValue = $prototype->getDefaultValue(); + } else { + switch (\get_class($prototype)) { + case 'Symfony\Component\Config\Definition\ScalarNode': + $prototypeValue = 'scalar value'; + break; + + case 'Symfony\Component\Config\Definition\FloatNode': + case 'Symfony\Component\Config\Definition\IntegerNode': + $prototypeValue = 'numeric value'; + break; + + case 'Symfony\Component\Config\Definition\BooleanNode': + $prototypeValue = 'true|false'; + break; + + case 'Symfony\Component\Config\Definition\EnumNode': + $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues())); + break; + + default: + $prototypeValue = 'value'; + } + } + } + } + + // get attributes and elements + foreach ($children as $child) { + if (!$child instanceof ArrayNode) { + // get attributes + + // metadata + $name = str_replace('_', '-', $child->getName()); + $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world + + // comments + $comments = array(); + if ($info = $child->getInfo()) { + $comments[] = $info; + } + + if ($example = $child->getExample()) { + $comments[] = 'Example: '.$example; + } + + if ($child->isRequired()) { + $comments[] = 'Required'; + } + + if ($child->isDeprecated()) { + $comments[] = sprintf('Deprecated (%s)', $child->getDeprecationMessage($child->getName(), $node->getPath())); + } + + if ($child instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues())); + } + + if (\count($comments)) { + $rootAttributeComments[$name] = implode(";\n", $comments); + } + + // default values + if ($child->hasDefaultValue()) { + $value = $child->getDefaultValue(); + } + + // append attribute + $rootAttributes[$name] = $value; + } else { + // get elements + $rootChildren[] = $child; + } + } + } + + // render comments + + // root node comment + if (\count($rootComments)) { + foreach ($rootComments as $comment) { + $this->writeLine('', $depth); + } + } + + // attribute comments + if (\count($rootAttributeComments)) { + foreach ($rootAttributeComments as $attrName => $comment) { + $commentDepth = $depth + 4 + \strlen($attrName) + 2; + $commentLines = explode("\n", $comment); + $multiline = (\count($commentLines) > 1); + $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines); + + if ($multiline) { + $this->writeLine('', $depth); + } else { + $this->writeLine('', $depth); + } + } + } + + // render start tag + attributes + $rootIsVariablePrototype = isset($prototypeValue); + $rootIsEmptyTag = (0 === \count($rootChildren) && !$rootIsVariablePrototype); + $rootOpenTag = '<'.$rootName; + if (1 >= ($attributesCount = \count($rootAttributes))) { + if (1 === $attributesCount) { + $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes))); + } + + $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>'; + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + + $this->writeLine($rootOpenTag, $depth); + } else { + $this->writeLine($rootOpenTag, $depth); + + $i = 1; + + foreach ($rootAttributes as $attrName => $attrValue) { + $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue)); + + $this->writeLine($attr, $depth + 4); + + if ($attributesCount === $i++) { + $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth); + + if ($rootIsVariablePrototype) { + $rootOpenTag .= $prototypeValue.''; + } + } + } + } + + // render children tags + foreach ($rootChildren as $child) { + $this->writeLine(''); + $this->writeNode($child, $depth + 4); + } + + // render end tag + if (!$rootIsEmptyTag && !$rootIsVariablePrototype) { + $this->writeLine(''); + + $rootEndTag = ''; + $this->writeLine($rootEndTag, $depth); + } + } + + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text).PHP_EOL; + } + + /** + * Renders the string conversion of the value. + * + * @param mixed $value + * + * @return string + */ + private function writeValue($value) + { + if ('%%%%not_defined%%%%' === $value) { + return ''; + } + + if (\is_string($value) || is_numeric($value)) { + return $value; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + if (null === $value) { + return 'null'; + } + + if (empty($value)) { + return ''; + } + + if (\is_array($value)) { + return implode(',', $value); + } + } +} diff --git a/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php b/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.php new file mode 100644 index 0000000..fc5d20a --- /dev/null +++ b/vendor/symfony/config/Definition/Dumper/YamlReferenceDumper.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\Config\Definition\Dumper; + +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\EnumNode; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Yaml\Inline; + +/** + * Dumps a Yaml reference configuration for the given configuration/node instance. + * + * @author Kevin Bond + */ +class YamlReferenceDumper +{ + private $reference; + + public function dump(ConfigurationInterface $configuration) + { + return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree()); + } + + public function dumpAtPath(ConfigurationInterface $configuration, $path) + { + $rootNode = $node = $configuration->getConfigTreeBuilder()->buildTree(); + + foreach (explode('.', $path) as $step) { + if (!$node instanceof ArrayNode) { + throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path)); + } + + /** @var NodeInterface[] $children */ + $children = $node instanceof PrototypedArrayNode ? $this->getPrototypeChildren($node) : $node->getChildren(); + + foreach ($children as $child) { + if ($child->getName() === $step) { + $node = $child; + + continue 2; + } + } + + throw new \UnexpectedValueException(sprintf('Unable to find node at path "%s.%s"', $rootNode->getName(), $path)); + } + + return $this->dumpNode($node); + } + + public function dumpNode(NodeInterface $node) + { + $this->reference = ''; + $this->writeNode($node); + $ref = $this->reference; + $this->reference = null; + + return $ref; + } + + /** + * @param NodeInterface $node + * @param NodeInterface|null $parentNode + * @param int $depth + * @param bool $prototypedArray + */ + private function writeNode(NodeInterface $node, NodeInterface $parentNode = null, $depth = 0, $prototypedArray = false) + { + $comments = array(); + $default = ''; + $defaultArray = null; + $children = null; + $example = $node->getExample(); + + // defaults + if ($node instanceof ArrayNode) { + $children = $node->getChildren(); + + if ($node instanceof PrototypedArrayNode) { + $children = $this->getPrototypeChildren($node); + } + + if (!$children) { + if ($node->hasDefaultValue() && \count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } + } elseif ($node instanceof EnumNode) { + $comments[] = 'One of '.implode('; ', array_map('json_encode', $node->getValues())); + $default = $node->hasDefaultValue() ? Inline::dump($node->getDefaultValue()) : '~'; + } else { + $default = '~'; + + if ($node->hasDefaultValue()) { + $default = $node->getDefaultValue(); + + if (\is_array($default)) { + if (\count($defaultArray = $node->getDefaultValue())) { + $default = ''; + } elseif (!\is_array($example)) { + $default = '[]'; + } + } else { + $default = Inline::dump($default); + } + } + } + + // required? + if ($node->isRequired()) { + $comments[] = 'Required'; + } + + // deprecated? + if ($node->isDeprecated()) { + $comments[] = sprintf('Deprecated (%s)', $node->getDeprecationMessage($node->getName(), $parentNode ? $parentNode->getPath() : $node->getPath())); + } + + // example + if ($example && !\is_array($example)) { + $comments[] = 'Example: '.$example; + } + + $default = '' != (string) $default ? ' '.$default : ''; + $comments = \count($comments) ? '# '.implode(', ', $comments) : ''; + + $key = $prototypedArray ? '-' : $node->getName().':'; + $text = rtrim(sprintf('%-21s%s %s', $key, $default, $comments), ' '); + + if ($info = $node->getInfo()) { + $this->writeLine(''); + // indenting multi-line info + $info = str_replace("\n", sprintf("\n%".($depth * 4).'s# ', ' '), $info); + $this->writeLine('# '.$info, $depth * 4); + } + + $this->writeLine($text, $depth * 4); + + // output defaults + if ($defaultArray) { + $this->writeLine(''); + + $message = \count($defaultArray) > 1 ? 'Defaults' : 'Default'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($defaultArray, $depth + 1); + } + + if (\is_array($example)) { + $this->writeLine(''); + + $message = \count($example) > 1 ? 'Examples' : 'Example'; + + $this->writeLine('# '.$message.':', $depth * 4 + 4); + + $this->writeArray($example, $depth + 1); + } + + if ($children) { + foreach ($children as $childNode) { + $this->writeNode($childNode, $node, $depth + 1, $node instanceof PrototypedArrayNode && !$node->getKeyAttribute()); + } + } + } + + /** + * Outputs a single config reference line. + * + * @param string $text + * @param int $indent + */ + private function writeLine($text, $indent = 0) + { + $indent = \strlen($text) + $indent; + $format = '%'.$indent.'s'; + + $this->reference .= sprintf($format, $text)."\n"; + } + + private function writeArray(array $array, $depth) + { + $isIndexed = array_values($array) === $array; + + foreach ($array as $key => $value) { + if (\is_array($value)) { + $val = ''; + } else { + $val = $value; + } + + if ($isIndexed) { + $this->writeLine('- '.$val, $depth * 4); + } else { + $this->writeLine(sprintf('%-20s %s', $key.':', $val), $depth * 4); + } + + if (\is_array($value)) { + $this->writeArray($value, $depth + 1); + } + } + } + + /** + * @param PrototypedArrayNode $node + * + * @return array + */ + private function getPrototypeChildren(PrototypedArrayNode $node) + { + $prototype = $node->getPrototype(); + $key = $node->getKeyAttribute(); + + // Do not expand prototype if it isn't an array node nor uses attribute as key + if (!$key && !$prototype instanceof ArrayNode) { + return $node->getChildren(); + } + + if ($prototype instanceof ArrayNode) { + $keyNode = new ArrayNode($key, $node); + $children = $prototype->getChildren(); + + if ($prototype instanceof PrototypedArrayNode && $prototype->getKeyAttribute()) { + $children = $this->getPrototypeChildren($prototype); + } + + // add children + foreach ($children as $childNode) { + $keyNode->addChild($childNode); + } + } else { + $keyNode = new ScalarNode($key, $node); + } + + $info = 'Prototype'; + if (null !== $prototype->getInfo()) { + $info .= ': '.$prototype->getInfo(); + } + $keyNode->setInfo($info); + + return array($key => $keyNode); + } +} diff --git a/vendor/symfony/config/Definition/EnumNode.php b/vendor/symfony/config/Definition/EnumNode.php new file mode 100644 index 0000000..a214a85 --- /dev/null +++ b/vendor/symfony/config/Definition/EnumNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * Node which only allows a finite set of values. + * + * @author Johannes M. Schmitt + */ +class EnumNode extends ScalarNode +{ + private $values; + + public function __construct($name, NodeInterface $parent = null, array $values = array()) + { + $values = array_unique($values); + if (empty($values)) { + throw new \InvalidArgumentException('$values must contain at least one element.'); + } + + parent::__construct($name, $parent); + $this->values = $values; + } + + public function getValues() + { + return $this->values; + } + + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + + if (!\in_array($value, $this->values, true)) { + $ex = new InvalidConfigurationException(sprintf('The value %s is not allowed for path "%s". Permissible values: %s', json_encode($value), $this->getPath(), implode(', ', array_map('json_encode', $this->values)))); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } +} diff --git a/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php b/vendor/symfony/config/Definition/Exception/DuplicateKeyException.php new file mode 100644 index 0000000..48dd932 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/DuplicateKeyException.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\Config\Definition\Exception; + +/** + * This exception is thrown whenever the key of an array is not unique. This can + * only be the case if the configuration is coming from an XML file. + * + * @author Johannes M. Schmitt + */ +class DuplicateKeyException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/Exception.php b/vendor/symfony/config/Definition/Exception/Exception.php new file mode 100644 index 0000000..8933a49 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/Exception.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\Config\Definition\Exception; + +/** + * Base exception for all configuration exceptions. + * + * @author Johannes M. Schmitt + */ +class Exception extends \RuntimeException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php b/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.php new file mode 100644 index 0000000..726c07f --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/ForbiddenOverwriteException.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\Config\Definition\Exception; + +/** + * This exception is thrown when a configuration path is overwritten from a + * subsequent configuration file, but the entry node specifically forbids this. + * + * @author Johannes M. Schmitt + */ +class ForbiddenOverwriteException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php b/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.php new file mode 100644 index 0000000..3dbc57b --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/InvalidConfigurationException.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\Config\Definition\Exception; + +/** + * A very general exception which can be thrown whenever non of the more specific + * exceptions is suitable. + * + * @author Johannes M. Schmitt + */ +class InvalidConfigurationException extends Exception +{ + private $path; + private $containsHints = false; + + public function setPath($path) + { + $this->path = $path; + } + + public function getPath() + { + return $this->path; + } + + /** + * Adds extra information that is suffixed to the original exception message. + * + * @param string $hint + */ + public function addHint($hint) + { + if (!$this->containsHints) { + $this->message .= "\nHint: ".$hint; + $this->containsHints = true; + } else { + $this->message .= ', '.$hint; + } + } +} diff --git a/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php b/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.php new file mode 100644 index 0000000..98310da --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/InvalidDefinitionException.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\Config\Definition\Exception; + +/** + * Thrown when an error is detected in a node Definition. + * + * @author Victor Berchet + */ +class InvalidDefinitionException extends Exception +{ +} diff --git a/vendor/symfony/config/Definition/Exception/InvalidTypeException.php b/vendor/symfony/config/Definition/Exception/InvalidTypeException.php new file mode 100644 index 0000000..d7ca8c9 --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/InvalidTypeException.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\Config\Definition\Exception; + +/** + * This exception is thrown if an invalid type is encountered. + * + * @author Johannes M. Schmitt + */ +class InvalidTypeException extends InvalidConfigurationException +{ +} diff --git a/vendor/symfony/config/Definition/Exception/UnsetKeyException.php b/vendor/symfony/config/Definition/Exception/UnsetKeyException.php new file mode 100644 index 0000000..863181a --- /dev/null +++ b/vendor/symfony/config/Definition/Exception/UnsetKeyException.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\Config\Definition\Exception; + +/** + * This exception is usually not encountered by the end-user, but only used + * internally to signal the parent scope to unset a key. + * + * @author Johannes M. Schmitt + */ +class UnsetKeyException extends Exception +{ +} diff --git a/vendor/symfony/config/Definition/FloatNode.php b/vendor/symfony/config/Definition/FloatNode.php new file mode 100644 index 0000000..9eb8789 --- /dev/null +++ b/vendor/symfony/config/Definition/FloatNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a float value in the config tree. + * + * @author Jeanmonod David + */ +class FloatNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + // Integers are also accepted, we just cast them + if (\is_int($value)) { + $value = (float) $value; + } + + if (!\is_float($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected float, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} diff --git a/vendor/symfony/config/Definition/IntegerNode.php b/vendor/symfony/config/Definition/IntegerNode.php new file mode 100644 index 0000000..8ec068a --- /dev/null +++ b/vendor/symfony/config/Definition/IntegerNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents an integer value in the config tree. + * + * @author Jeanmonod David + */ +class IntegerNode extends NumericNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!\is_int($value)) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected int, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } +} diff --git a/vendor/symfony/config/Definition/NodeInterface.php b/vendor/symfony/config/Definition/NodeInterface.php new file mode 100644 index 0000000..45f1f68 --- /dev/null +++ b/vendor/symfony/config/Definition/NodeInterface.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * Common Interface among all nodes. + * + * In most cases, it is better to inherit from BaseNode instead of implementing + * this interface yourself. + * + * @author Johannes M. Schmitt + */ +interface NodeInterface +{ + /** + * Returns the name of the node. + * + * @return string The name of the node + */ + public function getName(); + + /** + * Returns the path of the node. + * + * @return string The node path + */ + public function getPath(); + + /** + * Returns true when the node is required. + * + * @return bool If the node is required + */ + public function isRequired(); + + /** + * Returns true when the node has a default value. + * + * @return bool If the node has a default value + */ + public function hasDefaultValue(); + + /** + * Returns the default value of the node. + * + * @return mixed The default value + * + * @throws \RuntimeException if the node has no default value + */ + public function getDefaultValue(); + + /** + * Normalizes a value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidTypeException if the value type is invalid + */ + public function normalize($value); + + /** + * Merges two values together. + * + * @param mixed $leftSide + * @param mixed $rightSide + * + * @return mixed The merged value + * + * @throws ForbiddenOverwriteException if the configuration path cannot be overwritten + * @throws InvalidTypeException if the value type is invalid + */ + public function merge($leftSide, $rightSide); + + /** + * Finalizes a value. + * + * @param mixed $value The value to finalize + * + * @return mixed The finalized value + * + * @throws InvalidTypeException if the value type is invalid + * @throws InvalidConfigurationException if the value is invalid configuration + */ + public function finalize($value); +} diff --git a/vendor/symfony/config/Definition/NumericNode.php b/vendor/symfony/config/Definition/NumericNode.php new file mode 100644 index 0000000..439935e --- /dev/null +++ b/vendor/symfony/config/Definition/NumericNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a numeric value in the config tree. + * + * @author David Jeanmonod + */ +class NumericNode extends ScalarNode +{ + protected $min; + protected $max; + + public function __construct($name, NodeInterface $parent = null, $min = null, $max = null) + { + parent::__construct($name, $parent); + $this->min = $min; + $this->max = $max; + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + $value = parent::finalizeValue($value); + + $errorMsg = null; + if (isset($this->min) && $value < $this->min) { + $errorMsg = sprintf('The value %s is too small for path "%s". Should be greater than or equal to %s', $value, $this->getPath(), $this->min); + } + if (isset($this->max) && $value > $this->max) { + $errorMsg = sprintf('The value %s is too big for path "%s". Should be less than or equal to %s', $value, $this->getPath(), $this->max); + } + if (isset($errorMsg)) { + $ex = new InvalidConfigurationException($errorMsg); + $ex->setPath($this->getPath()); + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + // a numeric value cannot be empty + return false; + } +} diff --git a/vendor/symfony/config/Definition/Processor.php b/vendor/symfony/config/Definition/Processor.php new file mode 100644 index 0000000..3e0feab --- /dev/null +++ b/vendor/symfony/config/Definition/Processor.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\Config\Definition; + +/** + * This class is the entry point for config normalization/merging/finalization. + * + * @author Johannes M. Schmitt + */ +class Processor +{ + /** + * Processes an array of configurations. + * + * @param NodeInterface $configTree The node tree describing the configuration + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function process(NodeInterface $configTree, array $configs) + { + $currentConfig = array(); + foreach ($configs as $config) { + $config = $configTree->normalize($config); + $currentConfig = $configTree->merge($currentConfig, $config); + } + + return $configTree->finalize($currentConfig); + } + + /** + * Processes an array of configurations. + * + * @param ConfigurationInterface $configuration The configuration class + * @param array $configs An array of configuration items to process + * + * @return array The processed configuration + */ + public function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + return $this->process($configuration->getConfigTreeBuilder()->buildTree(), $configs); + } + + /** + * Normalizes a configuration entry. + * + * This method returns a normalize configuration array for a given key + * to remove the differences due to the original format (YAML and XML mainly). + * + * Here is an example. + * + * The configuration in XML: + * + * twig.extension.foo + * twig.extension.bar + * + * And the same configuration in YAML: + * + * extensions: ['twig.extension.foo', 'twig.extension.bar'] + * + * @param array $config A config array + * @param string $key The key to normalize + * @param string $plural The plural form of the key if it is irregular + * + * @return array + */ + public static function normalizeConfig($config, $key, $plural = null) + { + if (null === $plural) { + $plural = $key.'s'; + } + + if (isset($config[$plural])) { + return $config[$plural]; + } + + if (isset($config[$key])) { + if (\is_string($config[$key]) || !\is_int(key($config[$key]))) { + // only one + return array($config[$key]); + } + + return $config[$key]; + } + + return array(); + } +} diff --git a/vendor/symfony/config/Definition/PrototypeNodeInterface.php b/vendor/symfony/config/Definition/PrototypeNodeInterface.php new file mode 100644 index 0000000..8bbb84d --- /dev/null +++ b/vendor/symfony/config/Definition/PrototypeNodeInterface.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\Config\Definition; + +/** + * This interface must be implemented by nodes which can be used as prototypes. + * + * @author Johannes M. Schmitt + */ +interface PrototypeNodeInterface extends NodeInterface +{ + /** + * Sets the name of the node. + * + * @param string $name The name of the node + */ + public function setName($name); +} diff --git a/vendor/symfony/config/Definition/PrototypedArrayNode.php b/vendor/symfony/config/Definition/PrototypedArrayNode.php new file mode 100644 index 0000000..eddcb32 --- /dev/null +++ b/vendor/symfony/config/Definition/PrototypedArrayNode.php @@ -0,0 +1,377 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\DuplicateKeyException; +use Symfony\Component\Config\Definition\Exception\Exception; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Exception\UnsetKeyException; + +/** + * Represents a prototyped Array node in the config tree. + * + * @author Johannes M. Schmitt + */ +class PrototypedArrayNode extends ArrayNode +{ + protected $prototype; + protected $keyAttribute; + protected $removeKeyAttribute = false; + protected $minNumberOfElements = 0; + protected $defaultValue = array(); + protected $defaultChildren; + /** + * @var NodeInterface[] An array of the prototypes of the simplified value children + */ + private $valuePrototypes = array(); + + /** + * Sets the minimum number of elements that a prototype based node must + * contain. By default this is zero, meaning no elements. + * + * @param int $number + */ + public function setMinNumberOfElements($number) + { + $this->minNumberOfElements = $number; + } + + /** + * Sets the attribute which value is to be used as key. + * + * This is useful when you have an indexed array that should be an + * associative array. You can select an item from within the array + * to be the key of the particular item. For example, if "id" is the + * "key", then: + * + * array( + * array('id' => 'my_name', 'foo' => 'bar'), + * ); + * + * becomes + * + * array( + * 'my_name' => array('foo' => 'bar'), + * ); + * + * If you'd like "'id' => 'my_name'" to still be present in the resulting + * array, then you can set the second argument of this method to false. + * + * @param string $attribute The name of the attribute which value is to be used as a key + * @param bool $remove Whether or not to remove the key + */ + public function setKeyAttribute($attribute, $remove = true) + { + $this->keyAttribute = $attribute; + $this->removeKeyAttribute = $remove; + } + + /** + * Retrieves the name of the attribute which value should be used as key. + * + * @return string The name of the attribute + */ + public function getKeyAttribute() + { + return $this->keyAttribute; + } + + /** + * Sets the default value of this node. + * + * @param string $value + * + * @throws \InvalidArgumentException if the default value is not an array + */ + public function setDefaultValue($value) + { + if (!\is_array($value)) { + throw new \InvalidArgumentException($this->getPath().': the default value of an array node has to be an array.'); + } + + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return true; + } + + /** + * Adds default children when none are set. + * + * @param int|string|array|null $children The number of children|The child name|The children names to be added + */ + public function setAddChildrenIfNoneSet($children = array('defaults')) + { + if (null === $children) { + $this->defaultChildren = array('defaults'); + } else { + $this->defaultChildren = \is_int($children) && $children > 0 ? range(1, $children) : (array) $children; + } + } + + /** + * {@inheritdoc} + * + * The default value could be either explicited or derived from the prototype + * default value. + */ + public function getDefaultValue() + { + if (null !== $this->defaultChildren) { + $default = $this->prototype->hasDefaultValue() ? $this->prototype->getDefaultValue() : array(); + $defaults = array(); + foreach (array_values($this->defaultChildren) as $i => $name) { + $defaults[null === $this->keyAttribute ? $i : $name] = $default; + } + + return $defaults; + } + + return $this->defaultValue; + } + + /** + * Sets the node prototype. + */ + public function setPrototype(PrototypeNodeInterface $node) + { + $this->prototype = $node; + } + + /** + * Retrieves the prototype. + * + * @return PrototypeNodeInterface The prototype + */ + public function getPrototype() + { + return $this->prototype; + } + + /** + * Disable adding concrete children for prototyped nodes. + * + * @throws Exception + */ + public function addChild(NodeInterface $node) + { + throw new Exception('A prototyped array node can not have concrete children.'); + } + + /** + * Finalizes the value of this node. + * + * @param mixed $value + * + * @return mixed The finalized value + * + * @throws UnsetKeyException + * @throws InvalidConfigurationException if the node doesn't have enough children + */ + protected function finalizeValue($value) + { + if (false === $value) { + throw new UnsetKeyException(sprintf('Unsetting key for path "%s", value: %s', $this->getPath(), json_encode($value))); + } + + foreach ($value as $k => $v) { + $prototype = $this->getPrototypeForChild($k); + try { + $value[$k] = $prototype->finalize($v); + } catch (UnsetKeyException $e) { + unset($value[$k]); + } + } + + if (\count($value) < $this->minNumberOfElements) { + $ex = new InvalidConfigurationException(sprintf('The path "%s" should have at least %d element(s) defined.', $this->getPath(), $this->minNumberOfElements)); + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * Normalizes the value. + * + * @param mixed $value The value to normalize + * + * @return mixed The normalized value + * + * @throws InvalidConfigurationException + * @throws DuplicateKeyException + */ + protected function normalizeValue($value) + { + if (false === $value) { + return $value; + } + + $value = $this->remapXml($value); + + $isAssoc = array_keys($value) !== range(0, \count($value) - 1); + $normalized = array(); + foreach ($value as $k => $v) { + if (null !== $this->keyAttribute && \is_array($v)) { + if (!isset($v[$this->keyAttribute]) && \is_int($k) && !$isAssoc) { + $ex = new InvalidConfigurationException(sprintf('The attribute "%s" must be set for path "%s".', $this->keyAttribute, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } elseif (isset($v[$this->keyAttribute])) { + $k = $v[$this->keyAttribute]; + + // remove the key attribute when required + if ($this->removeKeyAttribute) { + unset($v[$this->keyAttribute]); + } + + // if only "value" is left + if (array_keys($v) === array('value')) { + $v = $v['value']; + if ($this->prototype instanceof ArrayNode && ($children = $this->prototype->getChildren()) && array_key_exists('value', $children)) { + $valuePrototype = current($this->valuePrototypes) ?: clone $children['value']; + $valuePrototype->parent = $this; + $originalClosures = $this->prototype->normalizationClosures; + if (\is_array($originalClosures)) { + $valuePrototypeClosures = $valuePrototype->normalizationClosures; + $valuePrototype->normalizationClosures = \is_array($valuePrototypeClosures) ? array_merge($originalClosures, $valuePrototypeClosures) : $originalClosures; + } + $this->valuePrototypes[$k] = $valuePrototype; + } + } + } + + if (array_key_exists($k, $normalized)) { + $ex = new DuplicateKeyException(sprintf('Duplicate key "%s" for path "%s".', $k, $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + $prototype = $this->getPrototypeForChild($k); + if (null !== $this->keyAttribute || $isAssoc) { + $normalized[$k] = $prototype->normalize($v); + } else { + $normalized[] = $prototype->normalize($v); + } + } + + return $normalized; + } + + /** + * Merges values together. + * + * @param mixed $leftSide The left side to merge + * @param mixed $rightSide The right side to merge + * + * @return mixed The merged values + * + * @throws InvalidConfigurationException + * @throws \RuntimeException + */ + protected function mergeValues($leftSide, $rightSide) + { + if (false === $rightSide) { + // if this is still false after the last config has been merged the + // finalization pass will take care of removing this key entirely + return false; + } + + if (false === $leftSide || !$this->performDeepMerging) { + return $rightSide; + } + + foreach ($rightSide as $k => $v) { + // prototype, and key is irrelevant, so simply append the element + if (null === $this->keyAttribute) { + $leftSide[] = $v; + continue; + } + + // no conflict + if (!array_key_exists($k, $leftSide)) { + if (!$this->allowNewKeys) { + $ex = new InvalidConfigurationException(sprintf('You are not allowed to define new elements for path "%s". Please define all elements for this path in one config file.', $this->getPath())); + $ex->setPath($this->getPath()); + + throw $ex; + } + + $leftSide[$k] = $v; + continue; + } + + $prototype = $this->getPrototypeForChild($k); + $leftSide[$k] = $prototype->merge($leftSide[$k], $v); + } + + return $leftSide; + } + + /** + * Returns a prototype for the child node that is associated to $key in the value array. + * For general child nodes, this will be $this->prototype. + * But if $this->removeKeyAttribute is true and there are only two keys in the child node: + * one is same as this->keyAttribute and the other is 'value', then the prototype will be different. + * + * For example, assume $this->keyAttribute is 'name' and the value array is as follows: + * + * array( + * array( + * 'name' => 'name001', + * 'value' => 'value001' + * ) + * ) + * + * Now, the key is 0 and the child node is: + * + * array( + * 'name' => 'name001', + * 'value' => 'value001' + * ) + * + * When normalizing the value array, the 'name' element will removed from the child node + * and its value becomes the new key of the child node: + * + * array( + * 'name001' => array('value' => 'value001') + * ) + * + * Now only 'value' element is left in the child node which can be further simplified into a string: + * + * array('name001' => 'value001') + * + * Now, the key becomes 'name001' and the child node becomes 'value001' and + * the prototype of child node 'name001' should be a ScalarNode instead of an ArrayNode instance. + * + * @param string $key The key of the child node + * + * @return mixed The prototype instance + */ + private function getPrototypeForChild($key) + { + $prototype = isset($this->valuePrototypes[$key]) ? $this->valuePrototypes[$key] : $this->prototype; + $prototype->setName($key); + + return $prototype; + } +} diff --git a/vendor/symfony/config/Definition/ScalarNode.php b/vendor/symfony/config/Definition/ScalarNode.php new file mode 100644 index 0000000..53c1ed2 --- /dev/null +++ b/vendor/symfony/config/Definition/ScalarNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a scalar value in the config tree. + * + * The following values are considered scalars: + * * booleans + * * strings + * * null + * * integers + * * floats + * + * @author Johannes M. Schmitt + */ +class ScalarNode extends VariableNode +{ + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + if (!is_scalar($value) && null !== $value) { + $ex = new InvalidTypeException(sprintf('Invalid type for path "%s". Expected scalar, but got %s.', $this->getPath(), \gettype($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + protected function isValueEmpty($value) + { + return null === $value || '' === $value; + } +} diff --git a/vendor/symfony/config/Definition/VariableNode.php b/vendor/symfony/config/Definition/VariableNode.php new file mode 100644 index 0000000..1a3442d --- /dev/null +++ b/vendor/symfony/config/Definition/VariableNode.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\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; + +/** + * This node represents a value of variable type in the config tree. + * + * This node is intended for values of arbitrary type. + * Any PHP type is accepted as a value. + * + * @author Jeremy Mikola + */ +class VariableNode extends BaseNode implements PrototypeNodeInterface +{ + protected $defaultValueSet = false; + protected $defaultValue; + protected $allowEmptyValue = true; + + public function setDefaultValue($value) + { + $this->defaultValueSet = true; + $this->defaultValue = $value; + } + + /** + * {@inheritdoc} + */ + public function hasDefaultValue() + { + return $this->defaultValueSet; + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue() + { + $v = $this->defaultValue; + + return $v instanceof \Closure ? $v() : $v; + } + + /** + * Sets if this node is allowed to have an empty value. + * + * @param bool $boolean True if this entity will accept empty values + */ + public function setAllowEmptyValue($boolean) + { + $this->allowEmptyValue = (bool) $boolean; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + protected function validateType($value) + { + } + + /** + * {@inheritdoc} + */ + protected function finalizeValue($value) + { + if (!$this->allowEmptyValue && $this->isValueEmpty($value)) { + $ex = new InvalidConfigurationException(sprintf('The path "%s" cannot contain an empty value, but got %s.', $this->getPath(), json_encode($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + protected function normalizeValue($value) + { + return $value; + } + + /** + * {@inheritdoc} + */ + protected function mergeValues($leftSide, $rightSide) + { + return $rightSide; + } + + /** + * Evaluates if the given value is to be treated as empty. + * + * By default, PHP's empty() function is used to test for emptiness. This + * method may be overridden by subtypes to better match their understanding + * of empty data. + * + * @param mixed $value + * + * @return bool + */ + protected function isValueEmpty($value) + { + return empty($value); + } +} diff --git a/vendor/symfony/config/DependencyInjection/ConfigCachePass.php b/vendor/symfony/config/DependencyInjection/ConfigCachePass.php new file mode 100644 index 0000000..128bf7e --- /dev/null +++ b/vendor/symfony/config/DependencyInjection/ConfigCachePass.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\Config\DependencyInjection; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + * + * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead. + */ +class ConfigCachePass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + private $factoryServiceId; + private $resourceCheckerTag; + + public function __construct($factoryServiceId = 'config_cache_factory', $resourceCheckerTag = 'config_cache.resource_checker') + { + $this->factoryServiceId = $factoryServiceId; + $this->resourceCheckerTag = $resourceCheckerTag; + } + + public function process(ContainerBuilder $container) + { + $resourceCheckers = $this->findAndSortTaggedServices($this->resourceCheckerTag, $container); + + if (empty($resourceCheckers)) { + return; + } + + $container->getDefinition($this->factoryServiceId)->replaceArgument(0, new IteratorArgument($resourceCheckers)); + } +} diff --git a/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php b/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.php new file mode 100644 index 0000000..6a3b01c --- /dev/null +++ b/vendor/symfony/config/Exception/FileLoaderImportCircularReferenceException.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\Config\Exception; + +/** + * Exception class for when a circular reference is detected when importing resources. + * + * @author Fabien Potencier + */ +class FileLoaderImportCircularReferenceException extends FileLoaderLoadException +{ + public function __construct(array $resources, $code = null, $previous = null) + { + $message = sprintf('Circular reference detected in "%s" ("%s" > "%s").', $this->varToString($resources[0]), implode('" > "', $resources), $resources[0]); + + \Exception::__construct($message, $code, $previous); + } +} diff --git a/vendor/symfony/config/Exception/FileLoaderLoadException.php b/vendor/symfony/config/Exception/FileLoaderLoadException.php new file mode 100644 index 0000000..e74bd67 --- /dev/null +++ b/vendor/symfony/config/Exception/FileLoaderLoadException.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\Config\Exception; + +/** + * Exception class for when a resource cannot be loaded or imported. + * + * @author Ryan Weaver + */ +class FileLoaderLoadException extends \Exception +{ + /** + * @param string $resource The resource that could not be imported + * @param string $sourceResource The original resource importing the new resource + * @param int $code The error code + * @param \Exception $previous A previous exception + * @param string $type The type of resource + */ + public function __construct($resource, $sourceResource = null, $code = null, $previous = null, $type = null) + { + $message = ''; + if ($previous) { + // Include the previous exception, to help the user see what might be the underlying cause + + // Trim the trailing period of the previous message. We only want 1 period remove so no rtrim... + if ('.' === substr($previous->getMessage(), -1)) { + $trimmedMessage = substr($previous->getMessage(), 0, -1); + $message .= sprintf('%s', $trimmedMessage).' in '; + } else { + $message .= sprintf('%s', $previous->getMessage()).' in '; + } + $message .= $resource.' '; + + // show tweaked trace to complete the human readable sentence + if (null === $sourceResource) { + $message .= sprintf('(which is loaded in resource "%s")', $this->varToString($resource)); + } else { + $message .= sprintf('(which is being imported from "%s")', $this->varToString($sourceResource)); + } + $message .= '.'; + + // if there's no previous message, present it the default way + } elseif (null === $sourceResource) { + $message .= sprintf('Cannot load resource "%s".', $this->varToString($resource)); + } else { + $message .= sprintf('Cannot import resource "%s" from "%s".', $this->varToString($resource), $this->varToString($sourceResource)); + } + + // Is the resource located inside a bundle? + if ('@' === $resource[0]) { + $parts = explode(\DIRECTORY_SEPARATOR, $resource); + $bundle = substr($parts[0], 1); + $message .= sprintf(' Make sure the "%s" bundle is correctly registered and loaded in the application kernel class.', $bundle); + $message .= sprintf(' If the bundle is registered, make sure the bundle path "%s" is not empty.', $resource); + } elseif (null !== $type) { + // maybe there is no loader for this specific type + if ('annotation' === $type) { + $message .= ' Make sure annotations are installed and enabled.'; + } else { + $message .= sprintf(' Make sure there is a loader supporting the "%s" type.', $type); + } + } + + parent::__construct($message, $code, $previous); + } + + protected 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/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php b/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.php new file mode 100644 index 0000000..af764eb --- /dev/null +++ b/vendor/symfony/config/Exception/FileLocatorFileNotFoundException.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\Config\Exception; + +/** + * File locator exception if a file does not exist. + * + * @author Leo Feyer + */ +class FileLocatorFileNotFoundException extends \InvalidArgumentException +{ + private $paths; + + public function __construct($message = '', $code = 0, $previous = null, array $paths = array()) + { + parent::__construct($message, $code, $previous); + + $this->paths = $paths; + } + + public function getPaths() + { + return $this->paths; + } +} diff --git a/vendor/symfony/config/FileLocator.php b/vendor/symfony/config/FileLocator.php new file mode 100644 index 0000000..2c9cea0 --- /dev/null +++ b/vendor/symfony/config/FileLocator.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\Config; + +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; + +/** + * FileLocator uses an array of pre-defined paths to find files. + * + * @author Fabien Potencier + */ +class FileLocator implements FileLocatorInterface +{ + protected $paths; + + /** + * @param string|array $paths A path or an array of paths where to look for resources + */ + public function __construct($paths = array()) + { + $this->paths = (array) $paths; + } + + /** + * {@inheritdoc} + */ + public function locate($name, $currentPath = null, $first = true) + { + if ('' == $name) { + throw new \InvalidArgumentException('An empty file name is not valid to be located.'); + } + + if ($this->isAbsolutePath($name)) { + if (!file_exists($name)) { + throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist.', $name), 0, null, array($name)); + } + + return $name; + } + + $paths = $this->paths; + + if (null !== $currentPath) { + array_unshift($paths, $currentPath); + } + + $paths = array_unique($paths); + $filepaths = $notfound = array(); + + foreach ($paths as $path) { + if (@file_exists($file = $path.\DIRECTORY_SEPARATOR.$name)) { + if (true === $first) { + return $file; + } + $filepaths[] = $file; + } else { + $notfound[] = $file; + } + } + + if (!$filepaths) { + throw new FileLocatorFileNotFoundException(sprintf('The file "%s" does not exist (in: %s).', $name, implode(', ', $paths)), 0, null, $notfound); + } + + return $filepaths; + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + private function isAbsolutePath($file) + { + if ('/' === $file[0] || '\\' === $file[0] + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && ('\\' === $file[2] || '/' === $file[2]) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ) { + return true; + } + + return false; + } +} diff --git a/vendor/symfony/config/FileLocatorInterface.php b/vendor/symfony/config/FileLocatorInterface.php new file mode 100644 index 0000000..cf3c2e5 --- /dev/null +++ b/vendor/symfony/config/FileLocatorInterface.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\Config; + +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; + +/** + * @author Fabien Potencier + */ +interface FileLocatorInterface +{ + /** + * Returns a full path for a given file name. + * + * @param string $name The file name to locate + * @param string|null $currentPath The current path + * @param bool $first Whether to return the first occurrence or an array of filenames + * + * @return string|array The full path to the file or an array of file paths + * + * @throws \InvalidArgumentException If $name is empty + * @throws FileLocatorFileNotFoundException If a file is not found + */ + public function locate($name, $currentPath = null, $first = true); +} diff --git a/vendor/symfony/config/LICENSE b/vendor/symfony/config/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/config/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/vendor/symfony/config/Loader/DelegatingLoader.php b/vendor/symfony/config/Loader/DelegatingLoader.php new file mode 100644 index 0000000..452e81c --- /dev/null +++ b/vendor/symfony/config/Loader/DelegatingLoader.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\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +/** + * DelegatingLoader delegates loading to other loaders using a loader resolver. + * + * This loader acts as an array of LoaderInterface objects - each having + * a chance to load a given resource (handled by the resolver) + * + * @author Fabien Potencier + */ +class DelegatingLoader extends Loader +{ + public function __construct(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + if (false === $loader = $this->resolver->resolve($resource, $type)) { + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + + return $loader->load($resource, $type); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return false !== $this->resolver->resolve($resource, $type); + } +} diff --git a/vendor/symfony/config/Loader/FileLoader.php b/vendor/symfony/config/Loader/FileLoader.php new file mode 100644 index 0000000..016ac41 --- /dev/null +++ b/vendor/symfony/config/Loader/FileLoader.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException; +use Symfony\Component\Config\Exception\FileLoaderLoadException; +use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\Config\Resource\GlobResource; + +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends Loader +{ + protected static $loading = array(); + + protected $locator; + + private $currentDir; + + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * Sets the current directory. + * + * @param string $dir + */ + public function setCurrentDir($dir) + { + $this->currentDir = $dir; + } + + /** + * Returns the file locator used by this loader. + * + * @return FileLocatorInterface + */ + public function getLocator() + { + return $this->locator; + } + + /** + * Imports a resource. + * + * @param mixed $resource A Resource + * @param string|null $type The resource type or null if unknown + * @param bool $ignoreErrors Whether to ignore import errors or not + * @param string|null $sourceResource The original resource importing the new resource + * + * @return mixed + * + * @throws FileLoaderLoadException + * @throws FileLoaderImportCircularReferenceException + * @throws FileLocatorFileNotFoundException + */ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + if (\is_string($resource) && \strlen($resource) !== $i = strcspn($resource, '*?{[')) { + $ret = array(); + $isSubpath = 0 !== $i && false !== strpos(substr($resource, 0, $i), '/'); + foreach ($this->glob($resource, false, $_, $ignoreErrors || !$isSubpath) as $path => $info) { + if (null !== $res = $this->doImport($path, $type, $ignoreErrors, $sourceResource)) { + $ret[] = $res; + } + $isSubpath = true; + } + + if ($isSubpath) { + return isset($ret[1]) ? $ret : (isset($ret[0]) ? $ret[0] : null); + } + } + + return $this->doImport($resource, $type, $ignoreErrors, $sourceResource); + } + + /** + * @internal + */ + protected function glob($pattern, $recursive, &$resource = null, $ignoreErrors = false) + { + if (\strlen($pattern) === $i = strcspn($pattern, '*?{[')) { + $prefix = $pattern; + $pattern = ''; + } elseif (0 === $i || false === strpos(substr($pattern, 0, $i), '/')) { + $prefix = '.'; + $pattern = '/'.$pattern; + } else { + $prefix = \dirname(substr($pattern, 0, 1 + $i)); + $pattern = substr($pattern, \strlen($prefix)); + } + + try { + $prefix = $this->locator->locate($prefix, $this->currentDir, true); + } catch (FileLocatorFileNotFoundException $e) { + if (!$ignoreErrors) { + throw $e; + } + + $resource = array(); + foreach ($e->getPaths() as $path) { + $resource[] = new FileExistenceResource($path); + } + + return; + } + $resource = new GlobResource($prefix, $pattern, $recursive); + + foreach ($resource as $path => $info) { + yield $path => $info; + } + } + + private function doImport($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + try { + $loader = $this->resolve($resource, $type); + + if ($loader instanceof self && null !== $this->currentDir) { + $resource = $loader->getLocator()->locate($resource, $this->currentDir, false); + } + + $resources = \is_array($resource) ? $resource : array($resource); + for ($i = 0; $i < $resourcesCount = \count($resources); ++$i) { + if (isset(self::$loading[$resources[$i]])) { + if ($i == $resourcesCount - 1) { + throw new FileLoaderImportCircularReferenceException(array_keys(self::$loading)); + } + } else { + $resource = $resources[$i]; + break; + } + } + self::$loading[$resource] = true; + + try { + $ret = $loader->load($resource, $type); + } finally { + unset(self::$loading[$resource]); + } + + return $ret; + } catch (FileLoaderImportCircularReferenceException $e) { + throw $e; + } catch (\Exception $e) { + if (!$ignoreErrors) { + // prevent embedded imports from nesting multiple exceptions + if ($e instanceof FileLoaderLoadException) { + throw $e; + } + + throw new FileLoaderLoadException($resource, $sourceResource, null, $e, $type); + } + } + } +} diff --git a/vendor/symfony/config/Loader/GlobFileLoader.php b/vendor/symfony/config/Loader/GlobFileLoader.php new file mode 100644 index 0000000..f432b45 --- /dev/null +++ b/vendor/symfony/config/Loader/GlobFileLoader.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\Config\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Fabien Potencier + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + return $this->import($resource); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'glob' === $type; + } +} diff --git a/vendor/symfony/config/Loader/Loader.php b/vendor/symfony/config/Loader/Loader.php new file mode 100644 index 0000000..d2f2ec9 --- /dev/null +++ b/vendor/symfony/config/Loader/Loader.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Loader; + +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +/** + * Loader is the abstract class used by all built-in loaders. + * + * @author Fabien Potencier + */ +abstract class Loader implements LoaderInterface +{ + protected $resolver; + + /** + * {@inheritdoc} + */ + public function getResolver() + { + return $this->resolver; + } + + /** + * {@inheritdoc} + */ + public function setResolver(LoaderResolverInterface $resolver) + { + $this->resolver = $resolver; + } + + /** + * Imports a resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return mixed + */ + public function import($resource, $type = null) + { + return $this->resolve($resource, $type)->load($resource, $type); + } + + /** + * Finds a loader able to load an imported resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return $this|LoaderInterface + * + * @throws FileLoaderLoadException If no loader is found + */ + public function resolve($resource, $type = null) + { + if ($this->supports($resource, $type)) { + return $this; + } + + $loader = null === $this->resolver ? false : $this->resolver->resolve($resource, $type); + + if (false === $loader) { + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + + return $loader; + } +} diff --git a/vendor/symfony/config/Loader/LoaderInterface.php b/vendor/symfony/config/Loader/LoaderInterface.php new file mode 100644 index 0000000..dfca9dd --- /dev/null +++ b/vendor/symfony/config/Loader/LoaderInterface.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\Config\Loader; + +/** + * LoaderInterface is the interface implemented by all loader classes. + * + * @author Fabien Potencier + */ +interface LoaderInterface +{ + /** + * Loads a resource. + * + * @param mixed $resource The resource + * @param string|null $type The resource type or null if unknown + * + * @throws \Exception If something went wrong + */ + public function load($resource, $type = null); + + /** + * Returns whether this class supports the given resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return bool True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null); + + /** + * Gets the loader resolver. + * + * @return LoaderResolverInterface A LoaderResolverInterface instance + */ + public function getResolver(); + + /** + * Sets the loader resolver. + */ + public function setResolver(LoaderResolverInterface $resolver); +} diff --git a/vendor/symfony/config/Loader/LoaderResolver.php b/vendor/symfony/config/Loader/LoaderResolver.php new file mode 100644 index 0000000..9299bc0 --- /dev/null +++ b/vendor/symfony/config/Loader/LoaderResolver.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\Config\Loader; + +/** + * LoaderResolver selects a loader for a given resource. + * + * A resource can be anything (e.g. a full path to a config file or a Closure). + * Each loader determines whether it can load a resource and how. + * + * @author Fabien Potencier + */ +class LoaderResolver implements LoaderResolverInterface +{ + /** + * @var LoaderInterface[] An array of LoaderInterface objects + */ + private $loaders = array(); + + /** + * @param LoaderInterface[] $loaders An array of loaders + */ + public function __construct(array $loaders = array()) + { + foreach ($loaders as $loader) { + $this->addLoader($loader); + } + } + + /** + * {@inheritdoc} + */ + public function resolve($resource, $type = null) + { + foreach ($this->loaders as $loader) { + if ($loader->supports($resource, $type)) { + return $loader; + } + } + + return false; + } + + public function addLoader(LoaderInterface $loader) + { + $this->loaders[] = $loader; + $loader->setResolver($this); + } + + /** + * Returns the registered loaders. + * + * @return LoaderInterface[] An array of LoaderInterface instances + */ + public function getLoaders() + { + return $this->loaders; + } +} diff --git a/vendor/symfony/config/Loader/LoaderResolverInterface.php b/vendor/symfony/config/Loader/LoaderResolverInterface.php new file mode 100644 index 0000000..40f1a1a --- /dev/null +++ b/vendor/symfony/config/Loader/LoaderResolverInterface.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\Config\Loader; + +/** + * LoaderResolverInterface selects a loader for a given resource. + * + * @author Fabien Potencier + */ +interface LoaderResolverInterface +{ + /** + * Returns a loader able to load the resource. + * + * @param mixed $resource A resource + * @param string|null $type The resource type or null if unknown + * + * @return LoaderInterface|false The loader or false if none is able to load the resource + */ + public function resolve($resource, $type = null); +} diff --git a/vendor/symfony/config/README.md b/vendor/symfony/config/README.md new file mode 100644 index 0000000..bf400da --- /dev/null +++ b/vendor/symfony/config/README.md @@ -0,0 +1,15 @@ +Config Component +================ + +The Config component provides several classes to help you find, load, combine, +autofill and validate configuration values of any kind, whatever their source +may be (YAML, XML, INI files, or for instance a database). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/config/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/vendor/symfony/config/Resource/ClassExistenceResource.php b/vendor/symfony/config/Resource/ClassExistenceResource.php new file mode 100644 index 0000000..9a1d7ba --- /dev/null +++ b/vendor/symfony/config/Resource/ClassExistenceResource.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\Config\Resource; + +/** + * ClassExistenceResource represents a class existence. + * Freshness is only evaluated against resource existence. + * + * The resource must be a fully-qualified class name. + * + * @author Fabien Potencier + */ +class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $exists; + + private static $autoloadLevel = 0; + private static $autoloadedClass; + private static $existsCache = array(); + + /** + * @param string $resource The fully-qualified class name + * @param bool|null $exists Boolean when the existency check has already been done + */ + public function __construct($resource, $exists = null) + { + $this->resource = $resource; + if (null !== $exists) { + $this->exists = (bool) $exists; + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + * + * @throws \ReflectionException when a parent class/interface/trait is not found + */ + public function isFresh($timestamp) + { + $loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + + if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) { + $exists = $exists || $loaded; + } elseif (!$exists = $loaded) { + if (!self::$autoloadLevel++) { + spl_autoload_register(__CLASS__.'::throwOnRequiredClass'); + } + $autoloadedClass = self::$autoloadedClass; + self::$autoloadedClass = $this->resource; + + try { + $exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false); + } catch (\ReflectionException $e) { + if (0 >= $timestamp) { + unset(self::$existsCache[1][$this->resource]); + throw $e; + } + } finally { + self::$autoloadedClass = $autoloadedClass; + if (!--self::$autoloadLevel) { + spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass'); + } + } + } + + if (null === $this->exists) { + $this->exists = $exists; + } + + return $this->exists xor !$exists; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + if (null === $this->exists) { + $this->isFresh(0); + } + + return serialize(array($this->resource, $this->exists)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } + + /** + * @throws \ReflectionException When $class is not found and is required + */ + private static function throwOnRequiredClass($class) + { + if (self::$autoloadedClass === $class) { + return; + } + $e = new \ReflectionException("Class $class not found"); + $trace = $e->getTrace(); + $autoloadFrame = array( + 'function' => 'spl_autoload_call', + 'args' => array($class), + ); + $i = 1 + array_search($autoloadFrame, $trace, true); + + if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) { + switch ($trace[$i]['function']) { + case 'get_class_methods': + case 'get_class_vars': + case 'get_parent_class': + case 'is_a': + case 'is_subclass_of': + case 'class_exists': + case 'class_implements': + case 'class_parents': + case 'trait_exists': + case 'defined': + case 'interface_exists': + case 'method_exists': + case 'property_exists': + case 'is_callable': + return; + } + + $props = array( + 'file' => $trace[$i]['file'], + 'line' => $trace[$i]['line'], + 'trace' => \array_slice($trace, 1 + $i), + ); + + foreach ($props as $p => $v) { + $r = new \ReflectionProperty('Exception', $p); + $r->setAccessible(true); + $r->setValue($e, $v); + } + } + + throw $e; + } +} diff --git a/vendor/symfony/config/Resource/ComposerResource.php b/vendor/symfony/config/Resource/ComposerResource.php new file mode 100644 index 0000000..9c170c0 --- /dev/null +++ b/vendor/symfony/config/Resource/ComposerResource.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * ComposerResource tracks the PHP version and Composer dependencies. + * + * @author Nicolas Grekas + */ +class ComposerResource implements SelfCheckingResourceInterface, \Serializable +{ + private $vendors; + + private static $runtimeVendors; + + public function __construct() + { + self::refresh(); + $this->vendors = self::$runtimeVendors; + } + + public function getVendors() + { + return array_keys($this->vendors); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return __CLASS__; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + self::refresh(); + + return self::$runtimeVendors === $this->vendors; + } + + public function serialize() + { + return serialize($this->vendors); + } + + public function unserialize($serialized) + { + $this->vendors = unserialize($serialized); + } + + private static function refresh() + { + self::$runtimeVendors = array(); + + foreach (get_declared_classes() as $class) { + if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) { + $r = new \ReflectionClass($class); + $v = \dirname(\dirname($r->getFileName())); + if (file_exists($v.'/composer/installed.json')) { + self::$runtimeVendors[$v] = @filemtime($v.'/composer/installed.json'); + } + } + } + } +} diff --git a/vendor/symfony/config/Resource/DirectoryResource.php b/vendor/symfony/config/Resource/DirectoryResource.php new file mode 100644 index 0000000..b178639 --- /dev/null +++ b/vendor/symfony/config/Resource/DirectoryResource.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +/** + * DirectoryResource represents a resources stored in a subdirectory tree. + * + * @author Fabien Potencier + */ +class DirectoryResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + private $pattern; + + /** + * @param string $resource The file path to the resource + * @param string|null $pattern A pattern to restrict monitored files + * + * @throws \InvalidArgumentException + */ + public function __construct($resource, $pattern = null) + { + $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); + $this->pattern = $pattern; + + if (false === $this->resource || !is_dir($this->resource)) { + throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $resource)); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return md5(serialize(array($this->resource, $this->pattern))); + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * Returns the pattern to restrict monitored files. + * + * @return string|null + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + if (!is_dir($this->resource)) { + return false; + } + + if ($timestamp < filemtime($this->resource)) { + return false; + } + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->resource), \RecursiveIteratorIterator::SELF_FIRST) as $file) { + // if regex filtering is enabled only check matching files + if ($this->pattern && $file->isFile() && !preg_match($this->pattern, $file->getBasename())) { + continue; + } + + // always monitor directories for changes, except the .. entries + // (otherwise deleted files wouldn't get detected) + if ($file->isDir() && '/..' === substr($file, -3)) { + continue; + } + + // for broken links + try { + $fileMTime = $file->getMTime(); + } catch (\RuntimeException $e) { + continue; + } + + // early return if a file's mtime exceeds the passed timestamp + if ($timestamp < $fileMTime) { + return false; + } + } + + return true; + } + + public function serialize() + { + return serialize(array($this->resource, $this->pattern)); + } + + public function unserialize($serialized) + { + list($this->resource, $this->pattern) = unserialize($serialized); + } +} diff --git a/vendor/symfony/config/Resource/FileExistenceResource.php b/vendor/symfony/config/Resource/FileExistenceResource.php new file mode 100644 index 0000000..6396ddd --- /dev/null +++ b/vendor/symfony/config/Resource/FileExistenceResource.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\Config\Resource; + +/** + * FileExistenceResource represents a resource stored on the filesystem. + * Freshness is only evaluated against resource creation or deletion. + * + * The resource can be a file or a directory. + * + * @author Charles-Henri Bruyand + */ +class FileExistenceResource implements SelfCheckingResourceInterface, \Serializable +{ + private $resource; + + private $exists; + + /** + * @param string $resource The file path to the resource + */ + public function __construct($resource) + { + $this->resource = (string) $resource; + $this->exists = file_exists($resource); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The file path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return file_exists($this->resource) === $this->exists; + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize(array($this->resource, $this->exists)); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + list($this->resource, $this->exists) = unserialize($serialized); + } +} diff --git a/vendor/symfony/config/Resource/FileResource.php b/vendor/symfony/config/Resource/FileResource.php new file mode 100644 index 0000000..1d373a6 --- /dev/null +++ b/vendor/symfony/config/Resource/FileResource.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\Config\Resource; + +/** + * FileResource represents a resource stored on the filesystem. + * + * The resource can be a file or a directory. + * + * @author Fabien Potencier + */ +class FileResource implements SelfCheckingResourceInterface, \Serializable +{ + /** + * @var string|false + */ + private $resource; + + /** + * @param string $resource The file path to the resource + * + * @throws \InvalidArgumentException + */ + public function __construct($resource) + { + $this->resource = realpath($resource) ?: (file_exists($resource) ? $resource : false); + + if (false === $this->resource) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not exist.', $resource)); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->resource; + } + + /** + * @return string The canonicalized, absolute path to the resource + */ + public function getResource() + { + return $this->resource; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + return false !== ($filemtime = @filemtime($this->resource)) && $filemtime <= $timestamp; + } + + public function serialize() + { + return serialize($this->resource); + } + + public function unserialize($serialized) + { + $this->resource = unserialize($serialized); + } +} diff --git a/vendor/symfony/config/Resource/GlobResource.php b/vendor/symfony/config/Resource/GlobResource.php new file mode 100644 index 0000000..e3c3be1 --- /dev/null +++ b/vendor/symfony/config/Resource/GlobResource.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\Config\Resource; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Glob; + +/** + * GlobResource represents a set of resources stored on the filesystem. + * + * Only existence/removal is tracked (not mtimes.) + * + * @author Nicolas Grekas + */ +class GlobResource implements \IteratorAggregate, SelfCheckingResourceInterface, \Serializable +{ + private $prefix; + private $pattern; + private $recursive; + private $hash; + + /** + * @param string $prefix A directory prefix + * @param string $pattern A glob pattern + * @param bool $recursive Whether directories should be scanned recursively or not + * + * @throws \InvalidArgumentException + */ + public function __construct($prefix, $pattern, $recursive) + { + $this->prefix = realpath($prefix) ?: (file_exists($prefix) ? $prefix : false); + $this->pattern = $pattern; + $this->recursive = $recursive; + + if (false === $this->prefix) { + throw new \InvalidArgumentException(sprintf('The path "%s" does not exist.', $prefix)); + } + } + + public function getPrefix() + { + return $this->prefix; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return 'glob.'.$this->prefix.$this->pattern.(int) $this->recursive; + } + + /** + * {@inheritdoc} + */ + public function isFresh($timestamp) + { + $hash = $this->computeHash(); + + if (null === $this->hash) { + $this->hash = $hash; + } + + return $this->hash === $hash; + } + + public function serialize() + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + } + + return serialize(array($this->prefix, $this->pattern, $this->recursive, $this->hash)); + } + + public function unserialize($serialized) + { + list($this->prefix, $this->pattern, $this->recursive, $this->hash) = unserialize($serialized); + } + + public function getIterator() + { + if (!file_exists($this->prefix) || (!$this->recursive && '' === $this->pattern)) { + return; + } + + if (0 !== strpos($this->prefix, 'phar://') && false === strpos($this->pattern, '/**/') && (\defined('GLOB_BRACE') || false === strpos($this->pattern, '{'))) { + foreach (glob($this->prefix.$this->pattern, \defined('GLOB_BRACE') ? GLOB_BRACE : 0) as $path) { + if ($this->recursive && is_dir($path)) { + $files = iterator_to_array(new \RecursiveIteratorIterator( + new \RecursiveCallbackFilterIterator( + new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), + function (\SplFileInfo $file) { return '.' !== $file->getBasename()[0]; } + ), + \RecursiveIteratorIterator::LEAVES_ONLY + )); + uasort($files, function (\SplFileInfo $a, \SplFileInfo $b) { + return (string) $a > (string) $b ? 1 : -1; + }); + + foreach ($files as $path => $info) { + if ($info->isFile()) { + yield $path => $info; + } + } + } elseif (is_file($path)) { + yield $path => new \SplFileInfo($path); + } + } + + return; + } + + if (!class_exists(Finder::class)) { + throw new \LogicException(sprintf('Extended glob pattern "%s" cannot be used as the Finder component is not installed.', $this->pattern)); + } + + $finder = new Finder(); + $regex = Glob::toRegex($this->pattern); + if ($this->recursive) { + $regex = substr_replace($regex, '(/|$)', -2, 1); + } + + $prefixLen = \strlen($this->prefix); + foreach ($finder->followLinks()->sortByName()->in($this->prefix) as $path => $info) { + if (preg_match($regex, substr('\\' === \DIRECTORY_SEPARATOR ? str_replace('\\', '/', $path) : $path, $prefixLen)) && $info->isFile()) { + yield $path => $info; + } + } + } + + private function computeHash() + { + $hash = hash_init('md5'); + + foreach ($this->getIterator() as $path => $info) { + hash_update($hash, $path."\n"); + } + + return hash_final($hash); + } +} diff --git a/vendor/symfony/config/Resource/ReflectionClassResource.php b/vendor/symfony/config/Resource/ReflectionClassResource.php new file mode 100644 index 0000000..65156a0 --- /dev/null +++ b/vendor/symfony/config/Resource/ReflectionClassResource.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Resource; + +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * @author Nicolas Grekas + */ +class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable +{ + private $files = array(); + private $className; + private $classReflector; + private $excludedVendors = array(); + private $hash; + + public function __construct(\ReflectionClass $classReflector, $excludedVendors = array()) + { + $this->className = $classReflector->name; + $this->classReflector = $classReflector; + $this->excludedVendors = $excludedVendors; + } + + public function isFresh($timestamp) + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + + foreach ($this->files as $file => $v) { + if (false === $filemtime = @filemtime($file)) { + return false; + } + + if ($filemtime > $timestamp) { + return $this->hash === $this->computeHash(); + } + } + + return true; + } + + public function __toString() + { + return 'reflection.'.$this->className; + } + + public function serialize() + { + if (null === $this->hash) { + $this->hash = $this->computeHash(); + $this->loadFiles($this->classReflector); + } + + return serialize(array($this->files, $this->className, $this->hash)); + } + + public function unserialize($serialized) + { + list($this->files, $this->className, $this->hash) = unserialize($serialized); + } + + private function loadFiles(\ReflectionClass $class) + { + foreach ($class->getInterfaces() as $v) { + $this->loadFiles($v); + } + do { + $file = $class->getFileName(); + if (false !== $file && file_exists($file)) { + foreach ($this->excludedVendors as $vendor) { + if (0 === strpos($file, $vendor) && false !== strpbrk(substr($file, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { + $file = false; + break; + } + } + if ($file) { + $this->files[$file] = null; + } + } + foreach ($class->getTraits() as $v) { + $this->loadFiles($v); + } + } while ($class = $class->getParentClass()); + } + + private function computeHash() + { + if (null === $this->classReflector) { + try { + $this->classReflector = new \ReflectionClass($this->className); + } catch (\ReflectionException $e) { + // the class does not exist anymore + return false; + } + } + $hash = hash_init('md5'); + + foreach ($this->generateSignature($this->classReflector) as $info) { + hash_update($hash, $info); + } + + return hash_final($hash); + } + + private function generateSignature(\ReflectionClass $class) + { + yield $class->getDocComment(); + yield (int) $class->isFinal(); + yield (int) $class->isAbstract(); + + if ($class->isTrait()) { + yield print_r(class_uses($class->name), true); + } else { + yield print_r(class_parents($class->name), true); + yield print_r(class_implements($class->name), true); + yield print_r($class->getConstants(), true); + } + + if (!$class->isInterface()) { + $defaults = $class->getDefaultProperties(); + + foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) { + yield $p->getDocComment().$p; + yield print_r($defaults[$p->name], true); + } + } + + if (\defined('HHVM_VERSION')) { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + // workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762 + yield preg_replace('/^ @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name)); + } + } else { + foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) { + yield preg_replace('/^ @@.*/m', '', $m); + + $defaults = array(); + foreach ($m->getParameters() as $p) { + $defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null; + } + yield print_r($defaults, true); + } + } + + if ($class->isAbstract() || $class->isInterface() || $class->isTrait()) { + return; + } + + if (interface_exists(EventSubscriberInterface::class, false) && $class->isSubclassOf(EventSubscriberInterface::class)) { + yield EventSubscriberInterface::class; + yield print_r(\call_user_func(array($class->name, 'getSubscribedEvents')), true); + } + + if (interface_exists(ServiceSubscriberInterface::class, false) && $class->isSubclassOf(ServiceSubscriberInterface::class)) { + yield ServiceSubscriberInterface::class; + yield print_r(\call_user_func(array($class->name, 'getSubscribedServices')), true); + } + } +} + +/** + * @internal + */ +class ReflectionMethodHhvmWrapper extends \ReflectionMethod +{ + public function getParameters() + { + $params = array(); + + foreach (parent::getParameters() as $i => $p) { + $params[] = new ReflectionParameterHhvmWrapper(array($this->class, $this->name), $i); + } + + return $params; + } +} + +/** + * @internal + */ +class ReflectionParameterHhvmWrapper extends \ReflectionParameter +{ + public function getDefaultValue() + { + return array($this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null); + } +} diff --git a/vendor/symfony/config/Resource/ResourceInterface.php b/vendor/symfony/config/Resource/ResourceInterface.php new file mode 100644 index 0000000..d98fd42 --- /dev/null +++ b/vendor/symfony/config/Resource/ResourceInterface.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\Config\Resource; + +/** + * ResourceInterface is the interface that must be implemented by all Resource classes. + * + * @author Fabien Potencier + */ +interface ResourceInterface +{ + /** + * Returns a string representation of the Resource. + * + * This method is necessary to allow for resource de-duplication, for example by means + * of array_unique(). The string returned need not have a particular meaning, but has + * to be identical for different ResourceInterface instances referring to the same + * resource; and it should be unlikely to collide with that of other, unrelated + * resource instances. + * + * @return string A string representation unique to the underlying Resource + */ + public function __toString(); +} diff --git a/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php b/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php new file mode 100644 index 0000000..d72203b --- /dev/null +++ b/vendor/symfony/config/Resource/SelfCheckingResourceChecker.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\Config\Resource; + +use Symfony\Component\Config\ResourceCheckerInterface; + +/** + * Resource checker for instances of SelfCheckingResourceInterface. + * + * As these resources perform the actual check themselves, we can provide + * this class as a standard way of validating them. + * + * @author Matthias Pigulla + */ +class SelfCheckingResourceChecker implements ResourceCheckerInterface +{ + public function supports(ResourceInterface $metadata) + { + return $metadata instanceof SelfCheckingResourceInterface; + } + + public function isFresh(ResourceInterface $resource, $timestamp) + { + /* @var SelfCheckingResourceInterface $resource */ + return $resource->isFresh($timestamp); + } +} diff --git a/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php b/vendor/symfony/config/Resource/SelfCheckingResourceInterface.php new file mode 100644 index 0000000..b3260f2 --- /dev/null +++ b/vendor/symfony/config/Resource/SelfCheckingResourceInterface.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\Config\Resource; + +/** + * Interface for Resources that can check for freshness autonomously, + * without special support from external services. + * + * @author Matthias Pigulla + */ +interface SelfCheckingResourceInterface extends ResourceInterface +{ + /** + * Returns true if the resource has not been updated since the given timestamp. + * + * @param int $timestamp The last time the resource was loaded + * + * @return bool True if the resource has not been updated, false otherwise + */ + public function isFresh($timestamp); +} diff --git a/vendor/symfony/config/ResourceCheckerConfigCache.php b/vendor/symfony/config/ResourceCheckerConfigCache.php new file mode 100644 index 0000000..87e2c5d --- /dev/null +++ b/vendor/symfony/config/ResourceCheckerConfigCache.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\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * ResourceCheckerConfigCache uses instances of ResourceCheckerInterface + * to check whether cached data is still fresh. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCache implements ConfigCacheInterface +{ + /** + * @var string + */ + private $file; + + /** + * @var iterable|ResourceCheckerInterface[] + */ + private $resourceCheckers; + + /** + * @param string $file The absolute cache path + * @param iterable|ResourceCheckerInterface[] $resourceCheckers The ResourceCheckers to use for the freshness check + */ + public function __construct($file, $resourceCheckers = array()) + { + $this->file = $file; + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->file; + } + + /** + * Checks if the cache is still fresh. + * + * This implementation will make a decision solely based on the ResourceCheckers + * passed in the constructor. + * + * The first ResourceChecker that supports a given resource is considered authoritative. + * Resources with no matching ResourceChecker will silently be ignored and considered fresh. + * + * @return bool true if the cache is fresh, false otherwise + */ + public function isFresh() + { + if (!is_file($this->file)) { + return false; + } + + if ($this->resourceCheckers instanceof \Traversable && !$this->resourceCheckers instanceof \Countable) { + $this->resourceCheckers = iterator_to_array($this->resourceCheckers); + } + + if (!\count($this->resourceCheckers)) { + return true; // shortcut - if we don't have any checkers we don't need to bother with the meta file at all + } + + $metadata = $this->getMetaFile(); + + if (!is_file($metadata)) { + return false; + } + + $meta = $this->safelyUnserialize($metadata); + + if (false === $meta) { + return false; + } + + $time = filemtime($this->file); + + foreach ($meta as $resource) { + /* @var ResourceInterface $resource */ + foreach ($this->resourceCheckers as $checker) { + if (!$checker->supports($resource)) { + continue; // next checker + } + if ($checker->isFresh($resource, $time)) { + break; // no need to further check this resource + } + + return false; // cache is stale + } + // no suitable checker found, ignore this resource + } + + return true; + } + + /** + * Writes cache. + * + * @param string $content The content to write in the cache + * @param ResourceInterface[] $metadata An array of metadata + * + * @throws \RuntimeException When cache file can't be written + */ + public function write($content, array $metadata = null) + { + $mode = 0666; + $umask = umask(); + $filesystem = new Filesystem(); + $filesystem->dumpFile($this->file, $content); + try { + $filesystem->chmod($this->file, $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + + if (null !== $metadata) { + $filesystem->dumpFile($this->getMetaFile(), serialize($metadata)); + try { + $filesystem->chmod($this->getMetaFile(), $mode, $umask); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + + if (\function_exists('opcache_invalidate') && ini_get('opcache.enable')) { + @opcache_invalidate($this->file, true); + } + } + + /** + * Gets the meta file path. + * + * @return string The meta file path + */ + private function getMetaFile() + { + return $this->file.'.meta'; + } + + private function safelyUnserialize($file) + { + $e = null; + $meta = false; + $signalingException = new \UnexpectedValueException(); + $prevUnserializeHandler = ini_set('unserialize_callback_func', ''); + $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$prevErrorHandler, $signalingException) { + if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) { + throw $signalingException; + } + + return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; + }); + + try { + $meta = unserialize(file_get_contents($file)); + } catch (\Error $e) { + } catch (\Exception $e) { + } + restore_error_handler(); + ini_set('unserialize_callback_func', $prevUnserializeHandler); + if (null !== $e && $e !== $signalingException) { + throw $e; + } + + return $meta; + } +} diff --git a/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php b/vendor/symfony/config/ResourceCheckerConfigCacheFactory.php new file mode 100644 index 0000000..2e27eab --- /dev/null +++ b/vendor/symfony/config/ResourceCheckerConfigCacheFactory.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\Config; + +/** + * A ConfigCacheFactory implementation that validates the + * cache with an arbitrary set of ResourceCheckers. + * + * @author Matthias Pigulla + */ +class ResourceCheckerConfigCacheFactory implements ConfigCacheFactoryInterface +{ + private $resourceCheckers = array(); + + /** + * @param iterable|ResourceCheckerInterface[] $resourceCheckers + */ + public function __construct($resourceCheckers = array()) + { + $this->resourceCheckers = $resourceCheckers; + } + + /** + * {@inheritdoc} + */ + public function cache($file, $callback) + { + if (!\is_callable($callback)) { + throw new \InvalidArgumentException(sprintf('Invalid type for callback argument. Expected callable, but got "%s".', \gettype($callback))); + } + + $cache = new ResourceCheckerConfigCache($file, $this->resourceCheckers); + if (!$cache->isFresh()) { + \call_user_func($callback, $cache); + } + + return $cache; + } +} diff --git a/vendor/symfony/config/ResourceCheckerInterface.php b/vendor/symfony/config/ResourceCheckerInterface.php new file mode 100644 index 0000000..612d777 --- /dev/null +++ b/vendor/symfony/config/ResourceCheckerInterface.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\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Interface for ResourceCheckers. + * + * When a ResourceCheckerConfigCache instance is checked for freshness, all its associated + * metadata resources are passed to ResourceCheckers. The ResourceCheckers + * can then inspect the resources and decide whether the cache can be considered + * fresh or not. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +interface ResourceCheckerInterface +{ + /** + * Queries the ResourceChecker whether it can validate a given + * resource or not. + * + * @param ResourceInterface $metadata The resource to be checked for freshness + * + * @return bool True if the ResourceChecker can handle this resource type, false if not + */ + public function supports(ResourceInterface $metadata); + + /** + * Validates the resource. + * + * @param ResourceInterface $resource The resource to be validated + * @param int $timestamp The timestamp at which the cache associated with this resource was created + * + * @return bool True if the resource has not changed since the given timestamp, false otherwise + */ + public function isFresh(ResourceInterface $resource, $timestamp); +} diff --git a/vendor/symfony/config/Tests/ConfigCacheFactoryTest.php b/vendor/symfony/config/Tests/ConfigCacheFactoryTest.php new file mode 100644 index 0000000..24e3224 --- /dev/null +++ b/vendor/symfony/config/Tests/ConfigCacheFactoryTest.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\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ConfigCacheFactory; + +class ConfigCacheFactoryTest extends TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid type for callback argument. Expected callable, but got "object". + */ + public function testCacheWithInvalidCallback() + { + $cacheFactory = new ConfigCacheFactory(true); + + $cacheFactory->cache('file', new \stdClass()); + } +} diff --git a/vendor/symfony/config/Tests/ConfigCacheTest.php b/vendor/symfony/config/Tests/ConfigCacheTest.php new file mode 100644 index 0000000..bf8131d --- /dev/null +++ b/vendor/symfony/config/Tests/ConfigCacheTest.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\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Config\Tests\Resource\ResourceStub; + +class ConfigCacheTest extends TestCase +{ + private $cacheFile = null; + + protected function setUp() + { + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + } + + protected function tearDown() + { + $files = array($this->cacheFile, $this->cacheFile.'.meta'); + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + /** + * @dataProvider debugModes + */ + public function testCacheIsNotValidIfNothingHasBeenCached($debug) + { + unlink($this->cacheFile); // remove tempnam() side effect + $cache = new ConfigCache($this->cacheFile, $debug); + + $this->assertFalse($cache->isFresh()); + } + + public function testIsAlwaysFreshInProduction() + { + $staleResource = new ResourceStub(); + $staleResource->setFresh(false); + + $cache = new ConfigCache($this->cacheFile, false); + $cache->write('', array($staleResource)); + + $this->assertTrue($cache->isFresh()); + } + + /** + * @dataProvider debugModes + */ + public function testIsFreshWhenNoResourceProvided($debug) + { + $cache = new ConfigCache($this->cacheFile, $debug); + $cache->write('', array()); + $this->assertTrue($cache->isFresh()); + } + + public function testFreshResourceInDebug() + { + $freshResource = new ResourceStub(); + $freshResource->setFresh(true); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('', array($freshResource)); + + $this->assertTrue($cache->isFresh()); + } + + public function testStaleResourceInDebug() + { + $staleResource = new ResourceStub(); + $staleResource->setFresh(false); + + $cache = new ConfigCache($this->cacheFile, true); + $cache->write('', array($staleResource)); + + $this->assertFalse($cache->isFresh()); + } + + public function debugModes() + { + return array( + array(true), + array(false), + ); + } +} diff --git a/vendor/symfony/config/Tests/Definition/ArrayNodeTest.php b/vendor/symfony/config/Tests/Definition/ArrayNodeTest.php new file mode 100644 index 0000000..089cb56 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/ArrayNodeTest.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\ScalarNode; + +class ArrayNodeTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionWhenFalseIsNotAllowed() + { + $node = new ArrayNode('root'); + $node->normalize(false); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage Unrecognized option "foo" under "root" + */ + public function testExceptionThrownOnUnrecognizedChild() + { + $node = new ArrayNode('root'); + $node->normalize(array('foo' => 'bar')); + } + + public function ignoreAndRemoveMatrixProvider() + { + $unrecognizedOptionException = new InvalidConfigurationException('Unrecognized option "foo" under "root"'); + + return array( + array(true, true, array(), 'no exception is thrown for an unrecognized child if the ignoreExtraKeys option is set to true'), + array(true, false, array('foo' => 'bar'), 'extra keys are not removed when ignoreExtraKeys second option is set to false'), + array(false, true, $unrecognizedOptionException), + array(false, false, $unrecognizedOptionException), + ); + } + + /** + * @dataProvider ignoreAndRemoveMatrixProvider + */ + public function testIgnoreAndRemoveBehaviors($ignore, $remove, $expected, $message = '') + { + if ($expected instanceof \Exception) { + if (method_exists($this, 'expectException')) { + $this->expectException(\get_class($expected)); + $this->expectExceptionMessage($expected->getMessage()); + } else { + $this->setExpectedException(\get_class($expected), $expected->getMessage()); + } + } + $node = new ArrayNode('root'); + $node->setIgnoreExtraKeys($ignore, $remove); + $result = $node->normalize(array('foo' => 'bar')); + $this->assertSame($expected, $result, $message); + } + + /** + * @dataProvider getPreNormalizationTests + */ + public function testPreNormalize($denormalized, $normalized) + { + $node = new ArrayNode('foo'); + + $r = new \ReflectionMethod($node, 'preNormalize'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($node, $denormalized)); + } + + public function getPreNormalizationTests() + { + return array( + array( + array('foo-bar' => 'foo'), + array('foo_bar' => 'foo'), + ), + array( + array('foo-bar_moo' => 'foo'), + array('foo-bar_moo' => 'foo'), + ), + array( + array('anything-with-dash-and-no-underscore' => 'first', 'no_dash' => 'second'), + array('anything_with_dash_and_no_underscore' => 'first', 'no_dash' => 'second'), + ), + array( + array('foo-bar' => null, 'foo_bar' => 'foo'), + array('foo-bar' => null, 'foo_bar' => 'foo'), + ), + ); + } + + /** + * @dataProvider getZeroNamedNodeExamplesData + */ + public function testNodeNameCanBeZero($denormalized, $normalized) + { + $zeroNode = new ArrayNode(0); + $zeroNode->addChild(new ScalarNode('name')); + $fiveNode = new ArrayNode(5); + $fiveNode->addChild(new ScalarNode(0)); + $fiveNode->addChild(new ScalarNode('new_key')); + $rootNode = new ArrayNode('root'); + $rootNode->addChild($zeroNode); + $rootNode->addChild($fiveNode); + $rootNode->addChild(new ScalarNode('string_key')); + $r = new \ReflectionMethod($rootNode, 'normalizeValue'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($rootNode, $denormalized)); + } + + public function getZeroNamedNodeExamplesData() + { + return array( + array( + array( + 0 => array( + 'name' => 'something', + ), + 5 => array( + 0 => 'this won\'t work too', + 'new_key' => 'some other value', + ), + 'string_key' => 'just value', + ), + array( + 0 => array( + 'name' => 'something', + ), + 5 => array( + 0 => 'this won\'t work too', + 'new_key' => 'some other value', + ), + 'string_key' => 'just value', + ), + ), + ); + } + + /** + * @dataProvider getPreNormalizedNormalizedOrderedData + */ + public function testChildrenOrderIsMaintainedOnNormalizeValue($prenormalized, $normalized) + { + $scalar1 = new ScalarNode('1'); + $scalar2 = new ScalarNode('2'); + $scalar3 = new ScalarNode('3'); + $node = new ArrayNode('foo'); + $node->addChild($scalar1); + $node->addChild($scalar3); + $node->addChild($scalar2); + + $r = new \ReflectionMethod($node, 'normalizeValue'); + $r->setAccessible(true); + + $this->assertSame($normalized, $r->invoke($node, $prenormalized)); + } + + public function getPreNormalizedNormalizedOrderedData() + { + return array( + array( + array('2' => 'two', '1' => 'one', '3' => 'three'), + array('2' => 'two', '1' => 'one', '3' => 'three'), + ), + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Child nodes must be named. + */ + public function testAddChildEmptyName() + { + $node = new ArrayNode('root'); + + $childNode = new ArrayNode(''); + $node->addChild($childNode); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage A child node named "foo" already exists. + */ + public function testAddChildNameAlreadyExists() + { + $node = new ArrayNode('root'); + + $childNode = new ArrayNode('foo'); + $node->addChild($childNode); + + $childNodeWithSameName = new ArrayNode('foo'); + $node->addChild($childNodeWithSameName); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage The node at path "foo" has no default value. + */ + public function testGetDefaultValueWithoutDefaultValue() + { + $node = new ArrayNode('foo'); + $node->getDefaultValue(); + } + + public function testSetDeprecated() + { + $childNode = new ArrayNode('foo'); + $childNode->setDeprecated('"%node%" is deprecated'); + + $this->assertTrue($childNode->isDeprecated()); + $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath())); + + $node = new ArrayNode('root'); + $node->addChild($childNode); + + $deprecationTriggered = false; + $deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) { + if (E_USER_DEPRECATED === $level) { + return $deprecationTriggered = true; + } + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; + }; + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize(array()); + restore_error_handler(); + + $this->assertFalse($deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set'); + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize(array('foo' => array())); + restore_error_handler(); + $this->assertTrue($deprecationTriggered, '->finalize() should trigger if the deprecated node is set'); + } +} diff --git a/vendor/symfony/config/Tests/Definition/BooleanNodeTest.php b/vendor/symfony/config/Tests/Definition/BooleanNodeTest.php new file mode 100644 index 0000000..ab1d316 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/BooleanNodeTest.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\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\BooleanNode; + +class BooleanNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new BooleanNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param bool $value + */ + public function testValidNonEmptyValues($value) + { + $node = new BooleanNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return array( + array(false), + array(true), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new BooleanNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(null), + array(''), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php new file mode 100644 index 0000000..ba059ff --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -0,0 +1,347 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\ScalarNodeDefinition; +use Symfony\Component\Config\Definition\Exception\InvalidDefinitionException; +use Symfony\Component\Config\Definition\Processor; + +class ArrayNodeDefinitionTest extends TestCase +{ + public function testAppendingSomeNode() + { + $parent = new ArrayNodeDefinition('root'); + $child = new ScalarNodeDefinition('child'); + + $parent + ->children() + ->scalarNode('foo')->end() + ->scalarNode('bar')->end() + ->end() + ->append($child); + + $this->assertCount(3, $this->getField($parent, 'children')); + $this->assertContains($child, $this->getField($parent, 'children')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @dataProvider providePrototypeNodeSpecificCalls + */ + public function testPrototypeNodeSpecificOption($method, $args) + { + $node = new ArrayNodeDefinition('root'); + + \call_user_func_array(array($node, $method), $args); + + $node->getNode(); + } + + public function providePrototypeNodeSpecificCalls() + { + return array( + array('defaultValue', array(array())), + array('addDefaultChildrenIfNoneSet', array()), + array('requiresAtLeastOneElement', array()), + array('useAttributeAsKey', array('foo')), + ); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + */ + public function testConcreteNodeSpecificOption() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultsIfNotSet() + ->prototype('array') + ; + $node->getNode(); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + */ + public function testPrototypeNodesCantHaveADefaultValueWhenUsingDefaultChildren() + { + $node = new ArrayNodeDefinition('root'); + $node + ->defaultValue(array()) + ->addDefaultChildrenIfNoneSet('foo') + ->prototype('array') + ; + $node->getNode(); + } + + public function testPrototypedArrayNodeDefaultWhenUsingDefaultChildren() + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet() + ->prototype('array') + ; + $tree = $node->getNode(); + $this->assertEquals(array(array()), $tree->getDefaultValue()); + } + + /** + * @dataProvider providePrototypedArrayNodeDefaults + */ + public function testPrototypedArrayNodeDefault($args, $shouldThrowWhenUsingAttrAsKey, $shouldThrowWhenNotUsingAttrAsKey, $defaults) + { + $node = new ArrayNodeDefinition('root'); + $node + ->addDefaultChildrenIfNoneSet($args) + ->prototype('array') + ; + + try { + $tree = $node->getNode(); + $this->assertFalse($shouldThrowWhenNotUsingAttrAsKey); + $this->assertEquals($defaults, $tree->getDefaultValue()); + } catch (InvalidDefinitionException $e) { + $this->assertTrue($shouldThrowWhenNotUsingAttrAsKey); + } + + $node = new ArrayNodeDefinition('root'); + $node + ->useAttributeAsKey('attr') + ->addDefaultChildrenIfNoneSet($args) + ->prototype('array') + ; + + try { + $tree = $node->getNode(); + $this->assertFalse($shouldThrowWhenUsingAttrAsKey); + $this->assertEquals($defaults, $tree->getDefaultValue()); + } catch (InvalidDefinitionException $e) { + $this->assertTrue($shouldThrowWhenUsingAttrAsKey); + } + } + + public function providePrototypedArrayNodeDefaults() + { + return array( + array(null, true, false, array(array())), + array(2, true, false, array(array(), array())), + array('2', false, true, array('2' => array())), + array('foo', false, true, array('foo' => array())), + array(array('foo'), false, true, array('foo' => array())), + array(array('foo', 'bar'), false, true, array('foo' => array(), 'bar' => array())), + ); + } + + public function testNestedPrototypedArrayNodes() + { + $nodeDefinition = new ArrayNodeDefinition('root'); + $nodeDefinition + ->addDefaultChildrenIfNoneSet() + ->prototype('array') + ->prototype('array') + ; + $node = $nodeDefinition->getNode(); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node); + $this->assertInstanceOf('Symfony\Component\Config\Definition\PrototypedArrayNode', $node->getPrototype()); + } + + public function testEnabledNodeDefaults() + { + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals(array('enabled' => false, 'foo' => 'bar'), $node->getNode()->getDefaultValue()); + } + + /** + * @dataProvider getEnableableNodeFixtures + */ + public function testTrueEnableEnabledNode($expected, $config, $message) + { + $processor = new Processor(); + $node = new ArrayNodeDefinition('root'); + $node + ->canBeEnabled() + ->children() + ->scalarNode('foo')->defaultValue('bar')->end() + ; + + $this->assertEquals( + $expected, + $processor->process($node->getNode(), $config), + $message + ); + } + + public function testCanBeDisabled() + { + $node = new ArrayNodeDefinition('root'); + $node->canBeDisabled(); + + $this->assertTrue($this->getField($node, 'addDefaults')); + $this->assertEquals(array('enabled' => false), $this->getField($node, 'falseEquivalent')); + $this->assertEquals(array('enabled' => true), $this->getField($node, 'trueEquivalent')); + $this->assertEquals(array('enabled' => true), $this->getField($node, 'nullEquivalent')); + + $nodeChildren = $this->getField($node, 'children'); + $this->assertArrayHasKey('enabled', $nodeChildren); + + $enabledNode = $nodeChildren['enabled']; + $this->assertTrue($this->getField($enabledNode, 'default')); + $this->assertTrue($this->getField($enabledNode, 'defaultValue')); + } + + public function testIgnoreExtraKeys() + { + $node = new ArrayNodeDefinition('root'); + + $this->assertFalse($this->getField($node, 'ignoreExtraKeys')); + + $result = $node->ignoreExtraKeys(); + + $this->assertEquals($node, $result); + $this->assertTrue($this->getField($node, 'ignoreExtraKeys')); + } + + public function testNormalizeKeys() + { + $node = new ArrayNodeDefinition('root'); + + $this->assertTrue($this->getField($node, 'normalizeKeys')); + + $result = $node->normalizeKeys(false); + + $this->assertEquals($node, $result); + $this->assertFalse($this->getField($node, 'normalizeKeys')); + } + + public function testPrototypeVariable() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('variable'), $node->variablePrototype()); + } + + public function testPrototypeScalar() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('scalar'), $node->scalarPrototype()); + } + + public function testPrototypeBoolean() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('boolean'), $node->booleanPrototype()); + } + + public function testPrototypeInteger() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('integer'), $node->integerPrototype()); + } + + public function testPrototypeFloat() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('float'), $node->floatPrototype()); + } + + public function testPrototypeArray() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('array'), $node->arrayPrototype()); + } + + public function testPrototypeEnum() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('enum'), $node->enumPrototype()); + } + + public function getEnableableNodeFixtures() + { + return array( + array(array('enabled' => true, 'foo' => 'bar'), array(true), 'true enables an enableable node'), + array(array('enabled' => true, 'foo' => 'bar'), array(null), 'null enables an enableable node'), + array(array('enabled' => true, 'foo' => 'bar'), array(array('enabled' => true)), 'An enableable node can be enabled'), + array(array('enabled' => true, 'foo' => 'baz'), array(array('foo' => 'baz')), 'any configuration enables an enableable node'), + array(array('enabled' => false, 'foo' => 'baz'), array(array('foo' => 'baz', 'enabled' => false)), 'An enableable node can be disabled'), + array(array('enabled' => false, 'foo' => 'bar'), array(false), 'false disables an enableable node'), + ); + } + + public function testRequiresAtLeastOneElement() + { + $node = new ArrayNodeDefinition('root'); + $node + ->requiresAtLeastOneElement() + ->integerPrototype(); + + $node->getNode()->finalize(array(1)); + + $this->addToAssertionCount(1); + } + + /** + * @group legacy + * @expectedDeprecation Using Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition::cannotBeEmpty() at path "root" has no effect, consider requiresAtLeastOneElement() instead. In 4.0 both methods will behave the same. + */ + public function testCannotBeEmpty() + { + $node = new ArrayNodeDefinition('root'); + $node + ->cannotBeEmpty() + ->integerPrototype(); + + $node->getNode()->finalize(array()); + } + + public function testSetDeprecated() + { + $node = new ArrayNodeDefinition('root'); + $node + ->children() + ->arrayNode('foo')->setDeprecated('The "%path%" node is deprecated.')->end() + ->end() + ; + $deprecatedNode = $node->getNode()->getChildren()['foo']; + + $this->assertTrue($deprecatedNode->isDeprecated()); + $this->assertSame('The "root.foo" node is deprecated.', $deprecatedNode->getDeprecationMessage($deprecatedNode->getName(), $deprecatedNode->getPath())); + } + + /** + * @group legacy + * @expectedDeprecation ->cannotBeEmpty() is not applicable to concrete nodes at path "root". In 4.0 it will throw an exception. + */ + public function testCannotBeEmptyOnConcreteNode() + { + $node = new ArrayNodeDefinition('root'); + $node->cannotBeEmpty(); + + $node->getNode()->finalize(array()); + } + + protected function getField($object, $field) + { + $reflection = new \ReflectionProperty($object, $field); + $reflection->setAccessible(true); + + return $reflection->getValue($object); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php b/vendor/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.php new file mode 100644 index 0000000..c0d347f --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/BooleanNodeDefinitionTest.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\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition; + +class BooleanNodeDefinitionTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to BooleanNodeDefinition. + */ + public function testCannotBeEmptyThrowsAnException() + { + $def = new BooleanNodeDefinition('foo'); + $def->cannotBeEmpty(); + } + + public function testSetDeprecated() + { + $def = new BooleanNodeDefinition('foo'); + $def->setDeprecated('The "%path%" node is deprecated.'); + + $node = $def->getNode(); + + $this->assertTrue($node->isDeprecated()); + $this->assertSame('The "foo" node is deprecated.', $node->getDeprecationMessage($node->getName(), $node->getPath())); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php b/vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.php new file mode 100644 index 0000000..9c0aa0e --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/EnumNodeDefinitionTest.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\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition; + +class EnumNodeDefinitionTest extends TestCase +{ + public function testWithOneValue() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array('foo')); + + $node = $def->getNode(); + $this->assertEquals(array('foo'), $node->getValues()); + } + + public function testWithOneDistinctValue() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array('foo', 'foo')); + + $node = $def->getNode(); + $this->assertEquals(array('foo'), $node->getValues()); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must call ->values() on enum nodes. + */ + public function testNoValuesPassed() + { + $def = new EnumNodeDefinition('foo'); + $def->getNode(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage ->values() must be called with at least one value. + */ + public function testWithNoValues() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array()); + } + + public function testGetNode() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array('foo', 'bar')); + + $node = $def->getNode(); + $this->assertEquals(array('foo', 'bar'), $node->getValues()); + } + + public function testSetDeprecated() + { + $def = new EnumNodeDefinition('foo'); + $def->values(array('foo', 'bar')); + $def->setDeprecated('The "%path%" node is deprecated.'); + + $node = $def->getNode(); + + $this->assertTrue($node->isDeprecated()); + $this->assertSame('The "foo" node is deprecated.', $def->getNode()->getDeprecationMessage($node->getName(), $node->getPath())); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php b/vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php new file mode 100644 index 0000000..99a1041 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/ExprBuilderTest.php @@ -0,0 +1,270 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class ExprBuilderTest extends TestCase +{ + public function testAlwaysExpression() + { + $test = $this->getTestBuilder() + ->always($this->returnClosure('new_value')) + ->end(); + + $this->assertFinalizedValueIs('new_value', $test); + } + + public function testIfTrueExpression() + { + $test = $this->getTestBuilder() + ->ifTrue() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => true)); + + $test = $this->getTestBuilder() + ->ifTrue(function ($v) { return true; }) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifTrue(function ($v) { return false; }) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfStringExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifString() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs(45, $test, array('key' => 45)); + } + + public function testIfNullExpression() + { + $test = $this->getTestBuilder() + ->ifNull() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => null)); + + $test = $this->getTestBuilder() + ->ifNull() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfEmptyExpression() + { + $test = $this->getTestBuilder() + ->ifEmpty() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => array())); + + $test = $this->getTestBuilder() + ->ifEmpty() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfArrayExpression() + { + $test = $this->getTestBuilder() + ->ifArray() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test, array('key' => array())); + + $test = $this->getTestBuilder() + ->ifArray() + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfInArrayExpression() + { + $test = $this->getTestBuilder() + ->ifInArray(array('foo', 'bar', 'value')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifInArray(array('foo', 'bar')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('value', $test); + } + + public function testIfNotInArrayExpression() + { + $test = $this->getTestBuilder() + ->ifNotInArray(array('foo', 'bar')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + + $test = $this->getTestBuilder() + ->ifNotInArray(array('foo', 'bar', 'value_from_config')) + ->then($this->returnClosure('new_value')) + ->end(); + $this->assertFinalizedValueIs('new_value', $test); + } + + public function testThenEmptyArrayExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenEmptyArray() + ->end(); + $this->assertFinalizedValueIs(array(), $test); + } + + /** + * @dataProvider castToArrayValues + */ + public function testcastToArrayExpression($configValue, $expectedValue) + { + $test = $this->getTestBuilder() + ->castToArray() + ->end(); + $this->assertFinalizedValueIs($expectedValue, $test, array('key' => $configValue)); + } + + public function castToArrayValues() + { + yield array('value', array('value')); + yield array(-3.14, array(-3.14)); + yield array(null, array(null)); + yield array(array('value'), array('value')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testThenInvalid() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenInvalid('Invalid value') + ->end(); + $this->finalizeTestBuilder($test); + } + + public function testThenUnsetExpression() + { + $test = $this->getTestBuilder() + ->ifString() + ->thenUnset() + ->end(); + $this->assertEquals(array(), $this->finalizeTestBuilder($test)); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must specify an if part. + */ + public function testEndIfPartNotSpecified() + { + $this->getTestBuilder()->end(); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must specify a then part. + */ + public function testEndThenPartNotSpecified() + { + $builder = $this->getTestBuilder(); + $builder->ifPart = 'test'; + $builder->end(); + } + + /** + * Create a test treebuilder with a variable node, and init the validation. + * + * @return TreeBuilder + */ + protected function getTestBuilder() + { + $builder = new TreeBuilder(); + + return $builder + ->root('test') + ->children() + ->variableNode('key') + ->validate() + ; + } + + /** + * Close the validation process and finalize with the given config. + * + * @param TreeBuilder $testBuilder The tree builder to finalize + * @param array $config The config you want to use for the finalization, if nothing provided + * a simple array('key'=>'value') will be used + * + * @return array The finalized config values + */ + protected function finalizeTestBuilder($testBuilder, $config = null) + { + return $testBuilder + ->end() + ->end() + ->end() + ->buildTree() + ->finalize(null === $config ? array('key' => 'value') : $config) + ; + } + + /** + * Return a closure that will return the given value. + * + * @param mixed $val The value that the closure must return + * + * @return \Closure + */ + protected function returnClosure($val) + { + return function ($v) use ($val) { + return $val; + }; + } + + /** + * Assert that the given test builder, will return the given value. + * + * @param mixed $value The value to test + * @param TreeBuilder $treeBuilder The tree builder to finalize + * @param mixed $config The config values that new to be finalized + */ + protected function assertFinalizedValueIs($value, $treeBuilder, $config = null) + { + $this->assertEquals(array('key' => $value), $this->finalizeTestBuilder($treeBuilder, $config)); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php b/vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.php new file mode 100644 index 0000000..cd77dd7 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/NodeBuilderTest.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\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; +use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; + +class NodeBuilderTest extends TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testThrowsAnExceptionWhenTryingToCreateANonRegisteredNodeType() + { + $builder = new BaseNodeBuilder(); + $builder->node('', 'foobar'); + } + + /** + * @expectedException \RuntimeException + */ + public function testThrowsAnExceptionWhenTheNodeClassIsNotFound() + { + $builder = new BaseNodeBuilder(); + $builder + ->setNodeClass('noclasstype', '\\foo\\bar\\noclass') + ->node('', 'noclasstype'); + } + + public function testAddingANewNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('newtype', $class) + ->node('', 'newtype'); + + $this->assertInstanceOf($class, $node); + } + + public function testOverridingAnExistingNodeType() + { + $class = __NAMESPACE__.'\\SomeNodeDefinition'; + + $builder = new BaseNodeBuilder(); + $node = $builder + ->setNodeClass('variable', $class) + ->node('', 'variable'); + + $this->assertInstanceOf($class, $node); + } + + public function testNodeTypesAreNotCaseSensitive() + { + $builder = new BaseNodeBuilder(); + + $node1 = $builder->node('', 'VaRiAbLe'); + $node2 = $builder->node('', 'variable'); + + $this->assertInstanceOf(\get_class($node1), $node2); + + $builder->setNodeClass('CuStOm', __NAMESPACE__.'\\SomeNodeDefinition'); + + $node1 = $builder->node('', 'CUSTOM'); + $node2 = $builder->node('', 'custom'); + + $this->assertInstanceOf(\get_class($node1), $node2); + } + + public function testNumericNodeCreation() + { + $builder = new BaseNodeBuilder(); + + $node = $builder->integerNode('foo')->min(3)->max(5); + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition', $node); + + $node = $builder->floatNode('bar')->min(3.0)->max(5.0); + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\FloatNodeDefinition', $node); + } +} + +class SomeNodeDefinition extends BaseVariableNodeDefinition +{ +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php b/vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.php new file mode 100644 index 0000000..3134250 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/NumericNodeDefinitionTest.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\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\FloatNodeDefinition; +use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition; +use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition as NumericNodeDefinition; + +class NumericNodeDefinitionTest extends TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You cannot define a min(4) as you already have a max(3) + */ + public function testIncoherentMinAssertion() + { + $def = new NumericNodeDefinition('foo'); + $def->max(3)->min(4); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You cannot define a max(2) as you already have a min(3) + */ + public function testIncoherentMaxAssertion() + { + $node = new NumericNodeDefinition('foo'); + $node->min(3)->max(2); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4 is too small for path "foo". Should be greater than or equal to 5 + */ + public function testIntegerMinAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $def->min(5)->getNode()->finalize(4); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4 is too big for path "foo". Should be less than or equal to 3 + */ + public function testIntegerMaxAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $def->max(3)->getNode()->finalize(4); + } + + public function testIntegerValidMinMaxAssertion() + { + $def = new IntegerNodeDefinition('foo'); + $node = $def->min(3)->max(7)->getNode(); + $this->assertEquals(4, $node->finalize(4)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 400 is too small for path "foo". Should be greater than or equal to 500 + */ + public function testFloatMinAssertion() + { + $def = new FloatNodeDefinition('foo'); + $def->min(5E2)->getNode()->finalize(4e2); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value 4.3 is too big for path "foo". Should be less than or equal to 0.3 + */ + public function testFloatMaxAssertion() + { + $def = new FloatNodeDefinition('foo'); + $def->max(0.3)->getNode()->finalize(4.3); + } + + public function testFloatValidMinMaxAssertion() + { + $def = new FloatNodeDefinition('foo'); + $node = $def->min(3.0)->max(7e2)->getNode(); + $this->assertEquals(4.5, $node->finalize(4.5)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidDefinitionException + * @expectedExceptionMessage ->cannotBeEmpty() is not applicable to NumericNodeDefinition. + */ + public function testCannotBeEmptyThrowsAnException() + { + $def = new NumericNodeDefinition('foo'); + $def->cannotBeEmpty(); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php b/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.php new file mode 100644 index 0000000..c425f0e --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Builder/TreeBuilderTest.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\Config\Tests\Definition\Builder; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder as CustomNodeBuilder; + +class TreeBuilderTest extends TestCase +{ + public function testUsingACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('custom', 'array', new CustomNodeBuilder()); + + $nodeBuilder = $root->children(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder', $nodeBuilder); + + $nodeBuilder = $nodeBuilder->arrayNode('deeper')->children(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\NodeBuilder', $nodeBuilder); + } + + public function testOverrideABuiltInNodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->variableNode('variable'); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\VariableNodeDefinition', $definition); + } + + public function testAddANodeType() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->barNode('variable'); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\Builder\BarNodeDefinition', $definition); + } + + public function testCreateABuiltInNodeTypeWithACustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('builtin', 'array', new CustomNodeBuilder()); + + $definition = $root->children()->booleanNode('boolean'); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition', $definition); + } + + public function testPrototypedArrayNodeUseTheCustomNodeBuilder() + { + $builder = new TreeBuilder(); + $root = $builder->root('override', 'array', new CustomNodeBuilder()); + + $root->prototype('bar')->end(); + + $this->assertInstanceOf('Symfony\Component\Config\Tests\Fixtures\BarNode', $root->getNode(true)->getPrototype()); + } + + public function testAnExtendedNodeBuilderGetsPropagatedToTheChildren() + { + $builder = new TreeBuilder(); + + $builder->root('propagation') + ->children() + ->setNodeClass('extended', 'Symfony\Component\Config\Definition\Builder\BooleanNodeDefinition') + ->node('foo', 'extended')->end() + ->arrayNode('child') + ->children() + ->node('foo', 'extended') + ->end() + ->end() + ->end() + ->end(); + + $node = $builder->buildTree(); + $children = $node->getChildren(); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\BooleanNode', $children['foo']); + + $childChildren = $children['child']->getChildren(); + + $this->assertInstanceOf('Symfony\Component\Config\Definition\BooleanNode', $childChildren['foo']); + } + + public function testDefinitionInfoGetsTransferredToNode() + { + $builder = new TreeBuilder(); + + $builder->root('test')->info('root info') + ->children() + ->node('child', 'variable')->info('child info')->defaultValue('default') + ->end() + ->end(); + + $tree = $builder->buildTree(); + $children = $tree->getChildren(); + + $this->assertEquals('root info', $tree->getInfo()); + $this->assertEquals('child info', $children['child']->getInfo()); + } + + public function testDefinitionExampleGetsTransferredToNode() + { + $builder = new TreeBuilder(); + + $builder->root('test') + ->example(array('key' => 'value')) + ->children() + ->node('child', 'variable')->info('child info')->defaultValue('default')->example('example') + ->end() + ->end(); + + $tree = $builder->buildTree(); + $children = $tree->getChildren(); + + $this->assertInternalType('array', $tree->getExample()); + $this->assertEquals('example', $children['child']->getExample()); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.php new file mode 100644 index 0000000..5bc961b --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Dumper/XmlReferenceDumperTest.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\Config\Tests\Definition\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class XmlReferenceDumperTest extends TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new XmlReferenceDumper(); + $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); + } + + public function testNamespaceDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new XmlReferenceDumper(); + $this->assertEquals(str_replace('http://example.org/schema/dic/acme_root', 'http://symfony.com/schema/dic/symfony', $this->getConfigurationAsString()), $dumper->dump($configuration, 'http://symfony.com/schema/dic/symfony')); + } + + private function getConfigurationAsString() + { + return str_replace("\n", PHP_EOL, <<<'EOL' + + + + + + + + + + + + + + scalar value + + + scalar value + + + + + + + + + + + + + + + + + + + + + + + + +EOL + ); + } +} diff --git a/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php new file mode 100644 index 0000000..bf4db95 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/Dumper/YamlReferenceDumperTest.php @@ -0,0 +1,143 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; +use Symfony\Component\Config\Tests\Fixtures\Configuration\ExampleConfiguration; + +class YamlReferenceDumperTest extends TestCase +{ + public function testDumper() + { + $configuration = new ExampleConfiguration(); + + $dumper = new YamlReferenceDumper(); + + $this->assertEquals($this->getConfigurationAsString(), $dumper->dump($configuration)); + } + + public function provideDumpAtPath() + { + return array( + 'Regular node' => array('scalar_true', << array('array', << array('array.child2', << array('cms_pages.page', << array('cms_pages.page.locale', <<assertSame(trim($expected), trim($dumper->dumpAtPath($configuration, $path))); + } + + private function getConfigurationAsString() + { + return <<<'EOL' +acme_root: + boolean: true + scalar_empty: ~ + scalar_null: null + scalar_true: true + scalar_false: false + scalar_default: default + scalar_array_empty: [] + scalar_array_defaults: + + # Defaults: + - elem1 + - elem2 + scalar_required: ~ # Required + scalar_deprecated: ~ # Deprecated (The child node "scalar_deprecated" at path "acme_root" is deprecated.) + scalar_deprecated_with_message: ~ # Deprecated (Deprecation custom message for "scalar_deprecated_with_message" at "acme_root") + node_with_a_looong_name: ~ + enum_with_default: this # One of "this"; "that" + enum: ~ # One of "this"; "that" + + # some info + array: + child1: ~ + child2: ~ + + # this is a long + # multi-line info text + # which should be indented + child3: ~ # Example: example setting + scalar_prototyped: [] + parameters: + + # Prototype: Parameter name + name: ~ + connections: + + # Prototype + - + user: ~ + pass: ~ + cms_pages: + + # Prototype + page: + + # Prototype + locale: + title: ~ # Required + path: ~ # Required + pipou: + + # Prototype + name: [] + +EOL; + } +} diff --git a/vendor/symfony/config/Tests/Definition/EnumNodeTest.php b/vendor/symfony/config/Tests/Definition/EnumNodeTest.php new file mode 100644 index 0000000..e3e7ef0 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/EnumNodeTest.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\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\EnumNode; + +class EnumNodeTest extends TestCase +{ + public function testFinalizeValue() + { + $node = new EnumNode('foo', null, array('foo', 'bar')); + $this->assertSame('foo', $node->finalize('foo')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage $values must contain at least one element. + */ + public function testConstructionWithNoValues() + { + new EnumNode('foo', null, array()); + } + + public function testConstructionWithOneValue() + { + $node = new EnumNode('foo', null, array('foo')); + $this->assertSame('foo', $node->finalize('foo')); + } + + public function testConstructionWithOneDistinctValue() + { + $node = new EnumNode('foo', null, array('foo', 'foo')); + $this->assertSame('foo', $node->finalize('foo')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The value "foobar" is not allowed for path "foo". Permissible values: "foo", "bar" + */ + public function testFinalizeWithInvalidValue() + { + $node = new EnumNode('foo', null, array('foo', 'bar')); + $node->finalize('foobar'); + } +} diff --git a/vendor/symfony/config/Tests/Definition/FinalizationTest.php b/vendor/symfony/config/Tests/Definition/FinalizationTest.php new file mode 100644 index 0000000..d19fbd6 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/FinalizationTest.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\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\NodeInterface; +use Symfony\Component\Config\Definition\Processor; + +class FinalizationTest extends TestCase +{ + public function testUnsetKeyWithDeepHierarchy() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->children() + ->node('level1', 'array') + ->canBeUnset() + ->children() + ->node('level2', 'array') + ->canBeUnset() + ->children() + ->node('somevalue', 'scalar')->end() + ->node('anothervalue', 'scalar')->end() + ->end() + ->end() + ->node('level1_scalar', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'level1' => array( + 'level2' => array( + 'somevalue' => 'foo', + 'anothervalue' => 'bar', + ), + 'level1_scalar' => 'foo', + ), + ); + + $b = array( + 'level1' => array( + 'level2' => false, + ), + ); + + $this->assertEquals(array( + 'level1' => array( + 'level1_scalar' => 'foo', + ), + ), $this->process($tree, array($a, $b))); + } + + protected function process(NodeInterface $tree, array $configs) + { + $processor = new Processor(); + + return $processor->process($tree, $configs); + } +} diff --git a/vendor/symfony/config/Tests/Definition/FloatNodeTest.php b/vendor/symfony/config/Tests/Definition/FloatNodeTest.php new file mode 100644 index 0000000..b7ec12f --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/FloatNodeTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\FloatNode; + +class FloatNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new FloatNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param int $value + */ + public function testValidNonEmptyValues($value) + { + $node = new FloatNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return array( + array(1798.0), + array(-678.987), + array(12.56E45), + array(0.0), + // Integer are accepted too, they will be cast + array(17), + array(-10), + array(0), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new FloatNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(null), + array(''), + array('foo'), + array(true), + array(false), + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} diff --git a/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php b/vendor/symfony/config/Tests/Definition/IntegerNodeTest.php new file mode 100644 index 0000000..55e8a13 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/IntegerNodeTest.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\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\IntegerNode; + +class IntegerNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new IntegerNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @dataProvider getValidValues + * + * @param int $value + */ + public function testValidNonEmptyValues($value) + { + $node = new IntegerNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidValues() + { + return array( + array(1798), + array(-678), + array(0), + ); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new IntegerNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(null), + array(''), + array('foo'), + array(true), + array(false), + array(0.0), + array(0.1), + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } +} diff --git a/vendor/symfony/config/Tests/Definition/MergeTest.php b/vendor/symfony/config/Tests/Definition/MergeTest.php new file mode 100644 index 0000000..e539e25 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/MergeTest.php @@ -0,0 +1,196 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class MergeTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException + */ + public function testForbiddenOverwrite() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('foo', 'scalar') + ->cannotBeOverwritten() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'foo' => 'bar', + ); + + $b = array( + 'foo' => 'moo', + ); + + $tree->merge($a, $b); + } + + public function testUnsetKey() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->node('unsettable', 'array') + ->canBeUnset() + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->node('unsetted', 'array') + ->canBeUnset() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'foo' => 'bar', + 'unsettable' => array( + 'foo' => 'a', + 'bar' => 'b', + ), + 'unsetted' => false, + ); + + $b = array( + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => array('a', 'b'), + ); + + $this->assertEquals(array( + 'foo' => 'moo', + 'bar' => 'b', + 'unsettable' => false, + 'unsetted' => array('a', 'b'), + ), $tree->merge($a, $b)); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testDoesNotAllowNewKeysInSubsequentConfigs() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('config', 'array') + ->children() + ->node('test', 'array') + ->disallowNewKeysInSubsequentConfigs() + ->useAttributeAsKey('key') + ->prototype('array') + ->children() + ->node('value', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree(); + + $a = array( + 'test' => array( + 'a' => array('value' => 'foo'), + ), + ); + + $b = array( + 'test' => array( + 'b' => array('value' => 'foo'), + ), + ); + + $tree->merge($a, $b); + } + + public function testPerformsNoDeepMerging() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->children() + ->node('no_deep_merging', 'array') + ->performNoDeepMerging() + ->children() + ->node('foo', 'scalar')->end() + ->node('bar', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'no_deep_merging' => array( + 'foo' => 'a', + 'bar' => 'b', + ), + ); + + $b = array( + 'no_deep_merging' => array( + 'c' => 'd', + ), + ); + + $this->assertEquals(array( + 'no_deep_merging' => array( + 'c' => 'd', + ), + ), $tree->merge($a, $b)); + } + + public function testPrototypeWithoutAKeyAttribute() + { + $tb = new TreeBuilder(); + + $tree = $tb + ->root('config', 'array') + ->children() + ->arrayNode('append_elements') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $a = array( + 'append_elements' => array('a', 'b'), + ); + + $b = array( + 'append_elements' => array('c', 'd'), + ); + + $this->assertEquals(array('append_elements' => array('a', 'b', 'c', 'd')), $tree->merge($a, $b)); + } +} diff --git a/vendor/symfony/config/Tests/Definition/NormalizationTest.php b/vendor/symfony/config/Tests/Definition/NormalizationTest.php new file mode 100644 index 0000000..3273c78 --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/NormalizationTest.php @@ -0,0 +1,230 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\NodeInterface; + +class NormalizationTest extends TestCase +{ + /** + * @dataProvider getEncoderTests + */ + public function testNormalizeEncoders($denormalized) + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root_name', 'array') + ->fixXmlConfig('encoder') + ->children() + ->node('encoders', 'array') + ->useAttributeAsKey('class') + ->prototype('array') + ->beforeNormalization()->ifString()->then(function ($v) { return array('algorithm' => $v); })->end() + ->children() + ->node('algorithm', 'scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $normalized = array( + 'encoders' => array( + 'foo' => array('algorithm' => 'plaintext'), + ), + ); + + $this->assertNormalized($tree, $denormalized, $normalized); + } + + public function getEncoderTests() + { + $configs = array(); + + // XML + $configs[] = array( + 'encoder' => array( + array('class' => 'foo', 'algorithm' => 'plaintext'), + ), + ); + + // XML when only one element of this type + $configs[] = array( + 'encoder' => array('class' => 'foo', 'algorithm' => 'plaintext'), + ); + + // YAML/PHP + $configs[] = array( + 'encoders' => array( + array('class' => 'foo', 'algorithm' => 'plaintext'), + ), + ); + + // YAML/PHP + $configs[] = array( + 'encoders' => array( + 'foo' => 'plaintext', + ), + ); + + // YAML/PHP + $configs[] = array( + 'encoders' => array( + 'foo' => array('algorithm' => 'plaintext'), + ), + ); + + return array_map(function ($v) { + return array($v); + }, $configs); + } + + /** + * @dataProvider getAnonymousKeysTests + */ + public function testAnonymousKeysArray($denormalized) + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('logout', 'array') + ->fixXmlConfig('handler') + ->children() + ->node('handlers', 'array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $normalized = array('logout' => array('handlers' => array('a', 'b', 'c'))); + + $this->assertNormalized($tree, $denormalized, $normalized); + } + + public function getAnonymousKeysTests() + { + $configs = array(); + + $configs[] = array( + 'logout' => array( + 'handlers' => array('a', 'b', 'c'), + ), + ); + + $configs[] = array( + 'logout' => array( + 'handler' => array('a', 'b', 'c'), + ), + ); + + return array_map(function ($v) { return array($v); }, $configs); + } + + /** + * @dataProvider getNumericKeysTests + */ + public function testNumericKeysAsAttributes($denormalized) + { + $normalized = array( + 'thing' => array(42 => array('foo', 'bar'), 1337 => array('baz', 'qux')), + ); + + $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, $normalized); + } + + public function getNumericKeysTests() + { + $configs = array(); + + $configs[] = array( + 'thing' => array( + 42 => array('foo', 'bar'), 1337 => array('baz', 'qux'), + ), + ); + + $configs[] = array( + 'thing' => array( + array('foo', 'bar', 'id' => 42), array('baz', 'qux', 'id' => 1337), + ), + ); + + return array_map(function ($v) { return array($v); }, $configs); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage The attribute "id" must be set for path "root.thing". + */ + public function testNonAssociativeArrayThrowsExceptionIfAttributeNotSet() + { + $denormalized = array( + 'thing' => array( + array('foo', 'bar'), array('baz', 'qux'), + ), + ); + + $this->assertNormalized($this->getNumericKeysTestTree(), $denormalized, array()); + } + + public function testAssociativeArrayPreserveKeys() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->prototype('array') + ->children() + ->node('foo', 'scalar')->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + $data = array('first' => array('foo' => 'bar')); + + $this->assertNormalized($tree, $data, $data); + } + + public static function assertNormalized(NodeInterface $tree, $denormalized, $normalized) + { + self::assertSame($normalized, $tree->normalize($denormalized)); + } + + private function getNumericKeysTestTree() + { + $tb = new TreeBuilder(); + $tree = $tb + ->root('root', 'array') + ->children() + ->node('thing', 'array') + ->useAttributeAsKey('id') + ->prototype('array') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->buildTree() + ; + + return $tree; + } +} diff --git a/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php b/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php new file mode 100644 index 0000000..731d7af --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/PrototypedArrayNodeTest.php @@ -0,0 +1,342 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\PrototypedArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; +use Symfony\Component\Config\Definition\VariableNode; + +class PrototypedArrayNodeTest extends TestCase +{ + public function testGetDefaultValueReturnsAnEmptyArrayForPrototypes() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $node->setPrototype($prototype); + $this->assertEmpty($node->getDefaultValue()); + } + + public function testGetDefaultValueReturnsDefaultValueForPrototypes() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $node->setPrototype($prototype); + $node->setDefaultValue(array('test')); + $this->assertEquals(array('test'), $node->getDefaultValue()); + } + + // a remapped key (e.g. "mapping" -> "mappings") should be unset after being used + public function testRemappedKeysAreUnset() + { + $node = new ArrayNode('root'); + $mappingsNode = new PrototypedArrayNode('mappings'); + $node->addChild($mappingsNode); + + // each item under mappings is just a scalar + $prototype = new ScalarNode(null, $mappingsNode); + $mappingsNode->setPrototype($prototype); + + $remappings = array(); + $remappings[] = array('mapping', 'mappings'); + $node->setXmlRemappings($remappings); + + $normalized = $node->normalize(array('mapping' => array('foo', 'bar'))); + $this->assertEquals(array('mappings' => array('foo', 'bar')), $normalized); + } + + /** + * Tests that when a key attribute is mapped, that key is removed from the array. + * + * + * + * + * The above should finally be mapped to an array that looks like this + * (because "id" is the key attribute). + * + * array( + * 'things' => array( + * 'option1' => 'foo', + * 'option2' => 'bar', + * ) + * ) + */ + public function testMappedAttributeKeyIsRemoved() + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', true); + + // each item under the root is an array, with one scalar item + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('foo')); + $node->setPrototype($prototype); + + $children = array(); + $children[] = array('id' => 'item_name', 'foo' => 'bar'); + $normalized = $node->normalize($children); + + $expected = array(); + $expected['item_name'] = array('foo' => 'bar'); + $this->assertEquals($expected, $normalized); + } + + /** + * Tests the opposite of the testMappedAttributeKeyIsRemoved because + * the removal can be toggled with an option. + */ + public function testMappedAttributeKeyNotRemoved() + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', false); + + // each item under the root is an array, with two scalar items + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('foo')); + $prototype->addChild(new ScalarNode('id')); // the key attribute will remain + $node->setPrototype($prototype); + + $children = array(); + $children[] = array('id' => 'item_name', 'foo' => 'bar'); + $normalized = $node->normalize($children); + + $expected = array(); + $expected['item_name'] = array('id' => 'item_name', 'foo' => 'bar'); + $this->assertEquals($expected, $normalized); + } + + public function testAddDefaultChildren() + { + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('defaults' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet('defaultkey'); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(array('defaultkey')); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('defaultkey' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setKeyAttribute('foobar'); + $node->setAddChildrenIfNoneSet(array('dk1', 'dk2')); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array('dk1' => array('foo' => 'bar'), 'dk2' => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(array(5, 6)); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(0 => array('foo' => 'bar'), 1 => array('foo' => 'bar')), $node->getDefaultValue()); + + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(2); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(array('foo' => 'bar'), array('foo' => 'bar')), $node->getDefaultValue()); + } + + public function testDefaultChildrenWinsOverDefaultValue() + { + $node = $this->getPrototypeNodeWithDefaultChildren(); + $node->setAddChildrenIfNoneSet(); + $node->setDefaultValue(array('bar' => 'foo')); + $this->assertTrue($node->hasDefaultValue()); + $this->assertEquals(array(array('foo' => 'bar')), $node->getDefaultValue()); + } + + protected function getPrototypeNodeWithDefaultChildren() + { + $node = new PrototypedArrayNode('root'); + $prototype = new ArrayNode(null, $node); + $child = new ScalarNode('foo'); + $child->setDefaultValue('bar'); + $prototype->addChild($child); + $prototype->setAddIfNotSet(true); + $node->setPrototype($prototype); + + return $node; + } + + /** + * Tests that when a key attribute is mapped, that key is removed from the array. + * And if only 'value' element is left in the array, it will replace its wrapper array. + * + * + * + * + * The above should finally be mapped to an array that looks like this + * (because "id" is the key attribute). + * + * array( + * 'things' => array( + * 'option1' => 'value1' + * ) + * ) + * + * It's also possible to mix 'value-only' and 'non-value-only' elements in the array. + * + * + * + * + * The above should finally be mapped to an array as follows + * + * array( + * 'things' => array( + * 'option1' => 'value1', + * 'option2' => array( + * 'value' => 'value2', + * 'foo' => 'foo2' + * ) + * ) + * ) + * + * The 'value' element can also be ArrayNode: + * + * + * + * + * + * The above should be finally be mapped to an array as follows + * + * array( + * 'things' => array( + * 'option1' => array( + * 'foo' => 'foo1', + * 'bar' => 'bar1' + * ) + * ) + * ) + * + * If using VariableNode for value node, it's also possible to mix different types of value nodes: + * + * + * + * + * + * The above should be finally mapped to an array as follows + * + * array( + * 'things' => array( + * 'option1' => array( + * 'foo' => 'foo1', + * 'bar' => 'bar1' + * ), + * 'option2' => 'value2' + * ) + * ) + * + * + * @dataProvider getDataForKeyRemovedLeftValueOnly + */ + public function testMappedAttributeKeyIsRemovedLeftValueOnly($value, $children, $expected) + { + $node = new PrototypedArrayNode('root'); + $node->setKeyAttribute('id', true); + + // each item under the root is an array, with one scalar item + $prototype = new ArrayNode(null, $node); + $prototype->addChild(new ScalarNode('id')); + $prototype->addChild(new ScalarNode('foo')); + $prototype->addChild($value); + $node->setPrototype($prototype); + + $normalized = $node->normalize($children); + $this->assertEquals($expected, $normalized); + } + + public function getDataForKeyRemovedLeftValueOnly() + { + $scalarValue = new ScalarNode('value'); + + $arrayValue = new ArrayNode('value'); + $arrayValue->addChild(new ScalarNode('foo')); + $arrayValue->addChild(new ScalarNode('bar')); + + $variableValue = new VariableNode('value'); + + return array( + array( + $scalarValue, + array( + array('id' => 'option1', 'value' => 'value1'), + ), + array('option1' => 'value1'), + ), + + array( + $scalarValue, + array( + array('id' => 'option1', 'value' => 'value1'), + array('id' => 'option2', 'value' => 'value2', 'foo' => 'foo2'), + ), + array( + 'option1' => 'value1', + 'option2' => array('value' => 'value2', 'foo' => 'foo2'), + ), + ), + + array( + $arrayValue, + array( + array( + 'id' => 'option1', + 'value' => array('foo' => 'foo1', 'bar' => 'bar1'), + ), + ), + array( + 'option1' => array('foo' => 'foo1', 'bar' => 'bar1'), + ), + ), + + array($variableValue, + array( + array( + 'id' => 'option1', 'value' => array('foo' => 'foo1', 'bar' => 'bar1'), + ), + array('id' => 'option2', 'value' => 'value2'), + ), + array( + 'option1' => array('foo' => 'foo1', 'bar' => 'bar1'), + 'option2' => 'value2', + ), + ), + ); + } +} diff --git a/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php b/vendor/symfony/config/Tests/Definition/ScalarNodeTest.php new file mode 100644 index 0000000..481ef3f --- /dev/null +++ b/vendor/symfony/config/Tests/Definition/ScalarNodeTest.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\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\ArrayNode; +use Symfony\Component\Config\Definition\ScalarNode; + +class ScalarNodeTest extends TestCase +{ + /** + * @dataProvider getValidValues + */ + public function testNormalize($value) + { + $node = new ScalarNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + public function getValidValues() + { + return array( + array(false), + array(true), + array(null), + array(''), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + ); + } + + public function testSetDeprecated() + { + $childNode = new ScalarNode('foo'); + $childNode->setDeprecated('"%node%" is deprecated'); + + $this->assertTrue($childNode->isDeprecated()); + $this->assertSame('"foo" is deprecated', $childNode->getDeprecationMessage($childNode->getName(), $childNode->getPath())); + + $node = new ArrayNode('root'); + $node->addChild($childNode); + + $deprecationTriggered = 0; + $deprecationHandler = function ($level, $message, $file, $line) use (&$prevErrorHandler, &$deprecationTriggered) { + if (E_USER_DEPRECATED === $level) { + return ++$deprecationTriggered; + } + + return $prevErrorHandler ? $prevErrorHandler($level, $message, $file, $line) : false; + }; + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize(array()); + restore_error_handler(); + $this->assertSame(0, $deprecationTriggered, '->finalize() should not trigger if the deprecated node is not set'); + + $prevErrorHandler = set_error_handler($deprecationHandler); + $node->finalize(array('foo' => '')); + restore_error_handler(); + $this->assertSame(1, $deprecationTriggered, '->finalize() should trigger if the deprecated node is set'); + } + + /** + * @dataProvider getInvalidValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidTypeException + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new ScalarNode('test'); + $node->normalize($value); + } + + public function getInvalidValues() + { + return array( + array(array()), + array(array('foo' => 'bar')), + array(new \stdClass()), + ); + } + + public function testNormalizeThrowsExceptionWithoutHint() + { + $node = new ScalarNode('test'); + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException'); + $this->expectExceptionMessage('Invalid type for path "test". Expected scalar, but got array.'); + } else { + $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', 'Invalid type for path "test". Expected scalar, but got array.'); + } + + $node->normalize(array()); + } + + public function testNormalizeThrowsExceptionWithErrorMessage() + { + $node = new ScalarNode('test'); + $node->setInfo('"the test value"'); + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\Config\Definition\Exception\InvalidTypeException'); + $this->expectExceptionMessage("Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); + } else { + $this->setExpectedException('Symfony\Component\Config\Definition\Exception\InvalidTypeException', "Invalid type for path \"test\". Expected scalar, but got array.\nHint: \"the test value\""); + } + + $node->normalize(array()); + } + + /** + * @dataProvider getValidNonEmptyValues + * + * @param mixed $value + */ + public function testValidNonEmptyValues($value) + { + $node = new ScalarNode('test'); + $node->setAllowEmptyValue(false); + + $this->assertSame($value, $node->finalize($value)); + } + + public function getValidNonEmptyValues() + { + return array( + array(false), + array(true), + array('foo'), + array(0), + array(1), + array(0.0), + array(0.1), + ); + } + + /** + * @dataProvider getEmptyValues + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * + * @param mixed $value + */ + public function testNotAllowedEmptyValuesThrowException($value) + { + $node = new ScalarNode('test'); + $node->setAllowEmptyValue(false); + $node->finalize($value); + } + + public function getEmptyValues() + { + return array( + array(null), + array(''), + ); + } +} diff --git a/vendor/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.php b/vendor/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.php new file mode 100644 index 0000000..58aeda6 --- /dev/null +++ b/vendor/symfony/config/Tests/DependencyInjection/ConfigCachePassTest.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\Config\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\DependencyInjection\ConfigCachePass; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @group legacy + */ +class ConfigCachePassTest extends TestCase +{ + public function testThatCheckersAreProcessedInPriorityOrder() + { + $container = new ContainerBuilder(); + + $definition = $container->register('config_cache_factory')->addArgument(null); + $container->register('checker_2')->addTag('config_cache.resource_checker', array('priority' => 100)); + $container->register('checker_1')->addTag('config_cache.resource_checker', array('priority' => 200)); + $container->register('checker_3')->addTag('config_cache.resource_checker'); + + $pass = new ConfigCachePass(); + $pass->process($container); + + $expected = new IteratorArgument(array( + new Reference('checker_1'), + new Reference('checker_2'), + new Reference('checker_3'), + )); + $this->assertEquals($expected, $definition->getArgument(0)); + } + + public function testThatCheckersCanBeMissing() + { + $container = new ContainerBuilder(); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $pass = new ConfigCachePass(); + $pass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } +} diff --git a/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php b/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.php new file mode 100644 index 0000000..8363084 --- /dev/null +++ b/vendor/symfony/config/Tests/Exception/FileLoaderLoadExceptionTest.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\Config\Tests\Exception; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Exception\FileLoaderLoadException; + +class FileLoaderLoadExceptionTest extends TestCase +{ + public function testMessageCannotLoadResource() + { + $exception = new FileLoaderLoadException('resource', null); + $this->assertEquals('Cannot load resource "resource".', $exception->getMessage()); + } + + public function testMessageCannotLoadResourceWithType() + { + $exception = new FileLoaderLoadException('resource', null, null, null, 'foobar'); + $this->assertEquals('Cannot load resource "resource". Make sure there is a loader supporting the "foobar" type.', $exception->getMessage()); + } + + public function testMessageCannotLoadResourceWithAnnotationType() + { + $exception = new FileLoaderLoadException('resource', null, null, null, 'annotation'); + $this->assertEquals('Cannot load resource "resource". Make sure annotations are installed and enabled.', $exception->getMessage()); + } + + public function testMessageCannotImportResourceFromSource() + { + $exception = new FileLoaderLoadException('resource', 'sourceResource'); + $this->assertEquals('Cannot import resource "resource" from "sourceResource".', $exception->getMessage()); + } + + public function testMessageCannotImportBundleResource() + { + $exception = new FileLoaderLoadException('@resource', 'sourceResource'); + $this->assertEquals( + 'Cannot import resource "@resource" from "sourceResource". '. + 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. + 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorWithDotAndUnableToLoad() + { + $exception = new FileLoaderLoadException( + 'resource', + null, + null, + new \Exception('There was a previous error with an ending dot.') + ); + $this->assertEquals( + 'There was a previous error with an ending dot in resource (which is loaded in resource "resource").', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorWithoutDotAndUnableToLoad() + { + $exception = new FileLoaderLoadException( + 'resource', + null, + null, + new \Exception('There was a previous error with no ending dot') + ); + $this->assertEquals( + 'There was a previous error with no ending dot in resource (which is loaded in resource "resource").', + $exception->getMessage() + ); + } + + public function testMessageHasPreviousErrorAndUnableToLoadBundle() + { + $exception = new FileLoaderLoadException( + '@resource', + null, + null, + new \Exception('There was a previous error with an ending dot.') + ); + $this->assertEquals( + 'There was a previous error with an ending dot in @resource '. + '(which is loaded in resource "@resource"). '. + 'Make sure the "resource" bundle is correctly registered and loaded in the application kernel class. '. + 'If the bundle is registered, make sure the bundle path "@resource" is not empty.', + $exception->getMessage() + ); + } +} diff --git a/vendor/symfony/config/Tests/FileLocatorTest.php b/vendor/symfony/config/Tests/FileLocatorTest.php new file mode 100644 index 0000000..7120504 --- /dev/null +++ b/vendor/symfony/config/Tests/FileLocatorTest.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\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; + +class FileLocatorTest extends TestCase +{ + /** + * @dataProvider getIsAbsolutePathTests + */ + public function testIsAbsolutePath($path) + { + $loader = new FileLocator(array()); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('isAbsolutePath'); + $m->setAccessible(true); + + $this->assertTrue($m->invoke($loader, $path), '->isAbsolutePath() returns true for an absolute path'); + } + + public function getIsAbsolutePathTests() + { + return array( + array('/foo.xml'), + array('c:\\\\foo.xml'), + array('c:/foo.xml'), + array('\\server\\foo.xml'), + array('https://server/foo.xml'), + array('phar://server/foo.xml'), + ); + } + + public function testLocate() + { + $loader = new FileLocator(__DIR__.'/Fixtures'); + + $this->assertEquals( + __DIR__.\DIRECTORY_SEPARATOR.'FileLocatorTest.php', + $loader->locate('FileLocatorTest.php', __DIR__), + '->locate() returns the absolute filename if the file exists in the given path' + ); + + $this->assertEquals( + __DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', + $loader->locate('foo.xml', __DIR__), + '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' + ); + + $this->assertEquals( + __DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', + $loader->locate(__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__), + '->locate() returns the absolute filename if the file exists in one of the paths given in the constructor' + ); + + $loader = new FileLocator(array(__DIR__.'/Fixtures', __DIR__.'/Fixtures/Again')); + + $this->assertEquals( + array(__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'), + $loader->locate('foo.xml', __DIR__, false), + '->locate() returns an array of absolute filenames' + ); + + $this->assertEquals( + array(__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'), + $loader->locate('foo.xml', __DIR__.'/Fixtures', false), + '->locate() returns an array of absolute filenames' + ); + + $loader = new FileLocator(__DIR__.'/Fixtures/Again'); + + $this->assertEquals( + array(__DIR__.'/Fixtures'.\DIRECTORY_SEPARATOR.'foo.xml', __DIR__.'/Fixtures/Again'.\DIRECTORY_SEPARATOR.'foo.xml'), + $loader->locate('foo.xml', __DIR__.'/Fixtures', false), + '->locate() returns an array of absolute filenames' + ); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException + * @expectedExceptionMessage The file "foobar.xml" does not exist + */ + public function testLocateThrowsAnExceptionIfTheFileDoesNotExists() + { + $loader = new FileLocator(array(__DIR__.'/Fixtures')); + + $loader->locate('foobar.xml', __DIR__); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLocatorFileNotFoundException + */ + public function testLocateThrowsAnExceptionIfTheFileDoesNotExistsInAbsolutePath() + { + $loader = new FileLocator(array(__DIR__.'/Fixtures')); + + $loader->locate(__DIR__.'/Fixtures/foobar.xml', __DIR__); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage An empty file name is not valid to be located. + */ + public function testLocateEmpty() + { + $loader = new FileLocator(array(__DIR__.'/Fixtures')); + + $loader->locate(null, __DIR__); + } +} diff --git a/vendor/symfony/config/Tests/Fixtures/Again/foo.xml b/vendor/symfony/config/Tests/Fixtures/Again/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/config/Tests/Fixtures/BadParent.php b/vendor/symfony/config/Tests/Fixtures/BadParent.php new file mode 100644 index 0000000..68d7296 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/BadParent.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\Config\Tests\Fixtures; + +use Symfony\Component\Config\Definition\ArrayNode; + +class BarNode extends ArrayNode +{ +} diff --git a/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php b/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.php new file mode 100644 index 0000000..b9c62e5 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Builder/BarNodeDefinition.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\Config\Tests\Fixtures\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeDefinition; +use Symfony\Component\Config\Tests\Fixtures\BarNode; + +class BarNodeDefinition extends NodeDefinition +{ + protected function createNode() + { + return new BarNode($this->name); + } +} diff --git a/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php b/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.php new file mode 100644 index 0000000..22b8b32 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Builder/NodeBuilder.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\Config\Tests\Fixtures\Builder; + +use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; + +class NodeBuilder extends BaseNodeBuilder +{ + public function barNode($name) + { + return $this->node($name, 'bar'); + } + + protected function getNodeClass($type) + { + switch ($type) { + case 'variable': + return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; + case 'bar': + return __NAMESPACE__.'\\'.ucfirst($type).'NodeDefinition'; + default: + return parent::getNodeClass($type); + } + } +} diff --git a/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php b/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.php new file mode 100644 index 0000000..6126ed4 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Builder/VariableNodeDefinition.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\Config\Tests\Fixtures\Builder; + +use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; + +class VariableNodeDefinition extends BaseVariableNodeDefinition +{ +} diff --git a/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.php new file mode 100644 index 0000000..d44b67c --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Configuration/ExampleConfiguration.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\Config\Tests\Fixtures\Configuration; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class ExampleConfiguration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('acme_root'); + + $rootNode + ->fixXmlConfig('parameter') + ->fixXmlConfig('connection') + ->fixXmlConfig('cms_page') + ->children() + ->booleanNode('boolean')->defaultTrue()->end() + ->scalarNode('scalar_empty')->end() + ->scalarNode('scalar_null')->defaultNull()->end() + ->scalarNode('scalar_true')->defaultTrue()->end() + ->scalarNode('scalar_false')->defaultFalse()->end() + ->scalarNode('scalar_default')->defaultValue('default')->end() + ->scalarNode('scalar_array_empty')->defaultValue(array())->end() + ->scalarNode('scalar_array_defaults')->defaultValue(array('elem1', 'elem2'))->end() + ->scalarNode('scalar_required')->isRequired()->end() + ->scalarNode('scalar_deprecated')->setDeprecated()->end() + ->scalarNode('scalar_deprecated_with_message')->setDeprecated('Deprecation custom message for "%node%" at "%path%"')->end() + ->scalarNode('node_with_a_looong_name')->end() + ->enumNode('enum_with_default')->values(array('this', 'that'))->defaultValue('this')->end() + ->enumNode('enum')->values(array('this', 'that'))->end() + ->arrayNode('array') + ->info('some info') + ->canBeUnset() + ->children() + ->scalarNode('child1')->end() + ->scalarNode('child2')->end() + ->scalarNode('child3') + ->info( + "this is a long\n". + "multi-line info text\n". + 'which should be indented' + ) + ->example('example setting') + ->end() + ->end() + ->end() + ->arrayNode('scalar_prototyped') + ->prototype('scalar')->end() + ->end() + ->arrayNode('parameters') + ->useAttributeAsKey('name') + ->prototype('scalar')->info('Parameter name')->end() + ->end() + ->arrayNode('connections') + ->prototype('array') + ->children() + ->scalarNode('user')->end() + ->scalarNode('pass')->end() + ->end() + ->end() + ->end() + ->arrayNode('cms_pages') + ->useAttributeAsKey('page') + ->prototype('array') + ->useAttributeAsKey('locale') + ->prototype('array') + ->children() + ->scalarNode('title')->isRequired()->end() + ->scalarNode('path')->isRequired()->end() + ->end() + ->end() + ->end() + ->end() + ->arrayNode('pipou') + ->useAttributeAsKey('name') + ->prototype('array') + ->prototype('array') + ->children() + ->scalarNode('didou') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/vendor/symfony/config/Tests/Fixtures/Resource/.hiddenFile b/vendor/symfony/config/Tests/Fixtures/Resource/.hiddenFile new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php b/vendor/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php new file mode 100644 index 0000000..2ba48c5 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Resource/ConditionalClass.php @@ -0,0 +1,9 @@ + +]> + diff --git a/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml b/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml new file mode 100644 index 0000000..a07af9f --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Util/invalid.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml b/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml new file mode 100644 index 0000000..e2725a2 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Util/invalid_schema.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd b/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd new file mode 100644 index 0000000..e56820f --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Util/schema.xsd @@ -0,0 +1,9 @@ + + + + + + diff --git a/vendor/symfony/config/Tests/Fixtures/Util/valid.xml b/vendor/symfony/config/Tests/Fixtures/Util/valid.xml new file mode 100644 index 0000000..a96bb38 --- /dev/null +++ b/vendor/symfony/config/Tests/Fixtures/Util/valid.xml @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/config/Tests/Fixtures/foo.xml b/vendor/symfony/config/Tests/Fixtures/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php b/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.php new file mode 100644 index 0000000..3f4b21f --- /dev/null +++ b/vendor/symfony/config/Tests/Loader/DelegatingLoaderTest.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\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolver; + +class DelegatingLoaderTest extends TestCase +{ + public function testConstructor() + { + $loader = new DelegatingLoader($resolver = new LoaderResolver()); + $this->assertTrue(true, '__construct() takes a loader resolver as its first argument'); + } + + public function testGetSetResolver() + { + $resolver = new LoaderResolver(); + $loader = new DelegatingLoader($resolver); + $this->assertSame($resolver, $loader->getResolver(), '->getResolver() gets the resolver loader'); + $loader->setResolver($resolver = new LoaderResolver()); + $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); + } + + public function testSupports() + { + $loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader1->expects($this->once())->method('supports')->will($this->returnValue(true)); + $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); + $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); + + $loader1 = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader1->expects($this->once())->method('supports')->will($this->returnValue(false)); + $loader = new DelegatingLoader(new LoaderResolver(array($loader1))); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); + } + + public function testLoad() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); + $loader->expects($this->once())->method('load'); + $resolver = new LoaderResolver(array($loader)); + $loader = new DelegatingLoader($resolver); + + $loader->load('foo'); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException + */ + public function testLoadThrowsAnExceptionIfTheResourceCannotBeLoaded() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->once())->method('supports')->will($this->returnValue(false)); + $resolver = new LoaderResolver(array($loader)); + $loader = new DelegatingLoader($resolver); + + $loader->load('foo'); + } +} diff --git a/vendor/symfony/config/Tests/Loader/FileLoaderTest.php b/vendor/symfony/config/Tests/Loader/FileLoaderTest.php new file mode 100644 index 0000000..f8409df --- /dev/null +++ b/vendor/symfony/config/Tests/Loader/FileLoaderTest.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\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Loader\LoaderResolver; + +class FileLoaderTest extends TestCase +{ + public function testImportWithFileLocatorDelegation() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + + $locatorMockForAdditionalLoader = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $locatorMockForAdditionalLoader->expects($this->any())->method('locate')->will($this->onConsecutiveCalls( + array('path/to/file1'), // Default + array('path/to/file1', 'path/to/file2'), // First is imported + array('path/to/file1', 'path/to/file2'), // Second is imported + array('path/to/file1'), // Exception + array('path/to/file1', 'path/to/file2') // Exception + )); + + $fileLoader = new TestFileLoader($locatorMock); + $fileLoader->setSupports(false); + $fileLoader->setCurrentDir('.'); + + $additionalLoader = new TestFileLoader($locatorMockForAdditionalLoader); + $additionalLoader->setCurrentDir('.'); + + $fileLoader->setResolver($loaderResolver = new LoaderResolver(array($fileLoader, $additionalLoader))); + + // Default case + $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); + + // Check first file is imported if not already loading + $this->assertSame('path/to/file1', $fileLoader->import('my_resource')); + + // Check second file is imported if first is already loading + $fileLoader->addLoading('path/to/file1'); + $this->assertSame('path/to/file2', $fileLoader->import('my_resource')); + + // Check exception throws if first (and only available) file is already loading + try { + $fileLoader->import('my_resource'); + $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } + + // Check exception throws if all files are already loading + try { + $fileLoader->addLoading('path/to/file2'); + $fileLoader->import('my_resource'); + $this->fail('->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\Config\Exception\FileLoaderImportCircularReferenceException', $e, '->import() throws a FileLoaderImportCircularReferenceException if the resource is already loading'); + } + } + + public function testImportWithGlobLikeResource() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $loader = new TestFileLoader($locatorMock); + + $this->assertSame('[foo]', $loader->import('[foo]')); + } + + public function testImportWithNoGlobMatch() + { + $locatorMock = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock(); + $loader = new TestFileLoader($locatorMock); + + $this->assertNull($loader->import('./*.abc')); + } + + public function testImportWithSimpleGlob() + { + $loader = new TestFileLoader(new FileLocator(__DIR__)); + + $this->assertSame(__FILE__, strtr($loader->import('FileLoaderTest.*'), '/', \DIRECTORY_SEPARATOR)); + } +} + +class TestFileLoader extends FileLoader +{ + private $supports = true; + + public function load($resource, $type = null) + { + return $resource; + } + + public function supports($resource, $type = null) + { + return $this->supports; + } + + public function addLoading($resource) + { + self::$loading[$resource] = true; + } + + public function removeLoading($resource) + { + unset(self::$loading[$resource]); + } + + public function clearLoading() + { + self::$loading = array(); + } + + public function setSupports($supports) + { + $this->supports = $supports; + } +} diff --git a/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php b/vendor/symfony/config/Tests/Loader/LoaderResolverTest.php new file mode 100644 index 0000000..0bf56b6 --- /dev/null +++ b/vendor/symfony/config/Tests/Loader/LoaderResolverTest.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\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderResolver; + +class LoaderResolverTest extends TestCase +{ + public function testConstructor() + { + $resolver = new LoaderResolver(array( + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(), + )); + + $this->assertEquals(array($loader), $resolver->getLoaders(), '__construct() takes an array of loaders as its first argument'); + } + + public function testResolve() + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolver = new LoaderResolver(array($loader)); + $this->assertFalse($resolver->resolve('foo.foo'), '->resolve() returns false if no loader is able to load the resource'); + + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $loader->expects($this->once())->method('supports')->will($this->returnValue(true)); + $resolver = new LoaderResolver(array($loader)); + $this->assertEquals($loader, $resolver->resolve(function () {}), '->resolve() returns the loader for the given resource'); + } + + public function testLoaders() + { + $resolver = new LoaderResolver(); + $resolver->addLoader($loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock()); + + $this->assertEquals(array($loader), $resolver->getLoaders(), 'addLoader() adds a loader'); + } +} diff --git a/vendor/symfony/config/Tests/Loader/LoaderTest.php b/vendor/symfony/config/Tests/Loader/LoaderTest.php new file mode 100644 index 0000000..30167e3 --- /dev/null +++ b/vendor/symfony/config/Tests/Loader/LoaderTest.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\Config\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\Loader; + +class LoaderTest extends TestCase +{ + public function testGetSetResolver() + { + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertSame($resolver, $loader->getResolver(), '->setResolver() sets the resolver loader'); + } + + public function testResolve() + { + $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('foo.xml') + ->will($this->returnValue($resolvedLoader)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertSame($loader, $loader->resolve('foo.foo'), '->resolve() finds a loader'); + $this->assertSame($resolvedLoader, $loader->resolve('foo.xml'), '->resolve() finds a loader'); + } + + /** + * @expectedException \Symfony\Component\Config\Exception\FileLoaderLoadException + */ + public function testResolveWhenResolverCannotFindLoader() + { + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('FOOBAR') + ->will($this->returnValue(false)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $loader->resolve('FOOBAR'); + } + + public function testImport() + { + $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolvedLoader->expects($this->once()) + ->method('load') + ->with('foo') + ->will($this->returnValue('yes')); + + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo') + ->will($this->returnValue($resolvedLoader)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertEquals('yes', $loader->import('foo')); + } + + public function testImportWithType() + { + $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + $resolvedLoader->expects($this->once()) + ->method('load') + ->with('foo', 'bar') + ->will($this->returnValue('yes')); + + $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); + $resolver->expects($this->once()) + ->method('resolve') + ->with('foo', 'bar') + ->will($this->returnValue($resolvedLoader)); + + $loader = new ProjectLoader1(); + $loader->setResolver($resolver); + + $this->assertEquals('yes', $loader->import('foo', 'bar')); + } +} + +class ProjectLoader1 extends Loader +{ + public function load($resource, $type = null) + { + } + + public function supports($resource, $type = null) + { + return \is_string($resource) && 'foo' === pathinfo($resource, PATHINFO_EXTENSION); + } + + public function getType() + { + } +} diff --git a/vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php b/vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php new file mode 100644 index 0000000..79bc64d --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/ClassExistenceResourceTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\Config\Tests\Fixtures\BadParent; +use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass; + +class ClassExistenceResourceTest extends TestCase +{ + public function testToString() + { + $res = new ClassExistenceResource('BarClass'); + $this->assertSame('BarClass', (string) $res); + } + + public function testGetResource() + { + $res = new ClassExistenceResource('BarClass'); + $this->assertSame('BarClass', $res->getResource()); + } + + public function testIsFreshWhenClassDoesNotExist() + { + $res = new ClassExistenceResource('Symfony\Component\Config\Tests\Fixtures\BarClass'); + + $this->assertTrue($res->isFresh(time())); + + eval(<<assertFalse($res->isFresh(time())); + } + + public function testIsFreshWhenClassExists() + { + $res = new ClassExistenceResource('Symfony\Component\Config\Tests\Resource\ClassExistenceResourceTest'); + + $this->assertTrue($res->isFresh(time())); + } + + public function testExistsKo() + { + spl_autoload_register($autoloader = function ($class) use (&$loadedClass) { $loadedClass = $class; }); + + try { + $res = new ClassExistenceResource('MissingFooClass'); + $this->assertTrue($res->isFresh(0)); + + $this->assertSame('MissingFooClass', $loadedClass); + + $loadedClass = 123; + + $res = new ClassExistenceResource('MissingFooClass', false); + + $this->assertSame(123, $loadedClass); + } finally { + spl_autoload_unregister($autoloader); + } + } + + public function testBadParentWithTimestamp() + { + $res = new ClassExistenceResource(BadParent::class, false); + $this->assertTrue($res->isFresh(time())); + } + + /** + * @expectedException \ReflectionException + * @expectedExceptionMessage Class Symfony\Component\Config\Tests\Fixtures\MissingParent not found + */ + public function testBadParentWithNoTimestamp() + { + $res = new ClassExistenceResource(BadParent::class, false); + $res->isFresh(0); + } + + public function testConditionalClass() + { + $res = new ClassExistenceResource(ConditionalClass::class, false); + + $this->assertFalse($res->isFresh(0)); + } +} diff --git a/vendor/symfony/config/Tests/Resource/ComposerResourceTest.php b/vendor/symfony/config/Tests/Resource/ComposerResourceTest.php new file mode 100644 index 0000000..6857c76 --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/ComposerResourceTest.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\Config\Tests\Resource; + +use Composer\Autoload\ClassLoader; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ComposerResource; + +class ComposerResourceTest extends TestCase +{ + public function testGetVendor() + { + $res = new ComposerResource(); + + $r = new \ReflectionClass(ClassLoader::class); + $found = false; + + foreach ($res->getVendors() as $vendor) { + if ($vendor && 0 === strpos($r->getFileName(), $vendor)) { + $found = true; + break; + } + } + + $this->assertTrue($found); + } + + public function testSerializeUnserialize() + { + $res = new ComposerResource(); + $ser = unserialize(serialize($res)); + + $this->assertTrue($res->isFresh(0)); + $this->assertTrue($ser->isFresh(0)); + + $this->assertEquals($res, $ser); + } +} diff --git a/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php b/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.php new file mode 100644 index 0000000..cf20e43 --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/DirectoryResourceTest.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\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\DirectoryResource; + +class DirectoryResourceTest extends TestCase +{ + protected $directory; + + protected function setUp() + { + $this->directory = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfonyDirectoryIterator'; + if (!file_exists($this->directory)) { + mkdir($this->directory); + } + touch($this->directory.'/tmp.xml'); + } + + protected function tearDown() + { + if (!is_dir($this->directory)) { + return; + } + $this->removeDirectory($this->directory); + } + + protected function removeDirectory($directory) + { + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory), \RecursiveIteratorIterator::CHILD_FIRST); + foreach ($iterator as $path) { + if (preg_match('#[/\\\\]\.\.?$#', $path->__toString())) { + continue; + } + if ($path->isDir()) { + rmdir($path->__toString()); + } else { + unlink($path->__toString()); + } + } + rmdir($directory); + } + + public function testGetResource() + { + $resource = new DirectoryResource($this->directory); + $this->assertSame(realpath($this->directory), $resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testGetPattern() + { + $resource = new DirectoryResource($this->directory, 'bar'); + $this->assertEquals('bar', $resource->getPattern()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The directory ".*" does not exist./ + */ + public function testResourceDoesNotExist() + { + $resource = new DirectoryResource('/____foo/foobar'.mt_rand(1, 999999)); + } + + public function testIsFresh() + { + $resource = new DirectoryResource($this->directory); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($resource->isFresh(time() - 86400), '->isFresh() returns false if the resource has been updated'); + } + + public function testIsFreshForDeletedResources() + { + $resource = new DirectoryResource($this->directory); + $this->removeDirectory($this->directory); + + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the resource does not exist'); + } + + public function testIsFreshUpdateFile() + { + $resource = new DirectoryResource($this->directory); + touch($this->directory.'/tmp.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an existing file is modified'); + } + + public function testIsFreshNewFile() + { + $resource = new DirectoryResource($this->directory); + touch($this->directory.'/new.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file is added'); + } + + public function testIsFreshNewFileWithDifferentPattern() + { + $resource = new DirectoryResource($this->directory, '/.xml$/'); + touch($this->directory.'/new.yaml', time() + 20); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file with a non-matching pattern is added'); + } + + public function testIsFreshDeleteFile() + { + $resource = new DirectoryResource($this->directory); + $time = time(); + sleep(1); + unlink($this->directory.'/tmp.xml'); + $this->assertFalse($resource->isFresh($time), '->isFresh() returns false if an existing file is removed'); + } + + public function testIsFreshDeleteDirectory() + { + $resource = new DirectoryResource($this->directory); + $this->removeDirectory($this->directory); + $this->assertFalse($resource->isFresh(time()), '->isFresh() returns false if the whole resource is removed'); + } + + public function testIsFreshCreateFileInSubdirectory() + { + $subdirectory = $this->directory.'/subdirectory'; + mkdir($subdirectory); + + $resource = new DirectoryResource($this->directory); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if an unmodified subdirectory exists'); + + touch($subdirectory.'/newfile.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a new file in a subdirectory is added'); + } + + public function testIsFreshModifySubdirectory() + { + $resource = new DirectoryResource($this->directory); + + $subdirectory = $this->directory.'/subdirectory'; + mkdir($subdirectory); + touch($subdirectory, time() + 20); + + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if a subdirectory is modified (e.g. a file gets deleted)'); + } + + public function testFilterRegexListNoMatch() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + touch($this->directory.'/new.bar', time() + 20); + $this->assertTrue($resource->isFresh(time() + 10), '->isFresh() returns true if a new file not matching the filter regex is created'); + } + + public function testFilterRegexListMatch() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + touch($this->directory.'/new.xml', time() + 20); + $this->assertFalse($resource->isFresh(time() + 10), '->isFresh() returns false if an new file matching the filter regex is created '); + } + + public function testSerializeUnserialize() + { + $resource = new DirectoryResource($this->directory, '/\.(foo|xml)$/'); + + $unserialized = unserialize(serialize($resource)); + + $this->assertSame(realpath($this->directory), $resource->getResource()); + $this->assertSame('/\.(foo|xml)$/', $resource->getPattern()); + } + + public function testResourcesWithDifferentPatternsAreDifferent() + { + $resourceA = new DirectoryResource($this->directory, '/.xml$/'); + $resourceB = new DirectoryResource($this->directory, '/.yaml$/'); + + $this->assertCount(2, array_unique(array($resourceA, $resourceB))); + } +} diff --git a/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php b/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.php new file mode 100644 index 0000000..433f65e --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/FileExistenceResourceTest.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\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileExistenceResource; + +class FileExistenceResourceTest extends TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = realpath(sys_get_temp_dir()).'/tmp.xml'; + $this->time = time(); + $this->resource = new FileExistenceResource($this->file); + } + + protected function tearDown() + { + if (file_exists($this->file)) { + unlink($this->file); + } + } + + public function testToString() + { + $this->assertSame($this->file, (string) $this->resource); + } + + public function testGetResource() + { + $this->assertSame($this->file, $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testIsFreshWithExistingResource() + { + touch($this->file, $this->time); + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still present'); + + unlink($this->file); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been deleted'); + } + + public function testIsFreshWithAbsentResource() + { + $serialized = serialize(new FileExistenceResource($this->file)); + + $resource = unserialize($serialized); + $this->assertTrue($resource->isFresh($this->time), '->isFresh() returns true if the resource is still absent'); + + touch($this->file, $this->time); + $resource = unserialize($serialized); + $this->assertFalse($resource->isFresh($this->time), '->isFresh() returns false if the resource has been created'); + } +} diff --git a/vendor/symfony/config/Tests/Resource/FileResourceTest.php b/vendor/symfony/config/Tests/Resource/FileResourceTest.php new file mode 100644 index 0000000..97781ff --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/FileResourceTest.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\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; + +class FileResourceTest extends TestCase +{ + protected $resource; + protected $file; + protected $time; + + protected function setUp() + { + $this->file = sys_get_temp_dir().'/tmp.xml'; + $this->time = time(); + touch($this->file, $this->time); + $this->resource = new FileResource($this->file); + } + + protected function tearDown() + { + if (!file_exists($this->file)) { + return; + } + + unlink($this->file); + } + + public function testGetResource() + { + $this->assertSame(realpath($this->file), $this->resource->getResource(), '->getResource() returns the path to the resource'); + } + + public function testGetResourceWithScheme() + { + $resource = new FileResource('file://'.$this->file); + $this->assertSame('file://'.$this->file, $resource->getResource(), '->getResource() returns the path to the schemed resource'); + } + + public function testToString() + { + $this->assertSame(realpath($this->file), (string) $this->resource); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The file ".*" does not exist./ + */ + public function testResourceDoesNotExist() + { + $resource = new FileResource('/____foo/foobar'.mt_rand(1, 999999)); + } + + public function testIsFresh() + { + $this->assertTrue($this->resource->isFresh($this->time), '->isFresh() returns true if the resource has not changed in same second'); + $this->assertTrue($this->resource->isFresh($this->time + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertFalse($this->resource->isFresh($this->time - 86400), '->isFresh() returns false if the resource has been updated'); + } + + public function testIsFreshForDeletedResources() + { + unlink($this->file); + + $this->assertFalse($this->resource->isFresh($this->time), '->isFresh() returns false if the resource does not exist'); + } + + public function testSerializeUnserialize() + { + $unserialized = unserialize(serialize($this->resource)); + + $this->assertSame(realpath($this->file), $this->resource->getResource()); + } +} diff --git a/vendor/symfony/config/Tests/Resource/GlobResourceTest.php b/vendor/symfony/config/Tests/Resource/GlobResourceTest.php new file mode 100644 index 0000000..c66770c --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/GlobResourceTest.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\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\GlobResource; + +class GlobResourceTest extends TestCase +{ + protected function tearDown() + { + $dir = \dirname(__DIR__).'/Fixtures'; + @rmdir($dir.'/TmpGlob'); + @unlink($dir.'/TmpGlob'); + @unlink($dir.'/Resource/TmpGlob'); + touch($dir.'/Resource/.hiddenFile'); + } + + public function testIterator() + { + $dir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'; + $resource = new GlobResource($dir, '/Resource', true); + + $paths = iterator_to_array($resource); + + $file = $dir.'/Resource'.\DIRECTORY_SEPARATOR.'ConditionalClass.php'; + $this->assertEquals(array($file => new \SplFileInfo($file)), $paths); + $this->assertInstanceOf('SplFileInfo', current($paths)); + $this->assertSame($dir, $resource->getPrefix()); + + $resource = new GlobResource($dir, '/**/Resource', true); + + $paths = iterator_to_array($resource); + + $file = $dir.\DIRECTORY_SEPARATOR.'Resource'.\DIRECTORY_SEPARATOR.'ConditionalClass.php'; + $this->assertEquals(array($file => $file), $paths); + $this->assertInstanceOf('SplFileInfo', current($paths)); + $this->assertSame($dir, $resource->getPrefix()); + } + + public function testIsFreshNonRecursiveDetectsNewFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', false); + + $this->assertTrue($resource->isFresh(0)); + + mkdir($dir.'/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + rmdir($dir.'/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + touch($dir.'/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + + unlink($dir.'/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + } + + public function testIsFreshNonRecursiveDetectsRemovedFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', false); + + touch($dir.'/TmpGlob'); + touch($dir.'/.TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/.TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + } + + public function testIsFreshRecursiveDetectsRemovedFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', true); + + touch($dir.'/Resource/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/Resource/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + + touch($dir.'/Resource/TmpGlob'); + $this->assertTrue($resource->isFresh(0)); + + unlink($dir.'/Resource/.hiddenFile'); + $this->assertTrue($resource->isFresh(0)); + } + + public function testIsFreshRecursiveDetectsNewFile() + { + $dir = \dirname(__DIR__).'/Fixtures'; + $resource = new GlobResource($dir, '/*', true); + + $this->assertTrue($resource->isFresh(0)); + + touch($dir.'/Resource/TmpGlob'); + $this->assertFalse($resource->isFresh(0)); + } +} diff --git a/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php b/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.php new file mode 100644 index 0000000..61b0fdf --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/ReflectionClassResourceTest.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\Config\Tests\Resource; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\ReflectionClassResource; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ReflectionClassResourceTest extends TestCase +{ + public function testToString() + { + $res = new ReflectionClassResource(new \ReflectionClass('ErrorException')); + + $this->assertSame('reflection.ErrorException', (string) $res); + } + + public function testSerializeUnserialize() + { + $res = new ReflectionClassResource(new \ReflectionClass(DummyInterface::class)); + $ser = unserialize(serialize($res)); + + $this->assertTrue($res->isFresh(0)); + $this->assertTrue($ser->isFresh(0)); + + $this->assertSame((string) $res, (string) $ser); + } + + public function testIsFresh() + { + $res = new ReflectionClassResource(new \ReflectionClass(__CLASS__)); + $mtime = filemtime(__FILE__); + + $this->assertTrue($res->isFresh($mtime), '->isFresh() returns true if the resource has not changed in same second'); + $this->assertTrue($res->isFresh($mtime + 10), '->isFresh() returns true if the resource has not changed'); + $this->assertTrue($res->isFresh($mtime - 86400), '->isFresh() returns true if the resource has not changed'); + } + + public function testIsFreshForDeletedResources() + { + $now = time(); + $tmp = sys_get_temp_dir().'/tmp.php'; + file_put_contents($tmp, 'assertTrue($res->isFresh($now)); + + unlink($tmp); + $this->assertFalse($res->isFresh($now), '->isFresh() returns false if the resource does not exist'); + } + + /** + * @dataProvider provideHashedSignature + */ + public function testHashedSignature($changeExpected, $changedLine, $changedCode) + { + $code = <<<'EOPHP' +/* 0*/ +/* 1*/ class %s extends ErrorException +/* 2*/ { +/* 3*/ const FOO = 123; +/* 4*/ +/* 5*/ public $pub = array(); +/* 6*/ +/* 7*/ protected $prot; +/* 8*/ +/* 9*/ private $priv; +/*10*/ +/*11*/ public function pub($arg = null) {} +/*12*/ +/*13*/ protected function prot($a = array()) {} +/*14*/ +/*15*/ private function priv() {} +/*16*/ } +EOPHP; + + static $expectedSignature, $generateSignature; + + if (null === $expectedSignature) { + eval(sprintf($code, $class = 'Foo'.str_replace('.', '_', uniqid('', true)))); + $r = new \ReflectionClass(ReflectionClassResource::class); + $generateSignature = $r->getMethod('generateSignature'); + $generateSignature->setAccessible(true); + $generateSignature = $generateSignature->getClosure($r->newInstanceWithoutConstructor()); + $expectedSignature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class)))); + } + + $code = explode("\n", $code); + $code[$changedLine] = $changedCode; + eval(sprintf(implode("\n", $code), $class = 'Foo'.str_replace('.', '_', uniqid('', true)))); + $signature = implode("\n", iterator_to_array($generateSignature(new \ReflectionClass($class)))); + + if ($changeExpected) { + $this->assertNotSame($expectedSignature, $signature); + } else { + $this->assertSame($expectedSignature, $signature); + } + } + + public function provideHashedSignature() + { + yield array(0, 0, "// line change\n\n"); + yield array(1, 0, '/** class docblock */'); + yield array(1, 1, 'abstract class %s'); + yield array(1, 1, 'final class %s'); + yield array(1, 1, 'class %s extends Exception'); + yield array(1, 1, 'class %s implements '.DummyInterface::class); + yield array(1, 3, 'const FOO = 456;'); + yield array(1, 3, 'const BAR = 123;'); + yield array(1, 4, '/** pub docblock */'); + yield array(1, 5, 'protected $pub = array();'); + yield array(1, 5, 'public $pub = array(123);'); + yield array(1, 6, '/** prot docblock */'); + yield array(1, 7, 'private $prot;'); + yield array(0, 8, '/** priv docblock */'); + yield array(0, 9, 'private $priv = 123;'); + yield array(1, 10, '/** pub docblock */'); + if (\PHP_VERSION_ID >= 50600) { + yield array(1, 11, 'public function pub(...$arg) {}'); + } + if (\PHP_VERSION_ID >= 70000) { + yield array(1, 11, 'public function pub($arg = null): Foo {}'); + } + yield array(0, 11, "public function pub(\$arg = null) {\nreturn 123;\n}"); + yield array(1, 12, '/** prot docblock */'); + yield array(1, 13, 'protected function prot($a = array(123)) {}'); + yield array(0, 14, '/** priv docblock */'); + yield array(0, 15, ''); + } + + public function testEventSubscriber() + { + $res = new ReflectionClassResource(new \ReflectionClass(TestEventSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + + TestEventSubscriber::$subscribedEvents = array(123); + $this->assertFalse($res->isFresh(0)); + + $res = new ReflectionClassResource(new \ReflectionClass(TestEventSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + } + + public function testServiceSubscriber() + { + $res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + + TestServiceSubscriber::$subscribedServices = array(123); + $this->assertFalse($res->isFresh(0)); + + $res = new ReflectionClassResource(new \ReflectionClass(TestServiceSubscriber::class)); + $this->assertTrue($res->isFresh(0)); + } +} + +interface DummyInterface +{ +} + +class TestEventSubscriber implements EventSubscriberInterface +{ + public static $subscribedEvents = array(); + + public static function getSubscribedEvents() + { + return self::$subscribedEvents; + } +} + +class TestServiceSubscriber implements ServiceSubscriberInterface +{ + public static $subscribedServices = array(); + + public static function getSubscribedServices() + { + return self::$subscribedServices; + } +} diff --git a/vendor/symfony/config/Tests/Resource/ResourceStub.php b/vendor/symfony/config/Tests/Resource/ResourceStub.php new file mode 100644 index 0000000..b01729c --- /dev/null +++ b/vendor/symfony/config/Tests/Resource/ResourceStub.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\Config\Tests\Resource; + +use Symfony\Component\Config\Resource\SelfCheckingResourceInterface; + +class ResourceStub implements SelfCheckingResourceInterface +{ + private $fresh = true; + + public function setFresh($isFresh) + { + $this->fresh = $isFresh; + } + + public function __toString() + { + return 'stub'; + } + + public function isFresh($timestamp) + { + return $this->fresh; + } +} diff --git a/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php b/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php new file mode 100644 index 0000000..bb29ac0 --- /dev/null +++ b/vendor/symfony/config/Tests/ResourceCheckerConfigCacheTest.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\ResourceCheckerConfigCache; +use Symfony\Component\Config\Tests\Resource\ResourceStub; + +class ResourceCheckerConfigCacheTest extends TestCase +{ + private $cacheFile = null; + + protected function setUp() + { + $this->cacheFile = tempnam(sys_get_temp_dir(), 'config_'); + } + + protected function tearDown() + { + $files = array($this->cacheFile, "{$this->cacheFile}.meta"); + + foreach ($files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + public function testGetPath() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile); + + $this->assertSame($this->cacheFile, $cache->getPath()); + } + + public function testCacheIsNotFreshIfEmpty() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock() + ->expects($this->never())->method('supports'); + + /* If there is nothing in the cache, it needs to be filled (and thus it's not fresh). + It does not matter if you provide checkers or not. */ + + unlink($this->cacheFile); // remove tempnam() side effect + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsFreshIfNoCheckerProvided() + { + /* For example in prod mode, you may choose not to run any checkers + at all. In that case, the cache should always be considered fresh. */ + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $this->assertTrue($cache->isFresh()); + } + + public function testCacheIsFreshIfEmptyCheckerIteratorProvided() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile, new \ArrayIterator(array())); + $this->assertTrue($cache->isFresh()); + } + + public function testResourcesWithoutcheckersAreIgnoredAndConsideredFresh() + { + /* As in the previous test, but this time we have a resource. */ + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $cache->write('', array(new ResourceStub())); + + $this->assertTrue($cache->isFresh()); // no (matching) ResourceChecker passed + } + + public function testIsFreshWithchecker() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + + $checker->expects($this->once()) + ->method('supports') + ->willReturn(true); + + $checker->expects($this->once()) + ->method('isFresh') + ->willReturn(true); + + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + $cache->write('', array(new ResourceStub())); + + $this->assertTrue($cache->isFresh()); + } + + public function testIsNotFreshWithchecker() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + + $checker->expects($this->once()) + ->method('supports') + ->willReturn(true); + + $checker->expects($this->once()) + ->method('isFresh') + ->willReturn(false); + + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + $cache->write('', array(new ResourceStub())); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheIsNotFreshWhenUnserializeFails() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + $cache->write('foo', array(new FileResource(__FILE__))); + + $metaFile = "{$this->cacheFile}.meta"; + file_put_contents($metaFile, str_replace('FileResource', 'ClassNotHere', file_get_contents($metaFile))); + + $this->assertFalse($cache->isFresh()); + } + + public function testCacheKeepsContent() + { + $cache = new ResourceCheckerConfigCache($this->cacheFile); + $cache->write('FOOBAR'); + + $this->assertSame('FOOBAR', file_get_contents($cache->getPath())); + } + + public function testCacheIsNotFreshIfNotExistsMetaFile() + { + $checker = $this->getMockBuilder('\Symfony\Component\Config\ResourceCheckerInterface')->getMock(); + $cache = new ResourceCheckerConfigCache($this->cacheFile, array($checker)); + $cache->write('foo', array(new FileResource(__FILE__))); + + $metaFile = "{$this->cacheFile}.meta"; + unlink($metaFile); + + $this->assertFalse($cache->isFresh()); + } +} diff --git a/vendor/symfony/config/Tests/Util/XmlUtilsTest.php b/vendor/symfony/config/Tests/Util/XmlUtilsTest.php new file mode 100644 index 0000000..10b4a7a --- /dev/null +++ b/vendor/symfony/config/Tests/Util/XmlUtilsTest.php @@ -0,0 +1,217 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Util; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Util\XmlUtils; + +class XmlUtilsTest extends TestCase +{ + public function testLoadFile() + { + $fixtures = __DIR__.'/../Fixtures/Util/'; + + try { + XmlUtils::loadFile($fixtures.'invalid.xml'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('ERROR 77', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'document_type.xml'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('Document types are not allowed', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'invalid_schema.xml', $fixtures.'schema.xsd'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('ERROR 1845', $e->getMessage()); + } + + try { + XmlUtils::loadFile($fixtures.'invalid_schema.xml', 'invalid_callback_or_file'); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertContains('XSD file or callable', $e->getMessage()); + } + + $mock = $this->getMockBuilder(__NAMESPACE__.'\Validator')->getMock(); + $mock->expects($this->exactly(2))->method('validate')->will($this->onConsecutiveCalls(false, true)); + + try { + XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate')); + $this->fail(); + } catch (\InvalidArgumentException $e) { + $this->assertRegExp('/The XML file ".+" is not valid\./', $e->getMessage()); + } + + $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile($fixtures.'valid.xml', array($mock, 'validate'))); + $this->assertSame(array(), libxml_get_errors()); + } + + /** + * @expectedException \Symfony\Component\Config\Util\Exception\InvalidXmlException + * @expectedExceptionMessage The XML is not valid + */ + public function testParseWithInvalidValidatorCallable() + { + $fixtures = __DIR__.'/../Fixtures/Util/'; + + $mock = $this->getMockBuilder(__NAMESPACE__.'\Validator')->getMock(); + $mock->expects($this->once())->method('validate')->willReturn(false); + + XmlUtils::parse(file_get_contents($fixtures.'valid.xml'), array($mock, 'validate')); + } + + public function testLoadFileWithInternalErrorsEnabled() + { + $internalErrors = libxml_use_internal_errors(true); + + $this->assertSame(array(), libxml_get_errors()); + $this->assertInstanceOf('DOMDocument', XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/invalid_schema.xml')); + $this->assertSame(array(), libxml_get_errors()); + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + } + + /** + * @dataProvider getDataForConvertDomToArray + */ + public function testConvertDomToArray($expected, $xml, $root = false, $checkPrefix = true) + { + $dom = new \DOMDocument(); + $dom->loadXML($root ? $xml : ''.$xml.''); + + $this->assertSame($expected, XmlUtils::convertDomElementToArray($dom->documentElement, $checkPrefix)); + } + + public function getDataForConvertDomToArray() + { + return array( + array(null, ''), + array('bar', 'bar'), + array(array('bar' => 'foobar'), '', true), + array(array('foo' => null), ''), + array(array('foo' => 'bar'), 'bar'), + array(array('foo' => array('foo' => 'bar')), ''), + array(array('foo' => array('foo' => 0)), '0'), + array(array('foo' => array('foo' => 'bar')), 'bar'), + array(array('foo' => array('foo' => 'bar', 'value' => 'text')), 'text'), + array(array('foo' => array('attr' => 'bar', 'foo' => 'text')), 'text'), + array(array('foo' => array('bar', 'text')), 'bartext'), + array(array('foo' => array(array('foo' => 'bar'), array('foo' => 'text'))), ''), + array(array('foo' => array('foo' => array('bar', 'text'))), 'text'), + array(array('foo' => 'bar'), 'bar'), + array(array('foo' => 'text'), 'text'), + array(array('foo' => array('bar' => 'bar', 'value' => 'text')), 'text', false, false), + array(array('attr' => 1, 'b' => 'hello'), 'hello2', true), + ); + } + + /** + * @dataProvider getDataForPhpize + */ + public function testPhpize($expected, $value) + { + $this->assertSame($expected, XmlUtils::phpize($value)); + } + + public function getDataForPhpize() + { + return array( + array('', ''), + array(null, 'null'), + array(true, 'true'), + array(false, 'false'), + array(null, 'Null'), + array(true, 'True'), + array(false, 'False'), + array(0, '0'), + array(1, '1'), + array(-1, '-1'), + array(0777, '0777'), + array(255, '0xFF'), + array(100.0, '1e2'), + array(-120.0, '-1.2E2'), + array(-10100.1, '-10100.1'), + array('-10,100.1', '-10,100.1'), + array('1234 5678 9101 1121 3141', '1234 5678 9101 1121 3141'), + array('1,2,3,4', '1,2,3,4'), + array('11,22,33,44', '11,22,33,44'), + array('11,222,333,4', '11,222,333,4'), + array('1,222,333,444', '1,222,333,444'), + array('11,222,333,444', '11,222,333,444'), + array('111,222,333,444', '111,222,333,444'), + array('1111,2222,3333,4444,5555', '1111,2222,3333,4444,5555'), + array('foo', 'foo'), + array(6, '0b0110'), + ); + } + + public function testLoadEmptyXmlFile() + { + $file = __DIR__.'/../Fixtures/foo.xml'; + + if (method_exists($this, 'expectException')) { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage(sprintf('File %s does not contain valid XML, it is empty.', $file)); + } else { + $this->setExpectedException('InvalidArgumentException', sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + + XmlUtils::loadFile($file); + } + + // test for issue https://github.com/symfony/symfony/issues/9731 + public function testLoadWrongEmptyXMLWithErrorHandler() + { + $originalDisableEntities = libxml_disable_entity_loader(false); + $errorReporting = error_reporting(-1); + + set_error_handler(function ($errno, $errstr) { + throw new \Exception($errstr, $errno); + }); + + $file = __DIR__.'/../Fixtures/foo.xml'; + try { + try { + XmlUtils::loadFile($file); + $this->fail('An exception should have been raised'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals(sprintf('File %s does not contain valid XML, it is empty.', $file), $e->getMessage()); + } + } finally { + restore_error_handler(); + error_reporting($errorReporting); + } + + $disableEntities = libxml_disable_entity_loader(true); + libxml_disable_entity_loader($disableEntities); + + libxml_disable_entity_loader($originalDisableEntities); + + $this->assertFalse($disableEntities); + + // should not throw an exception + XmlUtils::loadFile(__DIR__.'/../Fixtures/Util/valid.xml', __DIR__.'/../Fixtures/Util/schema.xsd'); + } +} + +interface Validator +{ + public function validate(); +} diff --git a/vendor/symfony/config/Util/Exception/InvalidXmlException.php b/vendor/symfony/config/Util/Exception/InvalidXmlException.php new file mode 100644 index 0000000..a335bbd --- /dev/null +++ b/vendor/symfony/config/Util/Exception/InvalidXmlException.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\Config\Util\Exception; + +/** + * Exception class for when XML parsing with an XSD schema file path or a callable validator produces errors unrelated + * to the actual XML parsing. + * + * @author Ole Rößner + */ +class InvalidXmlException extends XmlParsingException +{ +} diff --git a/vendor/symfony/config/Util/Exception/XmlParsingException.php b/vendor/symfony/config/Util/Exception/XmlParsingException.php new file mode 100644 index 0000000..9bceed6 --- /dev/null +++ b/vendor/symfony/config/Util/Exception/XmlParsingException.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\Config\Util\Exception; + +/** + * Exception class for when XML cannot be parsed properly. + * + * @author Ole Rößner + */ +class XmlParsingException extends \InvalidArgumentException +{ +} diff --git a/vendor/symfony/config/Util/XmlUtils.php b/vendor/symfony/config/Util/XmlUtils.php new file mode 100644 index 0000000..d761e4e --- /dev/null +++ b/vendor/symfony/config/Util/XmlUtils.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\Config\Util; + +use Symfony\Component\Config\Util\Exception\InvalidXmlException; +use Symfony\Component\Config\Util\Exception\XmlParsingException; + +/** + * XMLUtils is a bunch of utility methods to XML operations. + * + * This class contains static methods only and is not meant to be instantiated. + * + * @author Fabien Potencier + * @author Martin Hasoň + * @author Ole Rößner + */ +class XmlUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Parses an XML string. + * + * @param string $content An XML string + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws XmlParsingException When parsing of XML file returns error + * @throws InvalidXmlException When parsing of XML with schema or callable produces any errors unrelated to the XML parsing itself + * @throws \RuntimeException When DOM extension is missing + */ + public static function parse($content, $schemaOrCallable = null) + { + if (!\extension_loaded('dom')) { + throw new \RuntimeException('Extension DOM is required.'); + } + + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + $dom = new \DOMDocument(); + $dom->validateOnParse = true; + if (!$dom->loadXML($content, LIBXML_NONET | (\defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { + libxml_disable_entity_loader($disableEntities); + + throw new XmlParsingException(implode("\n", static::getXmlErrors($internalErrors))); + } + + $dom->normalizeDocument(); + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + foreach ($dom->childNodes as $child) { + if (XML_DOCUMENT_TYPE_NODE === $child->nodeType) { + throw new XmlParsingException('Document types are not allowed.'); + } + } + + if (null !== $schemaOrCallable) { + $internalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + $e = null; + if (\is_callable($schemaOrCallable)) { + try { + $valid = \call_user_func($schemaOrCallable, $dom, $internalErrors); + } catch (\Exception $e) { + $valid = false; + } + } elseif (!\is_array($schemaOrCallable) && is_file((string) $schemaOrCallable)) { + $schemaSource = file_get_contents((string) $schemaOrCallable); + $valid = @$dom->schemaValidateSource($schemaSource); + } else { + libxml_use_internal_errors($internalErrors); + + throw new XmlParsingException('The schemaOrCallable argument has to be a valid path to XSD file or callable.'); + } + + if (!$valid) { + $messages = static::getXmlErrors($internalErrors); + if (empty($messages)) { + throw new InvalidXmlException('The XML is not valid.', 0, $e); + } + throw new XmlParsingException(implode("\n", $messages), 0, $e); + } + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $dom; + } + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string|callable|null $schemaOrCallable An XSD schema file path, a callable, or null to disable validation + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + * @throws XmlParsingException When XML parsing returns any errors + * @throws \RuntimeException When DOM extension is missing + */ + public static function loadFile($file, $schemaOrCallable = null) + { + $content = @file_get_contents($file); + if ('' === trim($content)) { + throw new \InvalidArgumentException(sprintf('File %s does not contain valid XML, it is empty.', $file)); + } + + try { + return static::parse($content, $schemaOrCallable); + } catch (InvalidXmlException $e) { + throw new XmlParsingException(sprintf('The XML file "%s" is not valid.', $file), 0, $e->getPrevious()); + } + } + + /** + * Converts a \DOMElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DOMElement $element A \DOMElement instance + * @param bool $checkPrefix Check prefix in an element or an attribute name + * + * @return array A PHP array + */ + public static function convertDomElementToArray(\DOMElement $element, $checkPrefix = true) + { + $prefix = (string) $element->prefix; + $empty = true; + $config = array(); + foreach ($element->attributes as $name => $node) { + if ($checkPrefix && !\in_array((string) $node->prefix, array('', $prefix), true)) { + continue; + } + $config[$name] = static::phpize($node->value); + $empty = false; + } + + $nodeValue = false; + foreach ($element->childNodes as $node) { + if ($node instanceof \DOMText) { + if ('' !== trim($node->nodeValue)) { + $nodeValue = trim($node->nodeValue); + $empty = false; + } + } elseif ($checkPrefix && $prefix != (string) $node->prefix) { + continue; + } elseif (!$node instanceof \DOMComment) { + $value = static::convertDomElementToArray($node, $checkPrefix); + + $key = $node->localName; + if (isset($config[$key])) { + if (!\is_array($config[$key]) || !\is_int(key($config[$key]))) { + $config[$key] = array($config[$key]); + } + $config[$key][] = $value; + } else { + $config[$key] = $value; + } + + $empty = false; + } + } + + if (false !== $nodeValue) { + $value = static::phpize($nodeValue); + if (\count($config)) { + $config['value'] = $value; + } else { + $config = $value; + } + } + + return !$empty ? $config : null; + } + + /** + * Converts an xml value to a PHP type. + * + * @param mixed $value + * + * @return mixed + */ + public static function phpize($value) + { + $value = (string) $value; + $lowercaseValue = strtolower($value); + + switch (true) { + case 'null' === $lowercaseValue: + return; + case ctype_digit($value): + $raw = $value; + $cast = (int) $value; + + return '0' == $value[0] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + case isset($value[1]) && '-' === $value[0] && ctype_digit(substr($value, 1)): + $raw = $value; + $cast = (int) $value; + + return '0' == $value[1] ? octdec($value) : (((string) $raw === (string) $cast) ? $cast : $raw); + case 'true' === $lowercaseValue: + return true; + case 'false' === $lowercaseValue: + return false; + case isset($value[1]) && '0b' == $value[0].$value[1]: + return bindec($value); + case is_numeric($value): + return '0x' === $value[0].$value[1] ? hexdec($value) : (float) $value; + case preg_match('/^0x[0-9a-f]++$/i', $value): + return hexdec($value); + case preg_match('/^(-|\+)?[0-9]+(\.[0-9]+)?$/', $value): + return (float) $value; + default: + return $value; + } + } + + protected static function getXmlErrors($internalErrors) + { + $errors = array(); + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', + LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), + $error->file ?: 'n/a', + $error->line, + $error->column + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $errors; + } +} diff --git a/vendor/symfony/config/composer.json b/vendor/symfony/config/composer.json new file mode 100644 index 0000000..c04294d --- /dev/null +++ b/vendor/symfony/config/composer.json @@ -0,0 +1,48 @@ +{ + "name": "symfony/config", + "type": "library", + "description": "Symfony Config 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/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "require-dev": { + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0", + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0" + }, + "conflict": { + "symfony/finder": "<3.3", + "symfony/dependency-injection": "<3.3" + }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Config\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/vendor/symfony/config/phpunit.xml.dist b/vendor/symfony/config/phpunit.xml.dist new file mode 100644 index 0000000..36ef339 --- /dev/null +++ b/vendor/symfony/config/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/debug/.gitignore b/vendor/symfony/debug/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/debug/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/debug/BufferingLogger.php b/vendor/symfony/debug/BufferingLogger.php new file mode 100644 index 0000000..a2ed75b --- /dev/null +++ b/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/vendor/symfony/debug/CHANGELOG.md b/vendor/symfony/debug/CHANGELOG.md new file mode 100644 index 0000000..122af73 --- /dev/null +++ b/vendor/symfony/debug/CHANGELOG.md @@ -0,0 +1,70 @@ +CHANGELOG +========= + +4.0.0 +----- + +* removed the symfony_debug extension +* removed `ContextErrorException` + +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/vendor/symfony/debug/Debug.php b/vendor/symfony/debug/Debug.php new file mode 100644 index 0000000..3f65703 --- /dev/null +++ b/vendor/symfony/debug/Debug.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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. + * + * @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 (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) { + 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/vendor/symfony/debug/DebugClassLoader.php b/vendor/symfony/debug/DebugClassLoader.php new file mode 100644 index 0000000..b7f3ca3 --- /dev/null +++ b/vendor/symfony/debug/DebugClassLoader.php @@ -0,0 +1,429 @@ + + * + * 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 $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 + * + * @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)); + } + + $deprecations = $this->checkAnnotations($refl, $name); + + foreach ($deprecations as $message) { + @trigger_error($message, E_USER_DEPRECATED); + } + } + + if (!$file) { + return; + } + + 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 && $message = $this->checkCase($refl, $file, $class)) { + throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2])); + } + } + + public function checkAnnotations(\ReflectionClass $refl, $class) + { + $deprecations = array(); + + // Don't trigger deprecations for classes in the same vendor + if (2 > $len = 1 + (\strpos($class, '\\') ?: \strpos($class, '_'))) { + $len = 0; + $ns = ''; + } else { + $ns = \substr($class, 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}[$class] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : ''; + } + } + } + + $parent = \get_parent_class($class); + $parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent); + if ($parent) { + $parentAndOwnInterfaces[$parent] = $parent; + + if (!isset(self::$checkedClasses[$parent])) { + $this->checkClass($parent); + } + + if (isset(self::$final[$parent])) { + $deprecations[] = 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], $class); + } + } + + // Detect if the parent is annotated + foreach ($parentAndOwnInterfaces + \class_uses($class, false) as $use) { + if (!isset(self::$checkedClasses[$use])) { + $this->checkClass($use); + } + if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) { + $type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait'); + $verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses'); + + $deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]); + } + if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) { + $deprecations[] = 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], $class); + } + } + + if (\trait_exists($class)) { + return $deprecations; + } + + // Inherit @final and @internal annotations for methods + self::$finalMethods[$class] = array(); + self::$internalMethods[$class] = array(); + foreach ($parentAndOwnInterfaces as $use) { + foreach (array('finalMethods', 'internalMethods') as $property) { + if (isset(self::${$property}[$use])) { + self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use]; + } + } + } + + foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) { + if ($method->class !== $class) { + continue; + } + + if ($parent && isset(self::$finalMethods[$parent][$method->name])) { + list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; + $deprecations[] = 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, $class); + } + + if (isset(self::$internalMethods[$class][$method->name])) { + list($declaringClass, $message) = self::$internalMethods[$class][$method->name]; + if (\strncmp($ns, $declaringClass, $len)) { + $deprecations[] = 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, $class); + } + } + + // 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'}[$class][$method->name] = array($class, $message); + } + } + } + + return $deprecations; + } + + public function checkCase(\ReflectionClass $refl, $file, $class) + { + $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 (!$tail) { + return; + } + + $tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail); + $tailLen = \strlen($tail); + $real = $refl->getFileName(); + + if (2 === self::$caseCheck) { + $real = $this->darwinRealpath($real); + } + + if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true) + && 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false) + ) { + return array(substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)); + } + } + + /** + * `realpath` on MacOSX doesn't normalize the case of characters. + */ + private function darwinRealpath($real) + { + $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])) { + return $real .= $dirFiles[$file]; + } + + $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; + } + + return $real .= $dirFiles[$kFile]; + } + + /** + * `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/vendor/symfony/debug/ErrorHandler.php b/vendor/symfony/debug/ErrorHandler.php new file mode 100644 index 0000000..caffd54 --- /dev/null +++ b/vendor/symfony/debug/ErrorHandler.php @@ -0,0 +1,682 @@ + + * + * 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\LoggerInterface; +use Psr\Log\LogLevel; +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\ClassNotFoundFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; +use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; + +/** + * 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 $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(); + $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; + } else { + $handlerIsRegistered = true; + } + if (\is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] instanceof self) { + restore_exception_handler(); + if (!$handlerIsRegistered) { + $handler = $prev[0]; + } elseif ($handler !== $prev[0] && $replace) { + set_exception_handler(array($handler, 'handleException')); + $p = $prev[0]->setExceptionHandler(null); + $handler->setExceptionHandler($p); + $prev[0]->setExceptionHandler($p); + } + } 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. + $level = error_reporting(); + $silenced = 0 === ($level & $type); + // Strong errors are not authorized to be silenced. + $level |= 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 !$silenced && $type && $log; + } + $scope = $this->scopedErrors & $type; + + if (4 < $numArgs = \func_num_args()) { + $context = $scope ? (func_get_arg(4) ?: array()) : array(); + } else { + $context = array(); + } + + 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; + } + + $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 { + $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 = $errorAsException->getTrace(); + $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); + $this->traceReflector->setValue($errorAsException, $lightTrace); + } else { + $this->traceReflector->setValue($errorAsException, array()); + $backtrace = 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(). + // PHP triggers a fatal error when throwing from __toString(). + // 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 \Throwable && $e->__toString() === $message) { + self::$toStringException = $e; + + return true; + } + } + + // 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; + } 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 !$silenced && $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 (\Throwable $handlerException) { + } + } + if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { + foreach ($this->getFatalErrorHandlers() as $handler) { + if ($e = $handler->handleError($error, $exception)) { + $exception = $e; + break; + } + } + } + $exceptionHandler = $this->exceptionHandler; + $this->exceptionHandler = null; + try { + if (null !== $exceptionHandler) { + return \call_user_func($exceptionHandler, $exception); + } + $handlerException = $handlerException ?: $exception; + } catch (\Throwable $handlerException) { + } + 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(); + } + + 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); + } + } else { + $exception = null; + } + + try { + if (null !== $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); }); + } + } + + /** + * 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/vendor/symfony/debug/Exception/ClassNotFoundException.php b/vendor/symfony/debug/Exception/ClassNotFoundException.php new file mode 100644 index 0000000..fa98c49 --- /dev/null +++ b/vendor/symfony/debug/Exception/ClassNotFoundException.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\Debug\Exception; + +/** + * Class (or Trait or Interface) Not Found Exception. + * + * @author Konstanton Myakshin + */ +class ClassNotFoundException extends FatalErrorException +{ + public function __construct(string $message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + null, + true, + null, + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/debug/Exception/FatalErrorException.php b/vendor/symfony/debug/Exception/FatalErrorException.php new file mode 100644 index 0000000..8305d39 --- /dev/null +++ b/vendor/symfony/debug/Exception/FatalErrorException.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\Debug\Exception; + +/** + * Fatal Error Exception. + * + * @author Konstanton Myakshin + */ +class FatalErrorException extends \ErrorException +{ + public function __construct(string $message, int $code, int $severity, string $filename, int $lineno, int $traceOffset = null, bool $traceArgs = true, array $trace = null, \Throwable $previous = null) + { + parent::__construct($message, $code, $severity, $filename, $lineno, $previous); + + 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); + } 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/vendor/symfony/debug/Exception/FatalThrowableError.php b/vendor/symfony/debug/Exception/FatalThrowableError.php new file mode 100644 index 0000000..cdafb2a --- /dev/null +++ b/vendor/symfony/debug/Exception/FatalThrowableError.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\Debug\Exception; + +/** + * Fatal Throwable Error. + * + * @author Nicolas Grekas + */ +class FatalThrowableError extends FatalErrorException +{ + private $originalClassName; + + public function __construct(\Throwable $e) + { + $this->originalClassName = \get_class($e); + + if ($e instanceof \ParseError) { + $severity = E_PARSE; + } elseif ($e instanceof \TypeError) { + $severity = E_RECOVERABLE_ERROR; + } else { + $severity = E_ERROR; + } + + \ErrorException::__construct( + $e->getMessage(), + $e->getCode(), + $severity, + $e->getFile(), + $e->getLine(), + $e->getPrevious() + ); + + $this->setTrace($e->getTrace()); + } + + public function getOriginalClassName(): string + { + return $this->originalClassName; + } +} diff --git a/vendor/symfony/debug/Exception/FlattenException.php b/vendor/symfony/debug/Exception/FlattenException.php new file mode 100644 index 0000000..b091b55 --- /dev/null +++ b/vendor/symfony/debug/Exception/FlattenException.php @@ -0,0 +1,276 @@ + + * + * 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 Error or 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()) + { + return static::createFromThrowable($exception, $statusCode, $headers); + } + + public static function createFromThrowable(\Throwable $exception, ?int $statusCode = null, array $headers = array()): self + { + $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->setTraceFromThrowable($exception); + $e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception)); + $e->setFile($exception->getFile()); + $e->setLine($exception->getLine()); + + $previous = $exception->getPrevious(); + + if ($previous instanceof \Throwable) { + $e->setPrevious(static::createFromThrowable($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(self $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; + } + + /** + * @deprecated since 4.1, use {@see setTraceFromThrowable()} instead. + */ + public function setTraceFromException(\Exception $exception) + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED); + + $this->setTraceFromThrowable($exception); + } + + public function setTraceFromThrowable(\Throwable $throwable): void + { + $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->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/vendor/symfony/debug/Exception/OutOfMemoryException.php b/vendor/symfony/debug/Exception/OutOfMemoryException.php new file mode 100644 index 0000000..fec1979 --- /dev/null +++ b/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/vendor/symfony/debug/Exception/SilencedErrorContext.php b/vendor/symfony/debug/Exception/SilencedErrorContext.php new file mode 100644 index 0000000..6f84617 --- /dev/null +++ b/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(int $severity, string $file, int $line, array $trace = array(), int $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/vendor/symfony/debug/Exception/UndefinedFunctionException.php b/vendor/symfony/debug/Exception/UndefinedFunctionException.php new file mode 100644 index 0000000..d936c87 --- /dev/null +++ b/vendor/symfony/debug/Exception/UndefinedFunctionException.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\Debug\Exception; + +/** + * Undefined Function Exception. + * + * @author Konstanton Myakshin + */ +class UndefinedFunctionException extends FatalErrorException +{ + public function __construct(string $message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + null, + true, + null, + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/debug/Exception/UndefinedMethodException.php b/vendor/symfony/debug/Exception/UndefinedMethodException.php new file mode 100644 index 0000000..f627561 --- /dev/null +++ b/vendor/symfony/debug/Exception/UndefinedMethodException.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\Debug\Exception; + +/** + * Undefined Method Exception. + * + * @author Grégoire Pineau + */ +class UndefinedMethodException extends FatalErrorException +{ + public function __construct(string $message, \ErrorException $previous) + { + parent::__construct( + $message, + $previous->getCode(), + $previous->getSeverity(), + $previous->getFile(), + $previous->getLine(), + null, + true, + null, + $previous->getPrevious() + ); + $this->setTrace($previous->getTrace()); + } +} diff --git a/vendor/symfony/debug/ExceptionHandler.php b/vendor/symfony/debug/ExceptionHandler.php new file mode 100644 index 0000000..1845bc8 --- /dev/null +++ b/vendor/symfony/debug/ExceptionHandler.php @@ -0,0 +1,440 @@ + + * + * 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(bool $debug = true, string $charset = null, $fileLinkFormat = null) + { + $this->debug = $debug; + $this->charset = $charset ?: ini_get('default_charset') ?: 'UTF-8'; + $this->fileLinkFormat = $fileLinkFormat; + } + + /** + * 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.'; + } + + if (!$this->debug) { + return << +

    $title

    + +EOF; + } + + $content = ''; + 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) + { + if (!$this->debug) { + return <<<'EOF' + body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; } + .container { margin: 30px; max-width: 600px; } + h1 { color: #dc3545; font-size: 24px; } +EOF; + } + + 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 ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + + if (!$fmt) { + return sprintf('in %s%s', $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : ''); + } + + if (\is_string($fmt)) { + $i = strpos($f = $fmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l'))) ?: \strlen($f); + $fmt = array(substr($f, 0, $i)) + preg_split('/&([^>]++)>/', substr($f, $i), -1, PREG_SPLIT_DELIM_CAPTURE); + + for ($i = 1; isset($fmt[$i]); ++$i) { + if (0 === strpos($path, $k = $fmt[$i++])) { + $path = substr_replace($path, $fmt[$i], 0, \strlen($k)); + break; + } + } + + $link = strtr($fmt[0], array('%f' => $path, '%l' => $line)); + } else { + $link = $fmt->format($path, $line); + } + + return sprintf('in %s
    %s', $this->escapeHtml($link), $file, 0 < $line ? ' line '.$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/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php new file mode 100644 index 0000000..4ccd16f --- /dev/null +++ b/vendor/symfony/debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.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\Debug\FatalErrorHandler; + +use Composer\Autoload\ClassLoader as ComposerClassLoader; +use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader; +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\Exception\ClassNotFoundException; +use Symfony\Component\Debug\Exception\FatalErrorException; + +/** + * 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(string $class): array + { + 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); + } + + private function findClassInPath(string $path, string $class, string $prefix): array + { + 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; + } + + private function convertFileToClass(string $path, string $file, string $prefix): ?string + { + $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; + } + } + + return null; + } + + private function classExists(string $class): bool + { + return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false); + } +} diff --git a/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php b/vendor/symfony/debug/FatalErrorHandler/FatalErrorHandlerInterface.php new file mode 100644 index 0000000..6b87eb3 --- /dev/null +++ b/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/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/UndefinedFunctionFatalErrorHandler.php new file mode 100644 index 0000000..db24180 --- /dev/null +++ b/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\FatalErrorException; +use Symfony\Component\Debug\Exception\UndefinedFunctionException; + +/** + * 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/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php b/vendor/symfony/debug/FatalErrorHandler/UndefinedMethodFatalErrorHandler.php new file mode 100644 index 0000000..618a2c2 --- /dev/null +++ b/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/vendor/symfony/debug/LICENSE b/vendor/symfony/debug/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/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/vendor/symfony/debug/README.md b/vendor/symfony/debug/README.md new file mode 100644 index 0000000..a1d1617 --- /dev/null +++ b/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/vendor/symfony/debug/Tests/DebugClassLoaderTest.php b/vendor/symfony/debug/Tests/DebugClassLoaderTest.php new file mode 100644 index 0000000..4a23755 --- /dev/null +++ b/vendor/symfony/debug/Tests/DebugClassLoaderTest.php @@ -0,0 +1,346 @@ + + * + * 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; + +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'); + } + + /** + * @expectedException \RuntimeException + */ + 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 + */ + 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 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. 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() + { + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); + $e = error_reporting(E_USER_DEPRECATED); + + class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true); + + error_reporting($e); + restore_error_handler(); + + $xError = array( + 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', + 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. 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, $deprecations); + } + + 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\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\InternalClass" class 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\InternalClass::internalMethod()" method is considered internal. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".', + )); + } + + public function testUseTraitWithInternalMethod() + { + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); + $e = error_reporting(E_USER_DEPRECATED); + + class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true); + + error_reporting($e); + restore_error_handler(); + + $this->assertSame(array(), $deprecations); + } +} + +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 { }'); + } elseif ('Test\\'.__NAMESPACE__.'\UseTraitWithInternalMethod' === $class) { + eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }'); + } + } +} diff --git a/vendor/symfony/debug/Tests/ErrorHandlerTest.php b/vendor/symfony/debug/Tests/ErrorHandlerTest.php new file mode 100644 index 0000000..15e4763 --- /dev/null +++ b/vendor/symfony/debug/Tests/ErrorHandlerTest.php @@ -0,0 +1,504 @@ + + * + * 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($handler, 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 testErrorGetLast() + { + $handler = ErrorHandler::register(); + $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); + $handler->setDefaultLogger($logger); + $handler->screamAt(E_ALL); + + try { + @trigger_error('Hello', E_USER_WARNING); + $expected = array( + 'type' => E_USER_WARNING, + 'message' => 'Hello', + 'file' => __FILE__, + 'line' => __LINE__ - 5, + ); + $this->assertSame($expected, error_get_last()); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + 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()); + } + + 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(); + } + } + + 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))); + } + + 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); + } + + 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; + } + } + + 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()); + } + + /** + * @expectedException \Exception + */ + public function testCustomExceptionHandler() + { + $handler = new ErrorHandler(); + $handler->setExceptionHandler(function ($e) use ($handler) { + $handler->handleException($e); + }); + + $handler->handleException(new \Exception()); + } +} diff --git a/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php b/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.php new file mode 100644 index 0000000..4bac160 --- /dev/null +++ b/vendor/symfony/debug/Tests/Exception/FlattenExceptionTest.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\Debug\Tests\Exception; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\Exception\FatalThrowableError; +use Symfony\Component\Debug\Exception\FlattenException; +use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\ConflictHttpException; +use Symfony\Component\HttpKernel\Exception\GoneHttpException; +use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +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\UnauthorizedHttpException; +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::createFromThrowable(new \DivisionByZeroError(), 403); + $this->assertEquals('403', $flattened->getStatusCode()); + + $flattened = FlattenException::createFromThrowable(new \DivisionByZeroError()); + $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(\Throwable $exception) + { + $flattened = FlattenException::createFromThrowable($exception); + $flattened2 = FlattenException::createFromThrowable($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'); + } + + public function testWrappedThrowable() + { + $exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42)); + $flattened = FlattenException::create($exception); + + $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.'); + $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.'); + $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error'); + } + + public function testThrowable() + { + $error = new \DivisionByZeroError('Ouch', 42); + $flattened = FlattenException::createFromThrowable($error); + + $this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.'); + $this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.'); + $this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testPrevious(\Throwable $exception) + { + $flattened = FlattenException::createFromThrowable($exception); + $flattened2 = FlattenException::createFromThrowable($exception); + + $flattened->setPrevious($flattened2); + + $this->assertSame($flattened2, $flattened->getPrevious()); + + $this->assertSame(array($flattened2), $flattened->getAllPrevious()); + } + + public function testPreviousError() + { + $exception = new \Exception('test', 123, new \ParseError('Oh noes!', 42)); + + $flattened = FlattenException::create($exception)->getPrevious(); + + $this->assertEquals($flattened->getMessage(), '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(), 'ParseError', 'The class is set to the class of the original exception'); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testLine(\Throwable $exception) + { + $flattened = FlattenException::createFromThrowable($exception); + $this->assertSame($exception->getLine(), $flattened->getLine()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testFile(\Throwable $exception) + { + $flattened = FlattenException::createFromThrowable($exception); + $this->assertSame($exception->getFile(), $flattened->getFile()); + } + + /** + * @dataProvider flattenDataProvider + */ + public function testToArray(\Throwable $exception, string $expectedClass) + { + $flattened = FlattenException::createFromThrowable($exception); + $flattened->setTrace(array(), 'foo.php', 123); + + $this->assertEquals(array( + array( + 'message' => 'test', + 'class' => $expectedClass, + 'trace' => array(array( + 'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123, + 'args' => array(), + )), + ), + ), $flattened->toArray()); + } + + public function testCreate() + { + $exception = new NotFoundHttpException( + 'test', + new \RuntimeException('previous', 123) + ); + + $this->assertSame( + FlattenException::createFromThrowable($exception)->toArray(), + FlattenException::create($exception)->toArray() + ); + } + + public function flattenDataProvider() + { + return array( + array(new \Exception('test', 123), 'Exception'), + array(new \Error('test', 123), 'Error'), + ); + } + + 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', '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/vendor/symfony/debug/Tests/ExceptionHandlerTest.php b/vendor/symfony/debug/Tests/ExceptionHandlerTest.php new file mode 100644 index 0000000..6ff6a74 --- /dev/null +++ b/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\Exception\OutOfMemoryException; +use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +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/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/vendor/symfony/debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php new file mode 100644 index 0000000..5cdac2f --- /dev/null +++ b/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 Composer\Autoload\ClassLoader as ComposerClassLoader; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Debug\DebugClassLoader; +use Symfony\Component\Debug\Exception\FatalErrorException; +use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; + +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/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php b/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedFunctionFatalErrorHandlerTest.php new file mode 100644 index 0000000..60153fc --- /dev/null +++ b/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 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/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php b/vendor/symfony/debug/Tests/FatalErrorHandler/UndefinedMethodFatalErrorHandlerTest.php new file mode 100644 index 0000000..a2647f5 --- /dev/null +++ b/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/vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php b/vendor/symfony/debug/Tests/Fixtures/AnnotatedClass.php new file mode 100644 index 0000000..4eecb6d --- /dev/null +++ b/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/vendor/symfony/debug/Tests/Fixtures/TraitWithInternalMethod.php b/vendor/symfony/debug/Tests/Fixtures/TraitWithInternalMethod.php new file mode 100644 index 0000000..4c3dae3 --- /dev/null +++ b/vendor/symfony/debug/Tests/Fixtures/TraitWithInternalMethod.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\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/vendor/symfony/debug/Tests/MockExceptionHandler.php b/vendor/symfony/debug/Tests/MockExceptionHandler.php new file mode 100644 index 0000000..2d6ce56 --- /dev/null +++ b/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/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt b/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt new file mode 100644 index 0000000..53839c4 --- /dev/null +++ b/vendor/symfony/debug/Tests/phpt/debug_class_loader.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test DebugClassLoader with previously loaded parents +--FILE-- + +--EXPECTF-- +The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod". +The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. 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/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt b/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt new file mode 100644 index 0000000..9cd4438 --- /dev/null +++ b/vendor/symfony/debug/Tests/phpt/decorate_exception_hander.phpt @@ -0,0 +1,47 @@ +--TEST-- +Test catching fatal errors when handlers are nested +--INI-- +display_errors=0 +--FILE-- + +--EXPECTF-- +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(%d) {%A} + ["previous":"Exception":private]=> + NULL + ["severity":protected]=> + int(1) +} diff --git a/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt b/vendor/symfony/debug/Tests/phpt/exception_rethrown.phpt new file mode 100644 index 0000000..3f45595 --- /dev/null +++ b/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/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt b/vendor/symfony/debug/Tests/phpt/fatal_with_nested_handlers.phpt new file mode 100644 index 0000000..2b74e5c --- /dev/null +++ b/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/vendor/symfony/debug/composer.json b/vendor/symfony/debug/composer.json new file mode 100644 index 0000000..9b2deeb --- /dev/null +++ b/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": "^7.1.3", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": "<3.4" + }, + "require-dev": { + "symfony/http-kernel": "~3.4|~4.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Debug\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/debug/phpunit.xml.dist b/vendor/symfony/debug/phpunit.xml.dist new file mode 100644 index 0000000..12e5861 --- /dev/null +++ b/vendor/symfony/debug/phpunit.xml.dist @@ -0,0 +1,33 @@ + + + + + + + + + + ./Tests/ + + + ./Resources/ext/tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/dependency-injection/.gitignore b/vendor/symfony/dependency-injection/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/dependency-injection/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/dependency-injection/Alias.php b/vendor/symfony/dependency-injection/Alias.php new file mode 100644 index 0000000..24484de --- /dev/null +++ b/vendor/symfony/dependency-injection/Alias.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\DependencyInjection; + +class Alias +{ + private $id; + private $public; + private $private; + + public function __construct(string $id, bool $public = true) + { + $this->id = $id; + $this->public = $public; + $this->private = 2 > \func_num_args(); + } + + /** + * Checks if this DI Alias should be public or not. + * + * @return bool + */ + public function isPublic() + { + return $this->public; + } + + /** + * Sets if this Alias is public. + * + * @param bool $boolean If this Alias should be public + * + * @return $this + */ + public function setPublic($boolean) + { + $this->public = (bool) $boolean; + $this->private = false; + + return $this; + } + + /** + * Sets if this Alias is private. + * + * When set, the "private" state has a higher precedence than "public". + * In version 3.4, a "private" alias always remains publicly accessible, + * but triggers a deprecation notice when accessed from the container, + * so that the alias can be made really private in 4.0. + * + * @param bool $boolean + * + * @return $this + */ + public function setPrivate($boolean) + { + $this->private = (bool) $boolean; + + return $this; + } + + /** + * Whether this alias is private. + * + * @return bool + */ + public function isPrivate() + { + return $this->private; + } + + /** + * Returns the Id of this alias. + * + * @return string The alias id + */ + public function __toString() + { + return $this->id; + } +} diff --git a/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php b/vendor/symfony/dependency-injection/Argument/ArgumentInterface.php new file mode 100644 index 0000000..b46eb77 --- /dev/null +++ b/vendor/symfony/dependency-injection/Argument/ArgumentInterface.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\DependencyInjection\Argument; + +/** + * Represents a complex argument containing nested values. + * + * @author Titouan Galopin + */ +interface ArgumentInterface +{ + /** + * @return array + */ + public function getValues(); + + public function setValues(array $values); +} diff --git a/vendor/symfony/dependency-injection/Argument/BoundArgument.php b/vendor/symfony/dependency-injection/Argument/BoundArgument.php new file mode 100644 index 0000000..f72f211 --- /dev/null +++ b/vendor/symfony/dependency-injection/Argument/BoundArgument.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\DependencyInjection\Argument; + +/** + * @author Guilhem Niot + */ +final class BoundArgument implements ArgumentInterface +{ + private static $sequence = 0; + + private $value; + private $identifier; + private $used; + + public function __construct($value) + { + $this->value = $value; + $this->identifier = ++self::$sequence; + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + return array($this->value, $this->identifier, $this->used); + } + + /** + * {@inheritdoc} + */ + public function setValues(array $values) + { + list($this->value, $this->identifier, $this->used) = $values; + } +} diff --git a/vendor/symfony/dependency-injection/Argument/IteratorArgument.php b/vendor/symfony/dependency-injection/Argument/IteratorArgument.php new file mode 100644 index 0000000..2d796d2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Argument/IteratorArgument.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\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Represents a collection of values to lazily iterate over. + * + * @author Titouan Galopin + */ +class IteratorArgument implements ArgumentInterface +{ + private $values; + + /** + * @param Reference[] $values + */ + public function __construct(array $values) + { + $this->setValues($values); + } + + /** + * @return array The values to lazily iterate over + */ + public function getValues() + { + return $this->values; + } + + /** + * @param Reference[] $values The service references to lazily iterate over + */ + public function setValues(array $values) + { + foreach ($values as $k => $v) { + if (null !== $v && !$v instanceof Reference) { + throw new InvalidArgumentException(sprintf('An IteratorArgument must hold only Reference instances, "%s" given.', \is_object($v) ? \get_class($v) : \gettype($v))); + } + } + + $this->values = $values; + } +} diff --git a/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php b/vendor/symfony/dependency-injection/Argument/RewindableGenerator.php new file mode 100644 index 0000000..f8f771d --- /dev/null +++ b/vendor/symfony/dependency-injection/Argument/RewindableGenerator.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\DependencyInjection\Argument; + +/** + * @internal + */ +class RewindableGenerator implements \IteratorAggregate, \Countable +{ + private $generator; + private $count; + + /** + * @param callable $generator + * @param int|callable $count + */ + public function __construct(callable $generator, $count) + { + $this->generator = $generator; + $this->count = $count; + } + + public function getIterator() + { + $g = $this->generator; + + return $g(); + } + + public function count() + { + if (\is_callable($count = $this->count)) { + $this->count = $count(); + } + + return $this->count; + } +} diff --git a/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php b/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.php new file mode 100644 index 0000000..2fec5d2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Argument/ServiceClosureArgument.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\DependencyInjection\Argument; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Represents a service wrapped in a memoizing closure. + * + * @author Nicolas Grekas + */ +class ServiceClosureArgument implements ArgumentInterface +{ + private $values; + + public function __construct(Reference $reference) + { + $this->values = array($reference); + } + + /** + * {@inheritdoc} + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function setValues(array $values) + { + if (array(0) !== array_keys($values) || !($values[0] instanceof Reference || null === $values[0])) { + throw new InvalidArgumentException('A ServiceClosureArgument must hold one and only one Reference.'); + } + + $this->values = $values; + } +} diff --git a/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php b/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.php new file mode 100644 index 0000000..3a6fab8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Argument/TaggedIteratorArgument.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\DependencyInjection\Argument; + +/** + * Represents a collection of services found by tag name to lazily iterate over. + * + * @author Roland Franssen + */ +class TaggedIteratorArgument extends IteratorArgument +{ + private $tag; + + public function __construct(string $tag) + { + parent::__construct(array()); + + $this->tag = $tag; + } + + public function getTag() + { + return $this->tag; + } +} diff --git a/vendor/symfony/dependency-injection/CHANGELOG.md b/vendor/symfony/dependency-injection/CHANGELOG.md new file mode 100644 index 0000000..43a41fa --- /dev/null +++ b/vendor/symfony/dependency-injection/CHANGELOG.md @@ -0,0 +1,182 @@ +CHANGELOG +========= + +4.0.0 +----- + + * Relying on service auto-registration while autowiring is not supported anymore. + Explicitly inject your dependencies or create services whose ids are + their fully-qualified class name. + + Before: + + ```php + namespace App\Controller; + + use App\Mailer; + + class DefaultController + { + public function __construct(Mailer $mailer) { + // ... + } + + // ... + } + ``` + ```yml + services: + App\Controller\DefaultController: + autowire: true + ``` + + After: + + ```php + // same PHP code + ``` + ```yml + services: + App\Controller\DefaultController: + autowire: true + + # or + # App\Controller\DefaultController: + # arguments: { $mailer: "@App\Mailer" } + + App\Mailer: + autowire: true + ``` + * removed autowiring services based on the types they implement + * added a third `$methodName` argument to the `getProxyFactoryCode()` method + of the `DumperInterface` + * removed support for autowiring types + * removed `Container::isFrozen` + * removed support for dumping an ucompiled container in `PhpDumper` + * removed support for generating a dumped `Container` without populating the method map + * removed support for case insensitive service identifiers + * removed the `DefinitionDecorator` class, replaced by `ChildDefinition` + * removed the `AutowireServiceResource` class and related `AutowirePass::createResourceForClass()` method + * removed `LoggingFormatter`, `Compiler::getLoggingFormatter()` and `addLogMessage()` class and methods, use the `ContainerBuilder::log()` method instead + * removed `FactoryReturnTypePass` + * removed `ContainerBuilder::addClassResource()`, use the `addObjectResource()` or the `getReflectionClass()` method instead. + * removed support for top-level anonymous services + * removed silent behavior for unused attributes and elements + * removed support for setting and accessing private services in `Container` + * removed support for setting pre-defined services in `Container` + * removed support for case insensitivity of parameter names + * removed `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead + +3.4.0 +----- + + * moved the `ExtensionCompilerPass` to before-optimization passes with priority -1000 + * deprecated "public-by-default" definitions and aliases, the new default will be "private" in 4.0 + * added `EnvVarProcessorInterface` and corresponding "container.env_var_processor" tag for processing env vars + * added support for ignore-on-uninitialized references + * deprecated service auto-registration while autowiring + * deprecated the ability to check for the initialization of a private service with the `Container::initialized()` method + * deprecated support for top-level anonymous services in XML + * deprecated case insensitivity of parameter names + * deprecated the `ResolveDefinitionTemplatesPass` class in favor of `ResolveChildDefinitionsPass` + * added `TaggedIteratorArgument` with YAML (`!tagged foo`) and XML (``) support + * deprecated `AutowireExceptionPass` and `AutowirePass::getAutowiringExceptions()`, use `Definition::addError()` and the `DefinitionErrorExceptionPass` instead + + +3.3.0 +----- + + * deprecated autowiring services based on the types they implement; + rename (or alias) your services to their FQCN id to make them autowirable + * added "ServiceSubscriberInterface" - to allow for per-class explicit service-locator definitions + * added "container.service_locator" tag for defining service-locator services + * added anonymous services support in YAML configuration files using the `!service` tag. + * added "TypedReference" and "ServiceClosureArgument" for creating service-locator services + * added `ServiceLocator` - a PSR-11 container holding a set of services to be lazily loaded + * added "instanceof" section for local interface-defined configs + * added prototype services for PSR4-based discovery and registration + * added `ContainerBuilder::getReflectionClass()` for retrieving and tracking reflection class info + * deprecated `ContainerBuilder::getClassResource()`, use `ContainerBuilder::getReflectionClass()` or `ContainerBuilder::addObjectResource()` instead + * added `ContainerBuilder::fileExists()` for checking and tracking file or directory existence + * deprecated autowiring-types, use aliases instead + * added support for omitting the factory class name in a service definition if the definition class is set + * deprecated case insensitivity of service identifiers + * added "iterator" argument type for lazy iteration over a set of values and services + * added file-wide configurable defaults for service attributes "public", "tags", + "autowire" and "autoconfigure" + * made the "class" attribute optional, using the "id" as fallback + * using the `PhpDumper` with an uncompiled `ContainerBuilder` is deprecated and + will not be supported anymore in 4.0 + * deprecated the `DefinitionDecorator` class in favor of `ChildDefinition` + * allow config files to be loaded using a glob pattern + * [BC BREAK] the `NullDumper` class is now final + +3.2.0 +----- + + * allowed to prioritize compiler passes by introducing a third argument to `PassConfig::addPass()`, to `Compiler::addPass` and to `ContainerBuilder::addCompilerPass()` + * added support for PHP constants in YAML configuration files + * deprecated the ability to set or unset a private service with the `Container::set()` method + * deprecated the ability to check for the existence of a private service with the `Container::has()` method + * deprecated the ability to request a private service with the `Container::get()` method + * deprecated support for generating a dumped `Container` without populating the method map + +3.0.0 +----- + + * removed all deprecated codes from 2.x versions + +2.8.0 +----- + + * deprecated the abstract ContainerAware class in favor of ContainerAwareTrait + * deprecated IntrospectableContainerInterface, to be merged with ContainerInterface in 3.0 + * allowed specifying a directory to recursively load all configuration files it contains + * deprecated the concept of scopes + * added `Definition::setShared()` and `Definition::isShared()` + * added ResettableContainerInterface to be able to reset the container to release memory on shutdown + * added a way to define the priority of service decoration + * added support for service autowiring + +2.7.0 +----- + + * deprecated synchronized services + +2.6.0 +----- + + * added new factory syntax and deprecated the old one + +2.5.0 +----- + +* added DecoratorServicePass and a way to override a service definition (Definition::setDecoratedService()) +* deprecated SimpleXMLElement class. + +2.4.0 +----- + + * added support for expressions in service definitions + * added ContainerAwareTrait to add default container aware behavior to a class + +2.2.0 +----- + + * added Extension::isConfigEnabled() to ease working with enableable configurations + * added an Extension base class with sensible defaults to be used in conjunction + with the Config component. + * added PrependExtensionInterface (to be able to allow extensions to prepend + application configuration settings for any Bundle) + +2.1.0 +----- + + * added IntrospectableContainerInterface (to be able to check if a service + has been initialized or not) + * added ConfigurationExtensionInterface + * added Definition::clearTag() + * component exceptions that inherit base SPL classes are now used exclusively + (this includes dumped containers) + * [BC BREAK] fixed unescaping of class arguments, method + ParameterBag::unescapeValue() was made public diff --git a/vendor/symfony/dependency-injection/ChildDefinition.php b/vendor/symfony/dependency-injection/ChildDefinition.php new file mode 100644 index 0000000..095beae --- /dev/null +++ b/vendor/symfony/dependency-injection/ChildDefinition.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\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +/** + * This definition extends another definition. + * + * @author Johannes M. Schmitt + */ +class ChildDefinition extends Definition +{ + private $parent; + + /** + * @param string $parent The id of Definition instance to decorate + */ + public function __construct(string $parent) + { + $this->parent = $parent; + $this->setPrivate(false); + } + + /** + * Returns the Definition to inherit from. + * + * @return string + */ + public function getParent() + { + return $this->parent; + } + + /** + * Sets the Definition to inherit from. + * + * @param string $parent + * + * @return $this + */ + public function setParent($parent) + { + $this->parent = $parent; + + return $this; + } + + /** + * Gets an argument to pass to the service constructor/factory method. + * + * If replaceArgument() has been used to replace an argument, this method + * will return the replacement value. + * + * @param int|string $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + */ + public function getArgument($index) + { + if (array_key_exists('index_'.$index, $this->arguments)) { + return $this->arguments['index_'.$index]; + } + + return parent::getArgument($index); + } + + /** + * You should always use this method when overwriting existing arguments + * of the parent definition. + * + * If you directly call setArguments() keep in mind that you must follow + * certain conventions when you want to overwrite the arguments of the + * parent definition, otherwise your arguments will only be appended. + * + * @param int|string $index + * @param mixed $value + * + * @return self the current instance + * + * @throws InvalidArgumentException when $index isn't an integer + */ + public function replaceArgument($index, $value) + { + if (\is_int($index)) { + $this->arguments['index_'.$index] = $value; + } elseif (0 === strpos($index, '$')) { + $this->arguments[$index] = $value; + } else { + throw new InvalidArgumentException('The argument must be an existing index or the name of a constructor\'s parameter.'); + } + + return $this; + } + + /** + * @internal + */ + public function setAutoconfigured($autoconfigured) + { + throw new BadMethodCallException('A ChildDefinition cannot be autoconfigured.'); + } + + /** + * @internal + */ + public function setInstanceofConditionals(array $instanceof) + { + throw new BadMethodCallException('A ChildDefinition cannot have instanceof conditionals set on it.'); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php b/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php new file mode 100644 index 0000000..cff09d5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +abstract class AbstractRecursivePass implements CompilerPassInterface +{ + /** + * @var ContainerBuilder + */ + protected $container; + protected $currentId; + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + + try { + $this->processValue($container->getDefinitions(), true); + } finally { + $this->container = null; + } + } + + /** + * Processes a value found in a definition tree. + * + * @param mixed $value + * @param bool $isRoot + * + * @return mixed The processed value + */ + protected function processValue($value, $isRoot = false) + { + if (\is_array($value)) { + foreach ($value as $k => $v) { + if ($isRoot) { + $this->currentId = $k; + } + if ($v !== $processedValue = $this->processValue($v, $isRoot)) { + $value[$k] = $processedValue; + } + } + } elseif ($value instanceof ArgumentInterface) { + $value->setValues($this->processValue($value->getValues())); + } elseif ($value instanceof Definition) { + $value->setArguments($this->processValue($value->getArguments())); + $value->setProperties($this->processValue($value->getProperties())); + $value->setMethodCalls($this->processValue($value->getMethodCalls())); + + $changes = $value->getChanges(); + if (isset($changes['factory'])) { + $value->setFactory($this->processValue($value->getFactory())); + } + if (isset($changes['configurator'])) { + $value->setConfigurator($this->processValue($value->getConfigurator())); + } + } + + return $value; + } + + /** + * @param Definition $definition + * @param bool $required + * + * @return \ReflectionFunctionAbstract|null + * + * @throws RuntimeException + */ + protected function getConstructor(Definition $definition, $required) + { + if (\is_string($factory = $definition->getFactory())) { + if (!\function_exists($factory)) { + throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.', $this->currentId, $factory)); + } + $r = new \ReflectionFunction($factory); + if (false !== $r->getFileName() && file_exists($r->getFileName())) { + $this->container->fileExists($r->getFileName()); + } + + return $r; + } + + if ($factory) { + list($class, $method) = $factory; + if ($class instanceof Reference) { + $class = $this->container->findDefinition((string) $class)->getClass(); + } elseif (null === $class) { + $class = $definition->getClass(); + } + if ('__construct' === $method) { + throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.', $this->currentId)); + } + + return $this->getReflectionMethod(new Definition($class), $method); + } + + $class = $definition->getClass(); + + try { + if (!$r = $this->container->getReflectionClass($class)) { + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); + } + } catch (\ReflectionException $e) { + throw new RuntimeException(sprintf('Invalid service "%s": %s.', $this->currentId, lcfirst(rtrim($e->getMessage(), '.')))); + } + if (!$r = $r->getConstructor()) { + if ($required) { + throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.', $this->currentId, sprintf($class !== $this->currentId ? ' "%s"' : '', $class))); + } + } elseif (!$r->isPublic()) { + throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.', $this->currentId, sprintf($class !== $this->currentId ? 'constructor of class "%s"' : 'its constructor', $class))); + } + + return $r; + } + + /** + * @param Definition $definition + * @param string $method + * + * @throws RuntimeException + * + * @return \ReflectionFunctionAbstract + */ + protected function getReflectionMethod(Definition $definition, $method) + { + if ('__construct' === $method) { + return $this->getConstructor($definition, true); + } + + if (!$class = $definition->getClass()) { + throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.', $this->currentId)); + } + + if (!$r = $this->container->getReflectionClass($class)) { + throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.', $this->currentId, $class)); + } + + if (!$r->hasMethod($method)) { + throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); + } + + $r = $r->getMethod($method); + if (!$r->isPublic()) { + throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method)); + } + + return $r; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php b/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.php new file mode 100644 index 0000000..22976bb --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/AnalyzeServiceReferencesPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * Run this pass before passes that need to know more about the relation of + * your services. + * + * This class will populate the ServiceReferenceGraph with information. You can + * retrieve the graph in other passes from the compiler. + * + * @author Johannes M. Schmitt + */ +class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements RepeatablePassInterface +{ + private $graph; + private $currentDefinition; + private $onlyConstructorArguments; + private $lazy; + private $expressionLanguage; + + /** + * @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls + */ + public function __construct(bool $onlyConstructorArguments = false) + { + $this->onlyConstructorArguments = $onlyConstructorArguments; + } + + /** + * {@inheritdoc} + */ + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + // no-op for BC + } + + /** + * Processes a ContainerBuilder object to populate the service reference graph. + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + $this->graph = $container->getCompiler()->getServiceReferenceGraph(); + $this->graph->clear(); + $this->lazy = false; + + foreach ($container->getAliases() as $id => $alias) { + $targetId = $this->getDefinitionId((string) $alias); + $this->graph->connect($id, $alias, $targetId, $this->getDefinition($targetId), null); + } + + parent::process($container); + } + + protected function processValue($value, $isRoot = false) + { + $lazy = $this->lazy; + + if ($value instanceof ArgumentInterface) { + $this->lazy = true; + parent::processValue($value->getValues()); + $this->lazy = $lazy; + + return $value; + } + if ($value instanceof Expression) { + $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); + + return $value; + } + if ($value instanceof Reference) { + $targetId = $this->getDefinitionId((string) $value); + $targetDefinition = $this->getDefinition($targetId); + + $this->graph->connect( + $this->currentId, + $this->currentDefinition, + $targetId, + $targetDefinition, + $value, + $this->lazy || ($targetDefinition && $targetDefinition->isLazy()), + ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior() + ); + + return $value; + } + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + if ($isRoot) { + if ($value->isSynthetic() || $value->isAbstract()) { + return $value; + } + $this->currentDefinition = $value; + } + $this->lazy = false; + + $this->processValue($value->getFactory()); + $this->processValue($value->getArguments()); + + if (!$this->onlyConstructorArguments) { + $this->processValue($value->getProperties()); + $this->processValue($value->getMethodCalls()); + $this->processValue($value->getConfigurator()); + } + $this->lazy = $lazy; + + return $value; + } + + private function getDefinition(?string $id): ?Definition + { + return null === $id ? null : $this->container->getDefinition($id); + } + + private function getDefinitionId(string $id): ?string + { + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + + if (!$this->container->hasDefinition($id)) { + return null; + } + + return $id; + } + + private function getExpressionLanguage() + { + if (null === $this->expressionLanguage) { + if (!class_exists(ExpressionLanguage::class)) { + throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); + } + + $providers = $this->container->getExpressionLanguageProviders(); + $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { + if ('""' === substr_replace($arg, '', 1, -1)) { + $id = stripcslashes(substr($arg, 1, -1)); + $id = $this->getDefinitionId($id); + + $this->graph->connect( + $this->currentId, + $this->currentDefinition, + $id, + $this->getDefinition($id) + ); + } + + return sprintf('$this->get(%s)', $arg); + }); + } + + return $this->expressionLanguage; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php b/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.php new file mode 100644 index 0000000..0342068 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/AutoAliasServicePass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Sets a service to be an alias of another one, given a format pattern. + */ +class AutoAliasServicePass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->findTaggedServiceIds('auto_alias') as $serviceId => $tags) { + foreach ($tags as $tag) { + if (!isset($tag['format'])) { + throw new InvalidArgumentException(sprintf('Missing tag information "format" on auto_alias service "%s".', $serviceId)); + } + + $aliasId = $container->getParameterBag()->resolveValue($tag['format']); + if ($container->hasDefinition($aliasId) || $container->hasAlias($aliasId)) { + $container->setAlias($serviceId, new Alias($aliasId, true)); + } + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/AutowirePass.php b/vendor/symfony/dependency-injection/Compiler/AutowirePass.php new file mode 100644 index 0000000..f737c77 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/AutowirePass.php @@ -0,0 +1,383 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Inspects existing service definitions and wires the autowired ones using the type hints of their classes. + * + * @author Kévin Dunglas + * @author Nicolas Grekas + */ +class AutowirePass extends AbstractRecursivePass +{ + private $types; + private $ambiguousServiceTypes; + private $lastFailure; + private $throwOnAutowiringException; + + public function __construct(bool $throwOnAutowireException = true) + { + $this->throwOnAutowiringException = $throwOnAutowireException; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + try { + parent::process($container); + } finally { + $this->types = null; + $this->ambiguousServiceTypes = null; + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + try { + return $this->doProcessValue($value, $isRoot); + } catch (AutowiringFailedException $e) { + if ($this->throwOnAutowiringException) { + throw $e; + } + + $this->container->getDefinition($this->currentId)->addError($e->getMessage()); + + return parent::processValue($value, $isRoot); + } + } + + private function doProcessValue($value, $isRoot = false) + { + if ($value instanceof TypedReference) { + if ($ref = $this->getAutowiredReference($value)) { + return $ref; + } + $this->container->log($this, $this->createTypeNotFoundMessage($value, 'it')); + } + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + $this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass())); + + return $value; + } + + $methodCalls = $value->getMethodCalls(); + + try { + $constructor = $this->getConstructor($value, false); + } catch (RuntimeException $e) { + throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e); + } + + if ($constructor) { + array_unshift($methodCalls, array($constructor, $value->getArguments())); + } + + $methodCalls = $this->autowireCalls($reflectionClass, $methodCalls); + + if ($constructor) { + list(, $arguments) = array_shift($methodCalls); + + if ($arguments !== $value->getArguments()) { + $value->setArguments($arguments); + } + } + + if ($methodCalls !== $value->getMethodCalls()) { + $value->setMethodCalls($methodCalls); + } + + return $value; + } + + /** + * @param \ReflectionClass $reflectionClass + * @param array $methodCalls + * + * @return array + */ + private function autowireCalls(\ReflectionClass $reflectionClass, array $methodCalls) + { + foreach ($methodCalls as $i => $call) { + list($method, $arguments) = $call; + + if ($method instanceof \ReflectionFunctionAbstract) { + $reflectionMethod = $method; + } else { + $reflectionMethod = $this->getReflectionMethod(new Definition($reflectionClass->name), $method); + } + + $arguments = $this->autowireMethod($reflectionMethod, $arguments); + + if ($arguments !== $call[1]) { + $methodCalls[$i][1] = $arguments; + } + } + + return $methodCalls; + } + + /** + * Autowires the constructor or a method. + * + * @return array The autowired arguments + * + * @throws AutowiringFailedException + */ + private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments): array + { + $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; + $method = $reflectionMethod->name; + $parameters = $reflectionMethod->getParameters(); + if ($reflectionMethod->isVariadic()) { + array_pop($parameters); + } + + foreach ($parameters as $index => $parameter) { + if (array_key_exists($index, $arguments) && '' !== $arguments[$index]) { + continue; + } + + $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true); + + if (!$type) { + if (isset($arguments[$index])) { + continue; + } + + // no default value? Then fail + if (!$parameter->isDefaultValueAvailable()) { + // For core classes, isDefaultValueAvailable() can + // be false when isOptional() returns true. If the + // argument *is* optional, allow it to be missing + if ($parameter->isOptional()) { + continue; + } + $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, false); + $type = $type ? sprintf('is type-hinted "%s"', $type) : 'has no type-hint'; + + throw new AutowiringFailedException($this->currentId, sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method, $type)); + } + + // specifically pass the default value + $arguments[$index] = $parameter->getDefaultValue(); + + continue; + } + + if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, !$parameter->isOptional() ? $class : ''))) { + $failureMessage = $this->createTypeNotFoundMessage($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); + + if ($parameter->isDefaultValueAvailable()) { + $value = $parameter->getDefaultValue(); + } elseif (!$parameter->allowsNull()) { + throw new AutowiringFailedException($this->currentId, $failureMessage); + } + $this->container->log($this, $failureMessage); + } + + $arguments[$index] = $value; + } + + if ($parameters && !isset($arguments[++$index])) { + while (0 <= --$index) { + $parameter = $parameters[$index]; + if (!$parameter->isDefaultValueAvailable() || $parameter->getDefaultValue() !== $arguments[$index]) { + break; + } + unset($arguments[$index]); + } + } + + // it's possible index 1 was set, then index 0, then 2, etc + // make sure that we re-order so they're injected as expected + ksort($arguments); + + return $arguments; + } + + /** + * @return TypedReference|null A reference to the service matching the given type, if any + */ + private function getAutowiredReference(TypedReference $reference) + { + $this->lastFailure = null; + $type = $reference->getType(); + + if ($type !== (string) $reference || ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract())) { + return $reference; + } + } + + /** + * Populates the list of available types. + */ + private function populateAvailableTypes() + { + $this->types = array(); + $this->ambiguousServiceTypes = array(); + + foreach ($this->container->getDefinitions() as $id => $definition) { + $this->populateAvailableType($id, $definition); + } + } + + /** + * Populates the list of available types for a given definition. + */ + private function populateAvailableType(string $id, Definition $definition) + { + // Never use abstract services + if ($definition->isAbstract()) { + return; + } + + if (preg_match('/^\d+_[^~]++~[._a-zA-Z\d]{7}$/', $id) || $definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) { + return; + } + + foreach ($reflectionClass->getInterfaces() as $reflectionInterface) { + $this->set($reflectionInterface->name, $id); + } + + do { + $this->set($reflectionClass->name, $id); + } while ($reflectionClass = $reflectionClass->getParentClass()); + } + + /** + * Associates a type and a service id if applicable. + */ + private function set(string $type, string $id) + { + // is this already a type/class that is known to match multiple services? + if (isset($this->ambiguousServiceTypes[$type])) { + $this->ambiguousServiceTypes[$type][] = $id; + + return; + } + + // check to make sure the type doesn't match multiple services + if (!isset($this->types[$type]) || $this->types[$type] === $id) { + $this->types[$type] = $id; + + return; + } + + // keep an array of all services matching this type + if (!isset($this->ambiguousServiceTypes[$type])) { + $this->ambiguousServiceTypes[$type] = array($this->types[$type]); + unset($this->types[$type]); + } + $this->ambiguousServiceTypes[$type][] = $id; + } + + private function createTypeNotFoundMessage(TypedReference $reference, $label) + { + if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) { + // either $type does not exist or a parent class does not exist + try { + $resource = new ClassExistenceResource($type, false); + // isFresh() will explode ONLY if a parent class/trait does not exist + $resource->isFresh(0); + $parentMsg = false; + } catch (\ReflectionException $e) { + $parentMsg = $e->getMessage(); + } + + $message = sprintf('has type "%s" but this class %s.', $type, $parentMsg ? sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found'); + } else { + $alternatives = $this->createTypeAlternatives($reference); + $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists'; + $message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives); + + if ($r->isInterface() && !$alternatives) { + $message .= ' Did you create a class that implements this interface?'; + } + } + + $message = sprintf('Cannot autowire service "%s": %s %s', $this->currentId, $label, $message); + + if (null !== $this->lastFailure) { + $message = $this->lastFailure."\n".$message; + $this->lastFailure = null; + } + + return $message; + } + + private function createTypeAlternatives(TypedReference $reference) + { + // try suggesting available aliases first + if ($message = $this->getAliasesSuggestionForType($type = $reference->getType())) { + return ' '.$message; + } + if (null === $this->ambiguousServiceTypes) { + $this->populateAvailableTypes(); + } + + $servicesAndAliases = $this->container->getServiceIds(); + if (!$this->container->has($type) && false !== $key = array_search(strtolower($type), array_map('strtolower', $servicesAndAliases))) { + return sprintf(' Did you mean "%s"?', $servicesAndAliases[$key]); + } elseif (isset($this->ambiguousServiceTypes[$type])) { + $message = sprintf('one of these existing services: "%s"', implode('", "', $this->ambiguousServiceTypes[$type])); + } elseif (isset($this->types[$type])) { + $message = sprintf('the existing "%s" service', $this->types[$type]); + } else { + return; + } + + return sprintf(' You should maybe alias this %s to %s.', class_exists($type, false) ? 'class' : 'interface', $message); + } + + private function getAliasesSuggestionForType($type, $extraContext = null) + { + $aliases = array(); + foreach (class_parents($type) + class_implements($type) as $parent) { + if ($this->container->has($parent) && !$this->container->findDefinition($parent)->isAbstract()) { + $aliases[] = $parent; + } + } + + $extraContext = $extraContext ? ' '.$extraContext : ''; + if (1 < $len = \count($aliases)) { + $message = sprintf('Try changing the type-hint%s to one of its parents: ', $extraContext); + for ($i = 0, --$len; $i < $len; ++$i) { + $message .= sprintf('%s "%s", ', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); + } + $message .= sprintf('or %s "%s".', class_exists($aliases[$i], false) ? 'class' : 'interface', $aliases[$i]); + + return $message; + } + + if ($aliases) { + return sprintf('Try changing the type-hint%s to "%s" instead.', $extraContext, $aliases[0]); + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php b/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.php new file mode 100644 index 0000000..6c744f8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/AutowireRequiredMethodsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Looks for definitions with autowiring enabled and registers their corresponding "@required" methods as setters. + * + * @author Nicolas Grekas + */ +class AutowireRequiredMethodsPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + return $value; + } + + $alreadyCalledMethods = array(); + + foreach ($value->getMethodCalls() as list($method)) { + $alreadyCalledMethods[strtolower($method)] = true; + } + + foreach ($reflectionClass->getMethods() as $reflectionMethod) { + $r = $reflectionMethod; + + if ($r->isConstructor() || isset($alreadyCalledMethods[strtolower($r->name)])) { + continue; + } + + while (true) { + if (false !== $doc = $r->getDocComment()) { + if (false !== stripos($doc, '@required') && preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { + $value->addMethodCall($reflectionMethod->name); + break; + } + if (false === stripos($doc, '@inheritdoc') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+(?:\{@inheritdoc\}|@inheritdoc)(?:\s|\*/$)#i', $doc)) { + break; + } + } + try { + $r = $r->getPrototype(); + } catch (\ReflectionException $e) { + break; // method has no prototype + } + } + } + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php b/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.php new file mode 100644 index 0000000..e76e940 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/CheckArgumentsValidityPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Checks if arguments of methods are properly configured. + * + * @author Kévin Dunglas + * @author Nicolas Grekas + */ +class CheckArgumentsValidityPass extends AbstractRecursivePass +{ + private $throwExceptions; + + public function __construct(bool $throwExceptions = true) + { + $this->throwExceptions = $throwExceptions; + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + $i = 0; + foreach ($value->getArguments() as $k => $v) { + if ($k !== $i++) { + if (!\is_int($k)) { + $msg = sprintf('Invalid constructor argument for service "%s": integer expected but found string "%s". Check your service definition.', $this->currentId, $k); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + + break; + } + + $msg = sprintf('Invalid constructor argument %d for service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $this->currentId, $i); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + } + } + + foreach ($value->getMethodCalls() as $methodCall) { + $i = 0; + foreach ($methodCall[1] as $k => $v) { + if ($k !== $i++) { + if (!\is_int($k)) { + $msg = sprintf('Invalid argument for method call "%s" of service "%s": integer expected but found string "%s". Check your service definition.', $methodCall[0], $this->currentId, $k); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + + break; + } + + $msg = sprintf('Invalid argument %d for method call "%s" of service "%s": argument %d must be defined before. Check your service definition.', 1 + $k, $methodCall[0], $this->currentId, $i); + $value->addError($msg); + if ($this->throwExceptions) { + throw new RuntimeException($msg); + } + } + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php b/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php new file mode 100644 index 0000000..ac7866b --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/CheckCircularReferencesPass.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; + +/** + * Checks your services for circular references. + * + * References from method calls are ignored since we might be able to resolve + * these references depending on the order in which services are called. + * + * Circular reference from method calls will only be detected at run-time. + * + * @author Johannes M. Schmitt + */ +class CheckCircularReferencesPass implements CompilerPassInterface +{ + private $currentPath; + private $checkedNodes; + + /** + * Checks the ContainerBuilder object for circular references. + */ + public function process(ContainerBuilder $container) + { + $graph = $container->getCompiler()->getServiceReferenceGraph(); + + $this->checkedNodes = array(); + foreach ($graph->getNodes() as $id => $node) { + $this->currentPath = array($id); + + $this->checkOutEdges($node->getOutEdges()); + } + } + + /** + * Checks for circular references. + * + * @param ServiceReferenceGraphEdge[] $edges An array of Edges + * + * @throws ServiceCircularReferenceException when a circular reference is found + */ + private function checkOutEdges(array $edges) + { + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $id = $node->getId(); + + if (empty($this->checkedNodes[$id])) { + // Don't check circular references for lazy edges + if (!$node->getValue() || (!$edge->isLazy() && !$edge->isWeak())) { + $searchKey = array_search($id, $this->currentPath); + $this->currentPath[] = $id; + + if (false !== $searchKey) { + throw new ServiceCircularReferenceException($id, \array_slice($this->currentPath, $searchKey)); + } + + $this->checkOutEdges($node->getOutEdges()); + } + + $this->checkedNodes[$id] = true; + array_pop($this->currentPath); + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php b/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.php new file mode 100644 index 0000000..7cf4f4a --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/CheckDefinitionValidityPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\EnvParameterException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * This pass validates each definition individually only taking the information + * into account which is contained in the definition itself. + * + * Later passes can rely on the following, and specifically do not need to + * perform these checks themselves: + * + * - non synthetic, non abstract services always have a class set + * - synthetic services are always public + * + * @author Johannes M. Schmitt + */ +class CheckDefinitionValidityPass implements CompilerPassInterface +{ + /** + * Processes the ContainerBuilder to validate the Definition. + * + * @throws RuntimeException When the Definition is invalid + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + // synthetic service is public + if ($definition->isSynthetic() && !$definition->isPublic()) { + throw new RuntimeException(sprintf('A synthetic service ("%s") must be public.', $id)); + } + + // non-synthetic, non-abstract service has class + if (!$definition->isAbstract() && !$definition->isSynthetic() && !$definition->getClass()) { + if ($definition->getFactory()) { + throw new RuntimeException(sprintf('Please add the class to service "%s" even if it is constructed by a factory since we might need to add method calls based on compile-time checks.', $id)); + } + if (class_exists($id) || interface_exists($id, false)) { + if (0 === strpos($id, '\\') && 1 < substr_count($id, '\\')) { + throw new RuntimeException(sprintf( + 'The definition for "%s" has no class attribute, and appears to reference a class or interface. ' + .'Please specify the class attribute explicitly or remove the leading backslash by renaming ' + .'the service to "%s" to get rid of this error.', + $id, substr($id, 1) + )); + } + + throw new RuntimeException(sprintf( + 'The definition for "%s" has no class attribute, and appears to reference a ' + .'class or interface in the global namespace. Leaving out the "class" attribute ' + .'is only allowed for namespaced classes. Please specify the class attribute ' + .'explicitly to get rid of this error.', + $id + )); + } + + throw new RuntimeException(sprintf( + 'The definition for "%s" has no class. If you intend to inject ' + .'this service dynamically at runtime, please mark it as synthetic=true. ' + .'If this is an abstract definition solely used by child definitions, ' + .'please add abstract=true, otherwise specify a class to get rid of this error.', + $id + )); + } + + // tag attribute values must be scalars + foreach ($definition->getTags() as $name => $tags) { + foreach ($tags as $attributes) { + foreach ($attributes as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new RuntimeException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $id, $name, $attribute)); + } + } + } + } + + if ($definition->isPublic() && !$definition->isPrivate()) { + $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs); + if (null !== $usedEnvs) { + throw new EnvParameterException(array($resolvedId), null, 'A service name ("%s") cannot contain dynamic values.'); + } + } + } + + foreach ($container->getAliases() as $id => $alias) { + if ($alias->isPublic() && !$alias->isPrivate()) { + $resolvedId = $container->resolveEnvPlaceholders($id, null, $usedEnvs); + if (null !== $usedEnvs) { + throw new EnvParameterException(array($resolvedId), null, 'An alias name ("%s") cannot contain dynamic values.'); + } + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php b/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php new file mode 100644 index 0000000..77b35f1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Checks that all references are pointing to a valid service. + * + * @author Johannes M. Schmitt + */ +class CheckExceptionOnInvalidReferenceBehaviorPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Reference) { + return parent::processValue($value, $isRoot); + } + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() && !$this->container->has($id = (string) $value)) { + throw new ServiceNotFoundException($id, $this->currentId); + } + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php b/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.php new file mode 100644 index 0000000..323dc6f --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/CheckReferenceValidityPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Checks the validity of references. + * + * The following checks are performed by this pass: + * - target definitions are not abstract + * + * @author Johannes M. Schmitt + */ +class CheckReferenceValidityPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if ($isRoot && $value instanceof Definition && ($value->isSynthetic() || $value->isAbstract())) { + return $value; + } + if ($value instanceof Reference && $this->container->hasDefinition((string) $value)) { + $targetDefinition = $this->container->getDefinition((string) $value); + + if ($targetDefinition->isAbstract()) { + throw new RuntimeException(sprintf( + 'The definition "%s" has a reference to an abstract definition "%s". ' + .'Abstract definitions cannot be the target of references.', + $this->currentId, + $value + )); + } + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/Compiler.php b/vendor/symfony/dependency-injection/Compiler/Compiler.php new file mode 100644 index 0000000..b102e70 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/Compiler.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\EnvParameterException; + +/** + * This class is used to remove circular dependencies between individual passes. + * + * @author Johannes M. Schmitt + */ +class Compiler +{ + private $passConfig; + private $log = array(); + private $serviceReferenceGraph; + + public function __construct() + { + $this->passConfig = new PassConfig(); + $this->serviceReferenceGraph = new ServiceReferenceGraph(); + } + + /** + * Returns the PassConfig. + * + * @return PassConfig The PassConfig instance + */ + public function getPassConfig() + { + return $this->passConfig; + } + + /** + * Returns the ServiceReferenceGraph. + * + * @return ServiceReferenceGraph The ServiceReferenceGraph instance + */ + public function getServiceReferenceGraph() + { + return $this->serviceReferenceGraph; + } + + /** + * Adds a pass to the PassConfig. + * + * @param CompilerPassInterface $pass A compiler pass + * @param string $type The type of the pass + * @param int $priority Used to sort the passes + */ + public function addPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) + { + $this->passConfig->addPass($pass, $type, $priority); + } + + /** + * @final + */ + public function log(CompilerPassInterface $pass, string $message) + { + if (false !== strpos($message, "\n")) { + $message = str_replace("\n", "\n".\get_class($pass).': ', trim($message)); + } + + $this->log[] = \get_class($pass).': '.$message; + } + + /** + * Returns the log. + * + * @return array Log array + */ + public function getLog() + { + return $this->log; + } + + /** + * Run the Compiler and process all Passes. + */ + public function compile(ContainerBuilder $container) + { + try { + foreach ($this->passConfig->getPasses() as $pass) { + $pass->process($container); + } + } catch (\Exception $e) { + $usedEnvs = array(); + $prev = $e; + + do { + $msg = $prev->getMessage(); + + if ($msg !== $resolvedMsg = $container->resolveEnvPlaceholders($msg, null, $usedEnvs)) { + $r = new \ReflectionProperty($prev, 'message'); + $r->setAccessible(true); + $r->setValue($prev, $resolvedMsg); + } + } while ($prev = $prev->getPrevious()); + + if ($usedEnvs) { + $e = new EnvParameterException($usedEnvs, $e); + } + + throw $e; + } finally { + $this->getServiceReferenceGraph()->clear(); + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php b/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.php new file mode 100644 index 0000000..3085006 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/CompilerPassInterface.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Interface that must be implemented by compilation passes. + * + * @author Johannes M. Schmitt + */ +interface CompilerPassInterface +{ + /** + * You can modify the container here before it is dumped to PHP code. + */ + public function process(ContainerBuilder $container); +} diff --git a/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php b/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.php new file mode 100644 index 0000000..39731f4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/DecoratorServicePass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Overwrites a service but keeps the overridden one. + * + * @author Christophe Coevoet + * @author Fabien Potencier + * @author Diego Saint Esteben + */ +class DecoratorServicePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $definitions = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + + foreach ($container->getDefinitions() as $id => $definition) { + if (!$decorated = $definition->getDecoratedService()) { + continue; + } + $definitions->insert(array($id, $definition), array($decorated[2], --$order)); + } + + foreach ($definitions as list($id, $definition)) { + list($inner, $renamedId) = $definition->getDecoratedService(); + + $definition->setDecoratedService(null); + + if (!$renamedId) { + $renamedId = $id.'.inner'; + } + + // we create a new alias/service for the service we are replacing + // to be able to reference it in the new one + if ($container->hasAlias($inner)) { + $alias = $container->getAlias($inner); + $public = $alias->isPublic(); + $private = $alias->isPrivate(); + $container->setAlias($renamedId, new Alias((string) $alias, false)); + } else { + $decoratedDefinition = $container->getDefinition($inner); + $definition->setTags(array_merge($decoratedDefinition->getTags(), $definition->getTags())); + $public = $decoratedDefinition->isPublic(); + $private = $decoratedDefinition->isPrivate(); + $decoratedDefinition->setPublic(false); + $decoratedDefinition->setTags(array()); + $container->setDefinition($renamedId, $decoratedDefinition); + } + + $container->setAlias($inner, $id)->setPublic($public)->setPrivate($private); + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php b/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.php new file mode 100644 index 0000000..73b5d1d --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/DefinitionErrorExceptionPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Throws an exception for any Definitions that have errors and still exist. + * + * @author Ryan Weaver + */ +class DefinitionErrorExceptionPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition || empty($value->getErrors())) { + return parent::processValue($value, $isRoot); + } + + // only show the first error so the user can focus on it + $errors = $value->getErrors(); + $message = reset($errors); + + throw new RuntimeException($message); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php b/vendor/symfony/dependency-injection/Compiler/ExtensionCompilerPass.php new file mode 100644 index 0000000..27e5048 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ExtensionCompilerPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * A pass to automatically process extensions if they implement + * CompilerPassInterface. + * + * @author Wouter J + */ +class ExtensionCompilerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getExtensions() as $extension) { + if (!$extension instanceof CompilerPassInterface) { + continue; + } + + $extension->process($container); + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php b/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.php new file mode 100644 index 0000000..4a0d9f2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/InlineServiceDefinitionsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Inline service definitions where this is possible. + * + * @author Johannes M. Schmitt + */ +class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface +{ + private $cloningIds = array(); + + /** + * {@inheritdoc} + */ + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + // no-op for BC + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if ($value instanceof ArgumentInterface) { + // Reference found in ArgumentInterface::getValues() are not inlineable + return $value; + } + + if ($value instanceof Definition && $this->cloningIds) { + if ($value->isShared()) { + return $value; + } + $value = clone $value; + } + + if (!$value instanceof Reference || !$this->container->hasDefinition($id = (string) $value)) { + return parent::processValue($value, $isRoot); + } + + $definition = $this->container->getDefinition($id); + + if (!$this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { + return $value; + } + + $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); + + if ($definition->isShared()) { + return $definition; + } + + if (isset($this->cloningIds[$id])) { + $ids = array_keys($this->cloningIds); + $ids[] = $id; + + throw new ServiceCircularReferenceException($id, \array_slice($ids, array_search($id, $ids))); + } + + $this->cloningIds[$id] = true; + try { + return $this->processValue($definition); + } finally { + unset($this->cloningIds[$id]); + } + } + + /** + * Checks if the definition is inlineable. + * + * @return bool If the definition is inlineable + */ + private function isInlineableDefinition($id, Definition $definition, ServiceReferenceGraph $graph) + { + if ($definition->getErrors() || $definition->isDeprecated() || $definition->isLazy() || $definition->isSynthetic()) { + return false; + } + + if (!$definition->isShared()) { + return true; + } + + if ($definition->isPublic()) { + return false; + } + + if (!$graph->hasNode($id)) { + return true; + } + + if ($this->currentId == $id) { + return false; + } + + $ids = array(); + foreach ($graph->getNode($id)->getInEdges() as $edge) { + if ($edge->isWeak()) { + return false; + } + $ids[] = $edge->getSourceNode()->getId(); + } + + if (\count(array_unique($ids)) > 1) { + return false; + } + + if (\count($ids) > 1 && \is_array($factory = $definition->getFactory()) && ($factory[0] instanceof Reference || $factory[0] instanceof Definition)) { + return false; + } + + return !$ids || $this->container->getDefinition($ids[0])->isShared(); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php b/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php new file mode 100644 index 0000000..cdd4e3f --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/MergeExtensionConfigurationPass.php @@ -0,0 +1,202 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; + +/** + * Merges extension configs into the container builder. + * + * @author Fabien Potencier + */ +class MergeExtensionConfigurationPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $parameters = $container->getParameterBag()->all(); + $definitions = $container->getDefinitions(); + $aliases = $container->getAliases(); + $exprLangProviders = $container->getExpressionLanguageProviders(); + + foreach ($container->getExtensions() as $extension) { + if ($extension instanceof PrependExtensionInterface) { + $extension->prepend($container); + } + } + + foreach ($container->getExtensions() as $name => $extension) { + if (!$config = $container->getExtensionConfig($name)) { + // this extension was not called + continue; + } + $resolvingBag = $container->getParameterBag(); + if ($resolvingBag instanceof EnvPlaceholderParameterBag && $extension instanceof Extension) { + // create a dedicated bag so that we can track env vars per-extension + $resolvingBag = new MergeExtensionConfigurationParameterBag($resolvingBag); + } + $config = $resolvingBag->resolveValue($config); + + try { + $tmpContainer = new MergeExtensionConfigurationContainerBuilder($extension, $resolvingBag); + $tmpContainer->setResourceTracking($container->isTrackingResources()); + $tmpContainer->addObjectResource($extension); + if ($extension instanceof ConfigurationExtensionInterface && null !== $configuration = $extension->getConfiguration($config, $tmpContainer)) { + $tmpContainer->addObjectResource($configuration); + } + + foreach ($exprLangProviders as $provider) { + $tmpContainer->addExpressionLanguageProvider($provider); + } + + $extension->load($config, $tmpContainer); + } catch (\Exception $e) { + if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { + $container->getParameterBag()->mergeEnvPlaceholders($resolvingBag); + } + + throw $e; + } + + if ($resolvingBag instanceof MergeExtensionConfigurationParameterBag) { + // don't keep track of env vars that are *overridden* when configs are merged + $resolvingBag->freezeAfterProcessing($extension, $tmpContainer); + } + + $container->merge($tmpContainer); + $container->getParameterBag()->add($parameters); + } + + $container->addDefinitions($definitions); + $container->addAliases($aliases); + } +} + +/** + * @internal + */ +class MergeExtensionConfigurationParameterBag extends EnvPlaceholderParameterBag +{ + private $processedEnvPlaceholders; + + public function __construct(parent $parameterBag) + { + parent::__construct($parameterBag->all()); + $this->mergeEnvPlaceholders($parameterBag); + } + + public function freezeAfterProcessing(Extension $extension, ContainerBuilder $container) + { + if (!$config = $extension->getProcessedConfigs()) { + // Extension::processConfiguration() wasn't called, we cannot know how configs were merged + return; + } + $this->processedEnvPlaceholders = array(); + + // serialize config and container to catch env vars nested in object graphs + $config = serialize($config).serialize($container->getDefinitions()).serialize($container->getAliases()).serialize($container->getParameterBag()->all()); + + foreach (parent::getEnvPlaceholders() as $env => $placeholders) { + foreach ($placeholders as $placeholder) { + if (false !== stripos($config, $placeholder)) { + $this->processedEnvPlaceholders[$env] = $placeholders; + break; + } + } + } + } + + /** + * {@inheritdoc} + */ + public function getEnvPlaceholders() + { + return null !== $this->processedEnvPlaceholders ? $this->processedEnvPlaceholders : parent::getEnvPlaceholders(); + } +} + +/** + * A container builder preventing using methods that wouldn't have any effect from extensions. + * + * @internal + */ +class MergeExtensionConfigurationContainerBuilder extends ContainerBuilder +{ + private $extensionClass; + + public function __construct(ExtensionInterface $extension, ParameterBagInterface $parameterBag = null) + { + parent::__construct($parameterBag); + + $this->extensionClass = \get_class($extension); + } + + /** + * {@inheritdoc} + */ + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) + { + throw new LogicException(sprintf('You cannot add compiler pass "%s" from extension "%s". Compiler passes must be registered before the container is compiled.', \get_class($pass), $this->extensionClass)); + } + + /** + * {@inheritdoc} + */ + public function registerExtension(ExtensionInterface $extension) + { + throw new LogicException(sprintf('You cannot register extension "%s" from "%s". Extensions must be registered before the container is compiled.', \get_class($extension), $this->extensionClass)); + } + + /** + * {@inheritdoc} + */ + public function compile(bool $resolveEnvPlaceholders = false) + { + throw new LogicException(sprintf('Cannot compile the container in extension "%s".', $this->extensionClass)); + } + + /** + * {@inheritdoc} + */ + public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) + { + if (true !== $format || !\is_string($value)) { + return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); + } + + $bag = $this->getParameterBag(); + $value = $bag->resolveValue($value); + + foreach ($bag->getEnvPlaceholders() as $env => $placeholders) { + if (false === strpos($env, ':')) { + continue; + } + foreach ($placeholders as $placeholder) { + if (false !== stripos($value, $placeholder)) { + throw new RuntimeException(sprintf('Using a cast in "env(%s)" is incompatible with resolution at compile time in "%s". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead.', $env, $this->extensionClass)); + } + } + } + + return parent::resolveEnvPlaceholders($value, $format, $usedEnvs); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/PassConfig.php b/vendor/symfony/dependency-injection/Compiler/PassConfig.php new file mode 100644 index 0000000..b6c2a7c --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/PassConfig.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Compiler Pass Configuration. + * + * This class has a default configuration embedded. + * + * @author Johannes M. Schmitt + */ +class PassConfig +{ + const TYPE_AFTER_REMOVING = 'afterRemoving'; + const TYPE_BEFORE_OPTIMIZATION = 'beforeOptimization'; + const TYPE_BEFORE_REMOVING = 'beforeRemoving'; + const TYPE_OPTIMIZE = 'optimization'; + const TYPE_REMOVE = 'removing'; + + private $mergePass; + private $afterRemovingPasses = array(); + private $beforeOptimizationPasses = array(); + private $beforeRemovingPasses = array(); + private $optimizationPasses; + private $removingPasses; + + public function __construct() + { + $this->mergePass = new MergeExtensionConfigurationPass(); + + $this->beforeOptimizationPasses = array( + 100 => array( + new ResolveClassPass(), + new ResolveInstanceofConditionalsPass(), + new RegisterEnvVarProcessorsPass(), + ), + -1000 => array(new ExtensionCompilerPass()), + ); + + $this->optimizationPasses = array(array( + new ResolveChildDefinitionsPass(), + new ServiceLocatorTagPass(), + new DecoratorServicePass(), + new ResolveParameterPlaceHoldersPass(false), + new ResolveFactoryClassPass(), + new CheckDefinitionValidityPass(), + new RegisterServiceSubscribersPass(), + new ResolveNamedArgumentsPass(), + new AutowireRequiredMethodsPass(), + new ResolveBindingsPass(), + new AutowirePass(false), + new ResolveTaggedIteratorArgumentPass(), + new ResolveServiceSubscribersPass(), + new ResolveReferencesToAliasesPass(), + new ResolveInvalidReferencesPass(), + new AnalyzeServiceReferencesPass(true), + new CheckCircularReferencesPass(), + new CheckReferenceValidityPass(), + new CheckArgumentsValidityPass(false), + )); + + $this->beforeRemovingPasses = array( + -100 => array( + new ResolvePrivatesPass(), + ), + ); + + $this->removingPasses = array(array( + new RemovePrivateAliasesPass(), + new ReplaceAliasByActualDefinitionPass(), + new RemoveAbstractDefinitionsPass(), + new RepeatedPass(array( + new AnalyzeServiceReferencesPass(), + new InlineServiceDefinitionsPass(), + new AnalyzeServiceReferencesPass(), + new RemoveUnusedDefinitionsPass(), + )), + new DefinitionErrorExceptionPass(), + new CheckExceptionOnInvalidReferenceBehaviorPass(), + new ResolveHotPathPass(), + )); + } + + /** + * Returns all passes in order to be processed. + * + * @return CompilerPassInterface[] + */ + public function getPasses() + { + return array_merge( + array($this->mergePass), + $this->getBeforeOptimizationPasses(), + $this->getOptimizationPasses(), + $this->getBeforeRemovingPasses(), + $this->getRemovingPasses(), + $this->getAfterRemovingPasses() + ); + } + + /** + * Adds a pass. + * + * @param CompilerPassInterface $pass A Compiler pass + * @param string $type The pass type + * @param int $priority Used to sort the passes + * + * @throws InvalidArgumentException when a pass type doesn't exist + */ + public function addPass(CompilerPassInterface $pass, $type = self::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) + { + $property = $type.'Passes'; + if (!isset($this->$property)) { + throw new InvalidArgumentException(sprintf('Invalid type "%s".', $type)); + } + + $passes = &$this->$property; + + if (!isset($passes[$priority])) { + $passes[$priority] = array(); + } + $passes[$priority][] = $pass; + } + + /** + * Gets all passes for the AfterRemoving pass. + * + * @return CompilerPassInterface[] + */ + public function getAfterRemovingPasses() + { + return $this->sortPasses($this->afterRemovingPasses); + } + + /** + * Gets all passes for the BeforeOptimization pass. + * + * @return CompilerPassInterface[] + */ + public function getBeforeOptimizationPasses() + { + return $this->sortPasses($this->beforeOptimizationPasses); + } + + /** + * Gets all passes for the BeforeRemoving pass. + * + * @return CompilerPassInterface[] + */ + public function getBeforeRemovingPasses() + { + return $this->sortPasses($this->beforeRemovingPasses); + } + + /** + * Gets all passes for the Optimization pass. + * + * @return CompilerPassInterface[] + */ + public function getOptimizationPasses() + { + return $this->sortPasses($this->optimizationPasses); + } + + /** + * Gets all passes for the Removing pass. + * + * @return CompilerPassInterface[] + */ + public function getRemovingPasses() + { + return $this->sortPasses($this->removingPasses); + } + + /** + * Gets the Merge pass. + * + * @return CompilerPassInterface + */ + public function getMergePass() + { + return $this->mergePass; + } + + public function setMergePass(CompilerPassInterface $pass) + { + $this->mergePass = $pass; + } + + /** + * Sets the AfterRemoving passes. + * + * @param CompilerPassInterface[] $passes + */ + public function setAfterRemovingPasses(array $passes) + { + $this->afterRemovingPasses = array($passes); + } + + /** + * Sets the BeforeOptimization passes. + * + * @param CompilerPassInterface[] $passes + */ + public function setBeforeOptimizationPasses(array $passes) + { + $this->beforeOptimizationPasses = array($passes); + } + + /** + * Sets the BeforeRemoving passes. + * + * @param CompilerPassInterface[] $passes + */ + public function setBeforeRemovingPasses(array $passes) + { + $this->beforeRemovingPasses = array($passes); + } + + /** + * Sets the Optimization passes. + * + * @param CompilerPassInterface[] $passes + */ + public function setOptimizationPasses(array $passes) + { + $this->optimizationPasses = array($passes); + } + + /** + * Sets the Removing passes. + * + * @param CompilerPassInterface[] $passes + */ + public function setRemovingPasses(array $passes) + { + $this->removingPasses = array($passes); + } + + /** + * Sort passes by priority. + * + * @param array $passes CompilerPassInterface instances with their priority as key + * + * @return CompilerPassInterface[] + */ + private function sortPasses(array $passes) + { + if (0 === \count($passes)) { + return array(); + } + + krsort($passes); + + // Flatten the array + return \call_user_func_array('array_merge', $passes); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php b/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.php new file mode 100644 index 0000000..ed9b0b7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/PriorityTaggedServiceTrait.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Trait that allows a generic method to find and sort service by priority option in the tag. + * + * @author Iltar van der Berg + */ +trait PriorityTaggedServiceTrait +{ + /** + * Finds all services with the given tag name and order them by their priority. + * + * The order of additions must be respected for services having the same priority, + * and knowing that the \SplPriorityQueue class does not respect the FIFO method, + * we should not use that class. + * + * @see https://bugs.php.net/bug.php?id=53710 + * @see https://bugs.php.net/bug.php?id=60926 + * + * @param string $tagName + * @param ContainerBuilder $container + * + * @return Reference[] + */ + private function findAndSortTaggedServices($tagName, ContainerBuilder $container) + { + $services = array(); + + foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $services[$priority][] = new Reference($serviceId); + } + + if ($services) { + krsort($services); + $services = \call_user_func_array('array_merge', $services); + } + + return $services; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php b/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php new file mode 100644 index 0000000..e206b0c --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RegisterEnvVarProcessorsPass.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\EnvVarProcessor; +use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; + +/** + * Creates the container.env_var_processors_locator service. + * + * @author Nicolas Grekas + */ +class RegisterEnvVarProcessorsPass implements CompilerPassInterface +{ + private static $allowedTypes = array('array', 'bool', 'float', 'int', 'string'); + + public function process(ContainerBuilder $container) + { + $bag = $container->getParameterBag(); + $types = array(); + $processors = array(); + foreach ($container->findTaggedServiceIds('container.env_var_processor') as $id => $tags) { + if (!$r = $container->getReflectionClass($class = $container->getDefinition($id)->getClass())) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } elseif (!$r->isSubclassOf(EnvVarProcessorInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EnvVarProcessorInterface::class)); + } + foreach ($class::getProvidedTypes() as $prefix => $type) { + $processors[$prefix] = new ServiceClosureArgument(new Reference($id)); + $types[$prefix] = self::validateProvidedTypes($type, $class); + } + } + + if ($bag instanceof EnvPlaceholderParameterBag) { + foreach (EnvVarProcessor::getProvidedTypes() as $prefix => $type) { + if (!isset($types[$prefix])) { + $types[$prefix] = self::validateProvidedTypes($type, EnvVarProcessor::class); + } + } + $bag->setProvidedTypes($types); + } + + if ($processors) { + $container->register('container.env_var_processors_locator', ServiceLocator::class) + ->setPublic(true) + ->setArguments(array($processors)) + ; + } + } + + private static function validateProvidedTypes($types, $class) + { + $types = explode('|', $types); + + foreach ($types as $type) { + if (!\in_array($type, self::$allowedTypes)) { + throw new InvalidArgumentException(sprintf('Invalid type "%s" returned by "%s::getProvidedTypes()", expected one of "%s".', $type, $class, implode('", "', self::$allowedTypes))); + } + } + + return $types; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php b/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php new file mode 100644 index 0000000..276e65e --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RegisterServiceSubscribersPass.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Compiler pass to register tagged services that require a service locator. + * + * @author Nicolas Grekas + */ +class RegisterServiceSubscribersPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition || $value->isAbstract() || $value->isSynthetic() || !$value->hasTag('container.service_subscriber')) { + return parent::processValue($value, $isRoot); + } + + $serviceMap = array(); + $autowire = $value->isAutowired(); + + foreach ($value->getTag('container.service_subscriber') as $attributes) { + if (!$attributes) { + $autowire = true; + continue; + } + ksort($attributes); + if (array() !== array_diff(array_keys($attributes), array('id', 'key'))) { + throw new InvalidArgumentException(sprintf('The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "%s" given for service "%s".', implode('", "', array_keys($attributes)), $this->currentId)); + } + if (!array_key_exists('id', $attributes)) { + throw new InvalidArgumentException(sprintf('Missing "id" attribute on "container.service_subscriber" tag with key="%s" for service "%s".', $attributes['key'], $this->currentId)); + } + if (!array_key_exists('key', $attributes)) { + $attributes['key'] = $attributes['id']; + } + if (isset($serviceMap[$attributes['key']])) { + continue; + } + $serviceMap[$attributes['key']] = new Reference($attributes['id']); + } + $class = $value->getClass(); + + if (!$r = $this->container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $this->currentId)); + } + if (!$r->isSubclassOf(ServiceSubscriberInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $this->currentId, ServiceSubscriberInterface::class)); + } + $class = $r->name; + + $subscriberMap = array(); + $declaringClass = (new \ReflectionMethod($class, 'getSubscribedServices'))->class; + + foreach ($class::getSubscribedServices() as $key => $type) { + if (!\is_string($type) || !preg_match('/^\??[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $type)) { + throw new InvalidArgumentException(sprintf('"%s::getSubscribedServices()" must return valid PHP types for service "%s" key "%s", "%s" returned.', $class, $this->currentId, $key, \is_string($type) ? $type : \gettype($type))); + } + if ($optionalBehavior = '?' === $type[0]) { + $type = substr($type, 1); + $optionalBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } + if (\is_int($key)) { + $key = $type; + } + if (!isset($serviceMap[$key])) { + if (!$autowire) { + throw new InvalidArgumentException(sprintf('Service "%s" misses a "container.service_subscriber" tag with "key"/"id" attributes corresponding to entry "%s" as returned by "%s::getSubscribedServices()".', $this->currentId, $key, $class)); + } + $serviceMap[$key] = new Reference($type); + } + + $subscriberMap[$key] = new TypedReference((string) $serviceMap[$key], $type, $declaringClass, $optionalBehavior ?: ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); + unset($serviceMap[$key]); + } + + if ($serviceMap = array_keys($serviceMap)) { + $message = sprintf(1 < \count($serviceMap) ? 'keys "%s" do' : 'key "%s" does', str_replace('%', '%%', implode('", "', $serviceMap))); + throw new InvalidArgumentException(sprintf('Service %s not exist in the map returned by "%s::getSubscribedServices()" for service "%s".', $message, $class, $this->currentId)); + } + + $value->addTag('container.service_subscriber.locator', array('id' => (string) ServiceLocatorTagPass::register($this->container, $subscriberMap, $this->currentId))); + + return parent::processValue($value); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php b/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.php new file mode 100644 index 0000000..04b6852 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RemoveAbstractDefinitionsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Removes abstract Definitions. + */ +class RemoveAbstractDefinitionsPass implements CompilerPassInterface +{ + /** + * Removes abstract definitions from the ContainerBuilder. + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isAbstract()) { + $container->removeDefinition($id); + $container->log($this, sprintf('Removed service "%s"; reason: abstract.', $id)); + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php b/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.php new file mode 100644 index 0000000..75b36d2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RemovePrivateAliasesPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Remove private aliases from the container. They were only used to establish + * dependencies between services, and these dependencies have been resolved in + * one of the previous passes. + * + * @author Johannes M. Schmitt + */ +class RemovePrivateAliasesPass implements CompilerPassInterface +{ + /** + * Removes private aliases from the ContainerBuilder. + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getAliases() as $id => $alias) { + if ($alias->isPublic()) { + continue; + } + + $container->removeAlias($id); + $container->log($this, sprintf('Removed service "%s"; reason: private alias.', $id)); + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php b/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.php new file mode 100644 index 0000000..bd26fdb --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RemoveUnusedDefinitionsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Removes unused service definitions from the container. + * + * @author Johannes M. Schmitt + */ +class RemoveUnusedDefinitionsPass implements RepeatablePassInterface +{ + private $repeatedPass; + + /** + * {@inheritdoc} + */ + public function setRepeatedPass(RepeatedPass $repeatedPass) + { + $this->repeatedPass = $repeatedPass; + } + + /** + * Processes the ContainerBuilder to remove unused definitions. + */ + public function process(ContainerBuilder $container) + { + $graph = $container->getCompiler()->getServiceReferenceGraph(); + + $hasChanged = false; + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isPublic()) { + continue; + } + + if ($graph->hasNode($id)) { + $edges = $graph->getNode($id)->getInEdges(); + $referencingAliases = array(); + $sourceIds = array(); + foreach ($edges as $edge) { + if ($edge->isWeak()) { + continue; + } + $node = $edge->getSourceNode(); + $sourceIds[] = $node->getId(); + + if ($node->isAlias()) { + $referencingAliases[] = $node->getValue(); + } + } + $isReferenced = (\count(array_unique($sourceIds)) - \count($referencingAliases)) > 0; + } else { + $referencingAliases = array(); + $isReferenced = false; + } + + if (1 === \count($referencingAliases) && false === $isReferenced) { + $container->setDefinition((string) reset($referencingAliases), $definition); + $definition->setPublic(!$definition->isPrivate()); + $definition->setPrivate(reset($referencingAliases)->isPrivate()); + $container->removeDefinition($id); + $container->log($this, sprintf('Removed service "%s"; reason: replaces alias %s.', $id, reset($referencingAliases))); + } elseif (0 === \count($referencingAliases) && false === $isReferenced) { + $container->removeDefinition($id); + $container->resolveEnvPlaceholders(serialize($definition)); + $container->log($this, sprintf('Removed service "%s"; reason: unused.', $id)); + $hasChanged = true; + } + } + + if ($hasChanged) { + $this->repeatedPass->setRepeat(); + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.php b/vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.php new file mode 100644 index 0000000..2b88bfb --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RepeatablePassInterface.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\DependencyInjection\Compiler; + +/** + * Interface that must be implemented by passes that are run as part of an + * RepeatedPass. + * + * @author Johannes M. Schmitt + */ +interface RepeatablePassInterface extends CompilerPassInterface +{ + public function setRepeatedPass(RepeatedPass $repeatedPass); +} diff --git a/vendor/symfony/dependency-injection/Compiler/RepeatedPass.php b/vendor/symfony/dependency-injection/Compiler/RepeatedPass.php new file mode 100644 index 0000000..3da1a0d --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/RepeatedPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * A pass that might be run repeatedly. + * + * @author Johannes M. Schmitt + */ +class RepeatedPass implements CompilerPassInterface +{ + /** + * @var bool + */ + private $repeat = false; + + private $passes; + + /** + * @param RepeatablePassInterface[] $passes An array of RepeatablePassInterface objects + * + * @throws InvalidArgumentException when the passes don't implement RepeatablePassInterface + */ + public function __construct(array $passes) + { + foreach ($passes as $pass) { + if (!$pass instanceof RepeatablePassInterface) { + throw new InvalidArgumentException('$passes must be an array of RepeatablePassInterface.'); + } + + $pass->setRepeatedPass($this); + } + + $this->passes = $passes; + } + + /** + * Process the repeatable passes that run more than once. + */ + public function process(ContainerBuilder $container) + { + do { + $this->repeat = false; + foreach ($this->passes as $pass) { + $pass->process($container); + } + } while ($this->repeat); + } + + /** + * Sets if the pass should repeat. + */ + public function setRepeat() + { + $this->repeat = true; + } + + /** + * Returns the passes. + * + * @return RepeatablePassInterface[] An array of RepeatablePassInterface objects + */ + public function getPasses() + { + return $this->passes; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php b/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php new file mode 100644 index 0000000..9a27b37 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Replaces aliases with actual service definitions, effectively removing these + * aliases. + * + * @author Johannes M. Schmitt + */ +class ReplaceAliasByActualDefinitionPass extends AbstractRecursivePass +{ + private $replacements; + + /** + * Process the Container to replace aliases with service definitions. + * + * @throws InvalidArgumentException if the service definition does not exist + */ + public function process(ContainerBuilder $container) + { + // First collect all alias targets that need to be replaced + $seenAliasTargets = array(); + $replacements = array(); + foreach ($container->getAliases() as $definitionId => $target) { + $targetId = (string) $target; + // Special case: leave this target alone + if ('service_container' === $targetId) { + continue; + } + // Check if target needs to be replaces + if (isset($replacements[$targetId])) { + $container->setAlias($definitionId, $replacements[$targetId])->setPublic($target->isPublic())->setPrivate($target->isPrivate()); + } + // No need to process the same target twice + if (isset($seenAliasTargets[$targetId])) { + continue; + } + // Process new target + $seenAliasTargets[$targetId] = true; + try { + $definition = $container->getDefinition($targetId); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Unable to replace alias "%s" with actual definition "%s".', $definitionId, $targetId), null, $e); + } + if ($definition->isPublic()) { + continue; + } + // Remove private definition and schedule for replacement + $definition->setPublic(!$target->isPrivate()); + $definition->setPrivate($target->isPrivate()); + $container->setDefinition($definitionId, $definition); + $container->removeDefinition($targetId); + $replacements[$targetId] = $definitionId; + } + $this->replacements = $replacements; + + parent::process($container); + $this->replacements = array(); + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if ($value instanceof Reference && isset($this->replacements[$referenceId = (string) $value])) { + // Perform the replacement + $newId = $this->replacements[$referenceId]; + $value = new Reference($newId, $value->getInvalidBehavior()); + $this->container->log($this, sprintf('Changed reference of service "%s" previously pointing to "%s" to "%s".', $this->currentId, $referenceId, $newId)); + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.php new file mode 100644 index 0000000..a63b6de --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveBindingsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * @author Guilhem Niot + */ +class ResolveBindingsPass extends AbstractRecursivePass +{ + private $usedBindings = array(); + private $unusedBindings = array(); + private $errorMessages = array(); + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + try { + parent::process($container); + + foreach ($this->unusedBindings as list($key, $serviceId)) { + $message = sprintf('Unused binding "%s" in service "%s".', $key, $serviceId); + if ($this->errorMessages) { + $message .= sprintf("\nCould be related to%s:", 1 < \count($this->errorMessages) ? ' one of' : ''); + } + foreach ($this->errorMessages as $m) { + $message .= "\n - ".$m; + } + throw new InvalidArgumentException($message); + } + } finally { + $this->usedBindings = array(); + $this->unusedBindings = array(); + $this->errorMessages = array(); + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if ($value instanceof TypedReference && $value->getType() === (string) $value) { + // Already checked + $bindings = $this->container->getDefinition($this->currentId)->getBindings(); + + if (isset($bindings[$value->getType()])) { + return $this->getBindingValue($bindings[$value->getType()]); + } + + return parent::processValue($value, $isRoot); + } + + if (!$value instanceof Definition || !$bindings = $value->getBindings()) { + return parent::processValue($value, $isRoot); + } + + foreach ($bindings as $key => $binding) { + list($bindingValue, $bindingId, $used) = $binding->getValues(); + if ($used) { + $this->usedBindings[$bindingId] = true; + unset($this->unusedBindings[$bindingId]); + } elseif (!isset($this->usedBindings[$bindingId])) { + $this->unusedBindings[$bindingId] = array($key, $this->currentId); + } + + if (isset($key[0]) && '$' === $key[0]) { + continue; + } + + if (null !== $bindingValue && !$bindingValue instanceof Reference && !$bindingValue instanceof Definition) { + throw new InvalidArgumentException(sprintf('Invalid value for binding key "%s" for service "%s": expected null, an instance of %s or an instance of %s, %s given.', $key, $this->currentId, Reference::class, Definition::class, \gettype($bindingValue))); + } + } + + if ($value->isAbstract()) { + return parent::processValue($value, $isRoot); + } + + $calls = $value->getMethodCalls(); + + try { + if ($constructor = $this->getConstructor($value, false)) { + $calls[] = array($constructor, $value->getArguments()); + } + } catch (RuntimeException $e) { + $this->errorMessages[] = $e->getMessage(); + $this->container->getDefinition($this->currentId)->addError($e->getMessage()); + + return parent::processValue($value, $isRoot); + } + + foreach ($calls as $i => $call) { + list($method, $arguments) = $call; + + if ($method instanceof \ReflectionFunctionAbstract) { + $reflectionMethod = $method; + } else { + $reflectionMethod = $this->getReflectionMethod($value, $method); + } + + foreach ($reflectionMethod->getParameters() as $key => $parameter) { + if (array_key_exists($key, $arguments) && '' !== $arguments[$key]) { + continue; + } + + if (array_key_exists('$'.$parameter->name, $bindings)) { + $arguments[$key] = $this->getBindingValue($bindings['$'.$parameter->name]); + + continue; + } + + $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true); + + if (!isset($bindings[$typeHint])) { + continue; + } + + $arguments[$key] = $this->getBindingValue($bindings[$typeHint]); + } + + if ($arguments !== $call[1]) { + ksort($arguments); + $calls[$i][1] = $arguments; + } + } + + if ($constructor) { + list(, $arguments) = array_pop($calls); + + if ($arguments !== $value->getArguments()) { + $value->setArguments($arguments); + } + } + + if ($calls !== $value->getMethodCalls()) { + $value->setMethodCalls($calls); + } + + return parent::processValue($value, $isRoot); + } + + private function getBindingValue(BoundArgument $binding) + { + list($bindingValue, $bindingId) = $binding->getValues(); + + $this->usedBindings[$bindingId] = true; + unset($this->unusedBindings[$bindingId]); + + return $bindingValue; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.php new file mode 100644 index 0000000..5913c6e --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveChildDefinitionsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ExceptionInterface; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * This replaces all ChildDefinition instances with their equivalent fully + * merged Definition instance. + * + * @author Johannes M. Schmitt + * @author Nicolas Grekas + */ +class ResolveChildDefinitionsPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + if ($isRoot) { + // yes, we are specifically fetching the definition from the + // container to ensure we are not operating on stale data + $value = $this->container->getDefinition($this->currentId); + } + if ($value instanceof ChildDefinition) { + $value = $this->resolveDefinition($value); + if ($isRoot) { + $this->container->setDefinition($this->currentId, $value); + } + } + + return parent::processValue($value, $isRoot); + } + + /** + * Resolves the definition. + * + * @return Definition + * + * @throws RuntimeException When the definition is invalid + */ + private function resolveDefinition(ChildDefinition $definition) + { + try { + return $this->doResolveDefinition($definition); + } catch (ExceptionInterface $e) { + $r = new \ReflectionProperty($e, 'message'); + $r->setAccessible(true); + $r->setValue($e, sprintf('Service "%s": %s', $this->currentId, $e->getMessage())); + + throw $e; + } + } + + private function doResolveDefinition(ChildDefinition $definition) + { + if (!$this->container->has($parent = $definition->getParent())) { + throw new RuntimeException(sprintf('Parent definition "%s" does not exist.', $parent)); + } + + $parentDef = $this->container->findDefinition($parent); + if ($parentDef instanceof ChildDefinition) { + $id = $this->currentId; + $this->currentId = $parent; + $parentDef = $this->resolveDefinition($parentDef); + $this->container->setDefinition($parent, $parentDef); + $this->currentId = $id; + } + + $this->container->log($this, sprintf('Resolving inheritance for "%s" (parent: %s).', $this->currentId, $parent)); + $def = new Definition(); + + // merge in parent definition + // purposely ignored attributes: abstract, shared, tags, autoconfigured + $def->setClass($parentDef->getClass()); + $def->setArguments($parentDef->getArguments()); + $def->setMethodCalls($parentDef->getMethodCalls()); + $def->setProperties($parentDef->getProperties()); + if ($parentDef->isDeprecated()) { + $def->setDeprecated(true, $parentDef->getDeprecationMessage('%service_id%')); + } + $def->setFactory($parentDef->getFactory()); + $def->setConfigurator($parentDef->getConfigurator()); + $def->setFile($parentDef->getFile()); + $def->setPublic($parentDef->isPublic()); + $def->setLazy($parentDef->isLazy()); + $def->setAutowired($parentDef->isAutowired()); + $def->setChanges($parentDef->getChanges()); + + $def->setBindings($definition->getBindings() + $parentDef->getBindings()); + + // overwrite with values specified in the decorator + $changes = $definition->getChanges(); + if (isset($changes['class'])) { + $def->setClass($definition->getClass()); + } + if (isset($changes['factory'])) { + $def->setFactory($definition->getFactory()); + } + if (isset($changes['configurator'])) { + $def->setConfigurator($definition->getConfigurator()); + } + if (isset($changes['file'])) { + $def->setFile($definition->getFile()); + } + if (isset($changes['public'])) { + $def->setPublic($definition->isPublic()); + } else { + $def->setPrivate($definition->isPrivate() || $parentDef->isPrivate()); + } + if (isset($changes['lazy'])) { + $def->setLazy($definition->isLazy()); + } + if (isset($changes['deprecated'])) { + $def->setDeprecated($definition->isDeprecated(), $definition->getDeprecationMessage('%service_id%')); + } + if (isset($changes['autowired'])) { + $def->setAutowired($definition->isAutowired()); + } + if (isset($changes['shared'])) { + $def->setShared($definition->isShared()); + } + if (isset($changes['decorated_service'])) { + $decoratedService = $definition->getDecoratedService(); + if (null === $decoratedService) { + $def->setDecoratedService($decoratedService); + } else { + $def->setDecoratedService($decoratedService[0], $decoratedService[1], $decoratedService[2]); + } + } + + // merge arguments + foreach ($definition->getArguments() as $k => $v) { + if (is_numeric($k)) { + $def->addArgument($v); + } elseif (0 === strpos($k, 'index_')) { + $def->replaceArgument((int) substr($k, \strlen('index_')), $v); + } else { + $def->setArgument($k, $v); + } + } + + // merge properties + foreach ($definition->getProperties() as $k => $v) { + $def->setProperty($k, $v); + } + + // append method calls + if ($calls = $definition->getMethodCalls()) { + $def->setMethodCalls(array_merge($def->getMethodCalls(), $calls)); + } + + // these attributes are always taken from the child + $def->setAbstract($definition->isAbstract()); + $def->setTags($definition->getTags()); + // autoconfigure is never taken from parent (on purpose) + // and it's not legal on an instanceof + $def->setAutoconfigured($definition->isAutoconfigured()); + + return $def; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.php new file mode 100644 index 0000000..5932472 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveClassPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + */ +class ResolveClassPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isSynthetic() || null !== $definition->getClass()) { + continue; + } + if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) { + if ($definition instanceof ChildDefinition && !class_exists($id)) { + throw new InvalidArgumentException(sprintf('Service definition "%s" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); + } + $definition->setClass($id); + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.php new file mode 100644 index 0000000..9e1edd4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveEnvPlaceholdersPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Replaces env var placeholders by their current values. + */ +class ResolveEnvPlaceholdersPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (\is_string($value)) { + return $this->container->resolveEnvPlaceholders($value, true); + } + if ($value instanceof Definition) { + $changes = $value->getChanges(); + if (isset($changes['class'])) { + $value->setClass($this->container->resolveEnvPlaceholders($value->getClass(), true)); + } + if (isset($changes['file'])) { + $value->setFile($this->container->resolveEnvPlaceholders($value->getFile(), true)); + } + } + + $value = parent::processValue($value, $isRoot); + + if ($value && \is_array($value) && !$isRoot) { + $value = array_combine($this->container->resolveEnvPlaceholders(array_keys($value), true), $value); + } + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.php new file mode 100644 index 0000000..848da7f --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveFactoryClassPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * @author Maxime Steinhausser + */ +class ResolveFactoryClassPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if ($value instanceof Definition && \is_array($factory = $value->getFactory()) && null === $factory[0]) { + if (null === $class = $value->getClass()) { + throw new RuntimeException(sprintf('The "%s" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class?', $this->currentId)); + } + + $factory[0] = $class; + $value->setFactory($factory); + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.php new file mode 100644 index 0000000..38e96d0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveHotPathPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Propagate "container.hot_path" tags to referenced services. + * + * @author Nicolas Grekas + */ +class ResolveHotPathPass extends AbstractRecursivePass +{ + private $tagName; + private $resolvedIds = array(); + + public function __construct($tagName = 'container.hot_path') + { + $this->tagName = $tagName; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + try { + parent::process($container); + $container->getDefinition('service_container')->clearTag($this->tagName); + } finally { + $this->resolvedIds = array(); + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if ($value instanceof ArgumentInterface) { + return $value; + } + if ($value instanceof Definition && $isRoot && (isset($this->resolvedIds[$this->currentId]) || !$value->hasTag($this->tagName) || $value->isDeprecated())) { + return $value->isDeprecated() ? $value->clearTag($this->tagName) : $value; + } + if ($value instanceof Reference && ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE !== $value->getInvalidBehavior() && $this->container->has($id = (string) $value)) { + $definition = $this->container->findDefinition($id); + if (!$definition->hasTag($this->tagName) && !$definition->isDeprecated()) { + $this->resolvedIds[$id] = true; + $definition->addTag($this->tagName); + parent::processValue($definition, false); + } + + return $value; + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.php new file mode 100644 index 0000000..20507cf --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveInstanceofConditionalsPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Applies instanceof conditionals to definitions. + * + * @author Nicolas Grekas + */ +class ResolveInstanceofConditionalsPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getAutoconfiguredInstanceof() as $interface => $definition) { + if ($definition->getArguments()) { + throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines arguments but these are not supported and should be removed.', $interface)); + } + if ($definition->getMethodCalls()) { + throw new InvalidArgumentException(sprintf('Autoconfigured instanceof for type "%s" defines method calls but these are not supported and should be removed.', $interface)); + } + } + + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition instanceof ChildDefinition) { + // don't apply "instanceof" to children: it will be applied to their parent + continue; + } + $container->setDefinition($id, $this->processDefinition($container, $id, $definition)); + } + } + + private function processDefinition(ContainerBuilder $container, $id, Definition $definition) + { + $instanceofConditionals = $definition->getInstanceofConditionals(); + $autoconfiguredInstanceof = $definition->isAutoconfigured() ? $container->getAutoconfiguredInstanceof() : array(); + if (!$instanceofConditionals && !$autoconfiguredInstanceof) { + return $definition; + } + + if (!$class = $container->getParameterBag()->resolveValue($definition->getClass())) { + return $definition; + } + + $conditionals = $this->mergeConditionals($autoconfiguredInstanceof, $instanceofConditionals, $container); + + $definition->setInstanceofConditionals(array()); + $parent = $shared = null; + $instanceofTags = array(); + + foreach ($conditionals as $interface => $instanceofDefs) { + if ($interface !== $class && (!$container->getReflectionClass($class, false))) { + continue; + } + + if ($interface !== $class && !is_subclass_of($class, $interface)) { + continue; + } + + foreach ($instanceofDefs as $key => $instanceofDef) { + /** @var ChildDefinition $instanceofDef */ + $instanceofDef = clone $instanceofDef; + $instanceofDef->setAbstract(true)->setParent($parent ?: 'abstract.instanceof.'.$id); + $parent = 'instanceof.'.$interface.'.'.$key.'.'.$id; + $container->setDefinition($parent, $instanceofDef); + $instanceofTags[] = $instanceofDef->getTags(); + $instanceofDef->setTags(array()); + + if (isset($instanceofDef->getChanges()['shared'])) { + $shared = $instanceofDef->isShared(); + } + } + } + + if ($parent) { + $bindings = $definition->getBindings(); + $abstract = $container->setDefinition('abstract.instanceof.'.$id, $definition); + + // cast Definition to ChildDefinition + $definition->setBindings(array()); + $definition = serialize($definition); + $definition = substr_replace($definition, '53', 2, 2); + $definition = substr_replace($definition, 'Child', 44, 0); + $definition = unserialize($definition); + $definition->setParent($parent); + + if (null !== $shared && !isset($definition->getChanges()['shared'])) { + $definition->setShared($shared); + } + + $i = \count($instanceofTags); + while (0 <= --$i) { + foreach ($instanceofTags[$i] as $k => $v) { + foreach ($v as $v) { + if ($definition->hasTag($k) && \in_array($v, $definition->getTag($k))) { + continue; + } + $definition->addTag($k, $v); + } + } + } + + $definition->setBindings($bindings); + + // reset fields with "merge" behavior + $abstract + ->setBindings(array()) + ->setArguments(array()) + ->setMethodCalls(array()) + ->setDecoratedService(null) + ->setTags(array()) + ->setAbstract(true); + } + + return $definition; + } + + private function mergeConditionals(array $autoconfiguredInstanceof, array $instanceofConditionals, ContainerBuilder $container) + { + // make each value an array of ChildDefinition + $conditionals = array_map(function ($childDef) { return array($childDef); }, $autoconfiguredInstanceof); + + foreach ($instanceofConditionals as $interface => $instanceofDef) { + // make sure the interface/class exists (but don't validate automaticInstanceofConditionals) + if (!$container->getReflectionClass($interface)) { + throw new RuntimeException(sprintf('"%s" is set as an "instanceof" conditional, but it does not exist.', $interface)); + } + + if (!isset($autoconfiguredInstanceof[$interface])) { + $conditionals[$interface] = array(); + } + + $conditionals[$interface][] = $instanceofDef; + } + + return $conditionals; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.php new file mode 100644 index 0000000..f1a1475 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveInvalidReferencesPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Emulates the invalid behavior if the reference is not found within the + * container. + * + * @author Johannes M. Schmitt + */ +class ResolveInvalidReferencesPass implements CompilerPassInterface +{ + private $container; + private $signalingException; + + /** + * Process the ContainerBuilder to resolve invalid references. + */ + public function process(ContainerBuilder $container) + { + $this->container = $container; + $this->signalingException = new RuntimeException('Invalid reference.'); + + try { + $this->processValue($container->getDefinitions(), 1); + } finally { + $this->container = $this->signalingException = null; + } + } + + /** + * Processes arguments to determine invalid references. + * + * @throws RuntimeException When an invalid reference is found + */ + private function processValue($value, $rootLevel = 0, $level = 0) + { + if ($value instanceof ServiceClosureArgument) { + $value->setValues($this->processValue($value->getValues(), 1, 1)); + } elseif ($value instanceof ArgumentInterface) { + $value->setValues($this->processValue($value->getValues(), $rootLevel, 1 + $level)); + } elseif ($value instanceof Definition) { + if ($value->isSynthetic() || $value->isAbstract()) { + return $value; + } + $value->setArguments($this->processValue($value->getArguments(), 0)); + $value->setProperties($this->processValue($value->getProperties(), 1)); + $value->setMethodCalls($this->processValue($value->getMethodCalls(), 2)); + } elseif (\is_array($value)) { + $i = 0; + + foreach ($value as $k => $v) { + try { + if (false !== $i && $k !== $i++) { + $i = false; + } + if ($v !== $processedValue = $this->processValue($v, $rootLevel, 1 + $level)) { + $value[$k] = $processedValue; + } + } catch (RuntimeException $e) { + if ($rootLevel < $level || ($rootLevel && !$level)) { + unset($value[$k]); + } elseif ($rootLevel) { + throw $e; + } else { + $value[$k] = null; + } + } + } + + // Ensure numerically indexed arguments have sequential numeric keys. + if (false !== $i) { + $value = array_values($value); + } + } elseif ($value instanceof Reference) { + if ($this->container->has($value)) { + return $value; + } + $invalidBehavior = $value->getInvalidBehavior(); + + // resolve invalid behavior + if (ContainerInterface::NULL_ON_INVALID_REFERENCE === $invalidBehavior) { + $value = null; + } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $invalidBehavior) { + if (0 < $level || $rootLevel) { + throw $this->signalingException; + } + $value = null; + } + } + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php new file mode 100644 index 0000000..90cf64a --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveNamedArgumentsPass.php @@ -0,0 +1,101 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Resolves named arguments to their corresponding numeric index. + * + * @author Kévin Dunglas + */ +class ResolveNamedArgumentsPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + $calls = $value->getMethodCalls(); + $calls[] = array('__construct', $value->getArguments()); + + foreach ($calls as $i => $call) { + list($method, $arguments) = $call; + $parameters = null; + $resolvedArguments = array(); + + foreach ($arguments as $key => $argument) { + if (\is_int($key)) { + $resolvedArguments[$key] = $argument; + continue; + } + + if (null === $parameters) { + $r = $this->getReflectionMethod($value, $method); + $class = $r instanceof \ReflectionMethod ? $r->class : $this->currentId; + $parameters = $r->getParameters(); + } + + if (isset($key[0]) && '$' === $key[0]) { + foreach ($parameters as $j => $p) { + if ($key === '$'.$p->name) { + $resolvedArguments[$j] = $argument; + + continue 2; + } + } + + throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument named "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); + } + + if (null !== $argument && !$argument instanceof Reference && !$argument instanceof Definition) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": the value of argument "%s" of method "%s()" must be null, an instance of %s or an instance of %s, %s given.', $this->currentId, $key, $class !== $this->currentId ? $class.'::'.$method : $method, Reference::class, Definition::class, \gettype($argument))); + } + + $typeFound = false; + foreach ($parameters as $j => $p) { + if (!array_key_exists($j, $resolvedArguments) && ProxyHelper::getTypeHint($r, $p, true) === $key) { + $resolvedArguments[$j] = $argument; + $typeFound = true; + } + } + + if (!$typeFound) { + throw new InvalidArgumentException(sprintf('Invalid service "%s": method "%s()" has no argument type-hinted as "%s". Check your service definition.', $this->currentId, $class !== $this->currentId ? $class.'::'.$method : $method, $key)); + } + } + + if ($resolvedArguments !== $call[1]) { + ksort($resolvedArguments); + $calls[$i][1] = $resolvedArguments; + } + } + + list(, $arguments) = array_pop($calls); + + if ($arguments !== $value->getArguments()) { + $value->setArguments($arguments); + } + if ($calls !== $value->getMethodCalls()) { + $value->setMethodCalls($calls); + } + + return parent::processValue($value, $isRoot); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.php new file mode 100644 index 0000000..de16102 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveParameterPlaceHoldersPass.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 Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * Resolves all parameter placeholders "%somevalue%" to their real values. + * + * @author Johannes M. Schmitt + */ +class ResolveParameterPlaceHoldersPass extends AbstractRecursivePass +{ + private $bag; + private $resolveArrays; + + public function __construct(bool $resolveArrays = true) + { + $this->resolveArrays = $resolveArrays; + } + + /** + * {@inheritdoc} + * + * @throws ParameterNotFoundException + */ + public function process(ContainerBuilder $container) + { + $this->bag = $container->getParameterBag(); + + try { + parent::process($container); + + $aliases = array(); + foreach ($container->getAliases() as $name => $target) { + $this->currentId = $name; + $aliases[$this->bag->resolveValue($name)] = $target; + } + $container->setAliases($aliases); + } catch (ParameterNotFoundException $e) { + $e->setSourceId($this->currentId); + + throw $e; + } + + $this->bag->resolve(); + $this->bag = null; + } + + protected function processValue($value, $isRoot = false) + { + if (\is_string($value)) { + $v = $this->bag->resolveValue($value); + + return $this->resolveArrays || !$v || !\is_array($v) ? $v : $value; + } + if ($value instanceof Definition) { + $value->setBindings($this->processValue($value->getBindings())); + $changes = $value->getChanges(); + if (isset($changes['class'])) { + $value->setClass($this->bag->resolveValue($value->getClass())); + } + if (isset($changes['file'])) { + $value->setFile($this->bag->resolveValue($value->getFile())); + } + } + + $value = parent::processValue($value, $isRoot); + + if ($value && \is_array($value)) { + $value = array_combine($this->bag->resolveValue(array_keys($value)), $value); + } + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php b/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.php new file mode 100644 index 0000000..1bd9934 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolvePrivatesPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Nicolas Grekas + */ +class ResolvePrivatesPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition->isPrivate()) { + $definition->setPublic(false); + $definition->setPrivate(true); + } + } + + foreach ($container->getAliases() as $id => $alias) { + if ($alias->isPrivate()) { + $alias->setPublic(false); + $alias->setPrivate(true); + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.php new file mode 100644 index 0000000..31a7a27 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveReferencesToAliasesPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Replaces all references to aliases with references to the actual service. + * + * @author Johannes M. Schmitt + */ +class ResolveReferencesToAliasesPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + parent::process($container); + + foreach ($container->getAliases() as $id => $alias) { + $aliasId = (string) $alias; + if ($aliasId !== $defId = $this->getDefinitionId($aliasId, $container)) { + $container->setAlias($id, $defId)->setPublic($alias->isPublic())->setPrivate($alias->isPrivate()); + } + } + } + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if ($value instanceof Reference) { + $defId = $this->getDefinitionId($id = (string) $value, $this->container); + + if ($defId !== $id) { + return new Reference($defId, $value->getInvalidBehavior()); + } + } + + return parent::processValue($value); + } + + private function getDefinitionId(string $id, ContainerBuilder $container): string + { + $seen = array(); + while ($container->hasAlias($id)) { + if (isset($seen[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($seen)); + } + $seen[$id] = true; + $id = (string) $container->getAlias($id); + } + + return $id; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.php new file mode 100644 index 0000000..cc87f3a --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveServiceSubscribersPass.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\DependencyInjection\Compiler; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Compiler pass to inject their service locator to service subscribers. + * + * @author Nicolas Grekas + */ +class ResolveServiceSubscribersPass extends AbstractRecursivePass +{ + private $serviceLocator; + + protected function processValue($value, $isRoot = false) + { + if ($value instanceof Reference && $this->serviceLocator && ContainerInterface::class === (string) $value) { + return new Reference($this->serviceLocator); + } + + if (!$value instanceof Definition) { + return parent::processValue($value, $isRoot); + } + + $serviceLocator = $this->serviceLocator; + $this->serviceLocator = null; + + if ($value->hasTag('container.service_subscriber.locator')) { + $this->serviceLocator = $value->getTag('container.service_subscriber.locator')[0]['id']; + $value->clearTag('container.service_subscriber.locator'); + } + + try { + return parent::processValue($value); + } finally { + $this->serviceLocator = $serviceLocator; + } + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php b/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.php new file mode 100644 index 0000000..009cee9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ResolveTaggedIteratorArgumentPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; + +/** + * Resolves all TaggedIteratorArgument arguments. + * + * @author Roland Franssen + */ +class ResolveTaggedIteratorArgumentPass extends AbstractRecursivePass +{ + use PriorityTaggedServiceTrait; + + /** + * {@inheritdoc} + */ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof TaggedIteratorArgument) { + return parent::processValue($value, $isRoot); + } + + $value->setValues($this->findAndSortTaggedServices($value->getTag(), $this->container)); + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php b/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.php new file mode 100644 index 0000000..5a0703e --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ServiceLocatorTagPass.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; + +/** + * Applies the "container.service_locator" tag by wrapping references into ServiceClosureArgument instances. + * + * @author Nicolas Grekas + */ +final class ServiceLocatorTagPass extends AbstractRecursivePass +{ + protected function processValue($value, $isRoot = false) + { + if (!$value instanceof Definition || !$value->hasTag('container.service_locator')) { + return parent::processValue($value, $isRoot); + } + + if (!$value->getClass()) { + $value->setClass(ServiceLocator::class); + } + + $arguments = $value->getArguments(); + if (!isset($arguments[0]) || !\is_array($arguments[0])) { + throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set.', $this->currentId)); + } + + foreach ($arguments[0] as $k => $v) { + if ($v instanceof ServiceClosureArgument) { + continue; + } + if (!$v instanceof Reference) { + throw new InvalidArgumentException(sprintf('Invalid definition for service "%s": an array of references is expected as first argument when the "container.service_locator" tag is set, "%s" found for key "%s".', $this->currentId, \is_object($v) ? \get_class($v) : \gettype($v), $k)); + } + $arguments[0][$k] = new ServiceClosureArgument($v); + } + ksort($arguments[0]); + + $value->setArguments($arguments); + + $id = 'service_locator.'.ContainerBuilder::hash($value); + + if ($isRoot) { + if ($id !== $this->currentId) { + $this->container->setAlias($id, new Alias($this->currentId, false)); + } + + return $value; + } + + $this->container->setDefinition($id, $value->setPublic(false)); + + return new Reference($id); + } + + /** + * @param ContainerBuilder $container + * @param Reference[] $refMap + * @param string|null $callerId + * + * @return Reference + */ + public static function register(ContainerBuilder $container, array $refMap, $callerId = null) + { + foreach ($refMap as $id => $ref) { + if (!$ref instanceof Reference) { + throw new InvalidArgumentException(sprintf('Invalid service locator definition: only services can be referenced, "%s" found for key "%s". Inject parameter values using constructors instead.', \is_object($ref) ? \get_class($ref) : \gettype($ref), $id)); + } + $refMap[$id] = new ServiceClosureArgument($ref); + } + ksort($refMap); + + $locator = (new Definition(ServiceLocator::class)) + ->addArgument($refMap) + ->setPublic(false) + ->addTag('container.service_locator'); + + if (!$container->has($id = 'service_locator.'.ContainerBuilder::hash($locator))) { + $container->setDefinition($id, $locator); + } + + if (null !== $callerId) { + $locatorId = $id; + // Locators are shared when they hold the exact same list of factories; + // to have them specialized per consumer service, we use a cloning factory + // to derivate customized instances from the prototype one. + $container->register($id .= '.'.$callerId, ServiceLocator::class) + ->setPublic(false) + ->setFactory(array(new Reference($locatorId), 'withContext')) + ->addArgument($callerId) + ->addArgument(new Reference('service_container')); + } + + return new Reference($id); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php b/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.php new file mode 100644 index 0000000..721e875 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraph.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * This is a directed graph of your services. + * + * This information can be used by your compiler passes instead of collecting + * it themselves which improves performance quite a lot. + * + * @author Johannes M. Schmitt + * + * @final + */ +class ServiceReferenceGraph +{ + /** + * @var ServiceReferenceGraphNode[] + */ + private $nodes = array(); + + public function hasNode(string $id): bool + { + return isset($this->nodes[$id]); + } + + /** + * Gets a node by identifier. + * + * @throws InvalidArgumentException if no node matches the supplied identifier + */ + public function getNode(string $id): ServiceReferenceGraphNode + { + if (!isset($this->nodes[$id])) { + throw new InvalidArgumentException(sprintf('There is no node with id "%s".', $id)); + } + + return $this->nodes[$id]; + } + + /** + * Returns all nodes. + * + * @return ServiceReferenceGraphNode[] + */ + public function getNodes(): array + { + return $this->nodes; + } + + /** + * Clears all nodes. + */ + public function clear() + { + foreach ($this->nodes as $node) { + $node->clear(); + } + $this->nodes = array(); + } + + /** + * Connects 2 nodes together in the Graph. + */ + public function connect(?string $sourceId, $sourceValue, ?string $destId, $destValue = null, $reference = null, bool $lazy = false, bool $weak = false) + { + if (null === $sourceId || null === $destId) { + return; + } + + $sourceNode = $this->createNode($sourceId, $sourceValue); + $destNode = $this->createNode($destId, $destValue); + $edge = new ServiceReferenceGraphEdge($sourceNode, $destNode, $reference, $lazy, $weak); + + $sourceNode->addOutEdge($edge); + $destNode->addInEdge($edge); + } + + private function createNode(string $id, $value): ServiceReferenceGraphNode + { + if (isset($this->nodes[$id]) && $this->nodes[$id]->getValue() === $value) { + return $this->nodes[$id]; + } + + return $this->nodes[$id] = new ServiceReferenceGraphNode($id, $value); + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php b/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.php new file mode 100644 index 0000000..3e5b9c9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphEdge.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 Symfony\Component\DependencyInjection\Compiler; + +/** + * Represents an edge in your service graph. + * + * Value is typically a reference. + * + * @author Johannes M. Schmitt + */ +class ServiceReferenceGraphEdge +{ + private $sourceNode; + private $destNode; + private $value; + private $lazy; + private $weak; + + public function __construct(ServiceReferenceGraphNode $sourceNode, ServiceReferenceGraphNode $destNode, $value = null, bool $lazy = false, bool $weak = false) + { + $this->sourceNode = $sourceNode; + $this->destNode = $destNode; + $this->value = $value; + $this->lazy = $lazy; + $this->weak = $weak; + } + + /** + * Returns the value of the edge. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns the source node. + * + * @return ServiceReferenceGraphNode + */ + public function getSourceNode() + { + return $this->sourceNode; + } + + /** + * Returns the destination node. + * + * @return ServiceReferenceGraphNode + */ + public function getDestNode() + { + return $this->destNode; + } + + /** + * Returns true if the edge is lazy, meaning it's a dependency not requiring direct instantiation. + * + * @return bool + */ + public function isLazy() + { + return $this->lazy; + } + + /** + * Returns true if the edge is weak, meaning it shouldn't prevent removing the target service. + * + * @return bool + */ + public function isWeak() + { + return $this->weak; + } +} diff --git a/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php b/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.php new file mode 100644 index 0000000..210566f --- /dev/null +++ b/vendor/symfony/dependency-injection/Compiler/ServiceReferenceGraphNode.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\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Represents a node in your service graph. + * + * Value is typically a definition, or an alias. + * + * @author Johannes M. Schmitt + */ +class ServiceReferenceGraphNode +{ + private $id; + private $inEdges = array(); + private $outEdges = array(); + private $value; + + /** + * @param string $id The node identifier + * @param mixed $value The node value + */ + public function __construct(string $id, $value) + { + $this->id = $id; + $this->value = $value; + } + + public function addInEdge(ServiceReferenceGraphEdge $edge) + { + $this->inEdges[] = $edge; + } + + public function addOutEdge(ServiceReferenceGraphEdge $edge) + { + $this->outEdges[] = $edge; + } + + /** + * Checks if the value of this node is an Alias. + * + * @return bool True if the value is an Alias instance + */ + public function isAlias() + { + return $this->value instanceof Alias; + } + + /** + * Checks if the value of this node is a Definition. + * + * @return bool True if the value is a Definition instance + */ + public function isDefinition() + { + return $this->value instanceof Definition; + } + + /** + * Returns the identifier. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Returns the in edges. + * + * @return array The in ServiceReferenceGraphEdge array + */ + public function getInEdges() + { + return $this->inEdges; + } + + /** + * Returns the out edges. + * + * @return array The out ServiceReferenceGraphEdge array + */ + public function getOutEdges() + { + return $this->outEdges; + } + + /** + * Returns the value of this Node. + * + * @return mixed The value + */ + public function getValue() + { + return $this->value; + } + + /** + * Clears all edges. + */ + public function clear() + { + $this->inEdges = $this->outEdges = array(); + } +} diff --git a/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php b/vendor/symfony/dependency-injection/Config/ContainerParametersResource.php new file mode 100644 index 0000000..072f058 --- /dev/null +++ b/vendor/symfony/dependency-injection/Config/ContainerParametersResource.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\DependencyInjection\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * Tracks container parameters. + * + * @author Maxime Steinhausser + */ +class ContainerParametersResource implements ResourceInterface, \Serializable +{ + private $parameters; + + /** + * @param array $parameters The container parameters to track + */ + public function __construct(array $parameters) + { + $this->parameters = $parameters; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return 'container_parameters_'.md5(serialize($this->parameters)); + } + + /** + * {@inheritdoc} + */ + public function serialize() + { + return serialize($this->parameters); + } + + /** + * {@inheritdoc} + */ + public function unserialize($serialized) + { + $this->parameters = unserialize($serialized); + } + + /** + * @return array Tracked parameters + */ + public function getParameters() + { + return $this->parameters; + } +} diff --git a/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php b/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.php new file mode 100644 index 0000000..6ed77e3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Config/ContainerParametersResourceChecker.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\DependencyInjection\Config; + +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Config\ResourceCheckerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * @author Maxime Steinhausser + */ +class ContainerParametersResourceChecker implements ResourceCheckerInterface +{ + /** @var ContainerInterface */ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function supports(ResourceInterface $metadata) + { + return $metadata instanceof ContainerParametersResource; + } + + /** + * {@inheritdoc} + */ + public function isFresh(ResourceInterface $resource, $timestamp) + { + foreach ($resource->getParameters() as $key => $value) { + if (!$this->container->hasParameter($key) || $this->container->getParameter($key) !== $value) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/dependency-injection/Container.php b/vendor/symfony/dependency-injection/Container.php new file mode 100644 index 0000000..e6c631b --- /dev/null +++ b/vendor/symfony/dependency-injection/Container.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\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; + +/** + * Container is a dependency injection container. + * + * It gives access to object instances (services). + * Services and parameters are simple key/pair stores. + * The container can have four possible behaviors when a service + * does not exist (or is not initialized for the last case): + * + * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default) + * * NULL_ON_INVALID_REFERENCE: Returns null + * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference + * (for instance, ignore a setter if the service does not exist) + * * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class Container implements ResettableContainerInterface +{ + protected $parameterBag; + protected $services = array(); + protected $fileMap = array(); + protected $methodMap = array(); + protected $aliases = array(); + protected $loading = array(); + protected $resolving = array(); + protected $syntheticIds = array(); + + private $envCache = array(); + private $compiled = false; + private $getEnv; + + public function __construct(ParameterBagInterface $parameterBag = null) + { + $this->parameterBag = $parameterBag ?: new EnvPlaceholderParameterBag(); + } + + /** + * Compiles the container. + * + * This method does two things: + * + * * Parameter values are resolved; + * * The parameter bag is frozen. + */ + public function compile() + { + $this->parameterBag->resolve(); + + $this->parameterBag = new FrozenParameterBag($this->parameterBag->all()); + + $this->compiled = true; + } + + /** + * Returns true if the container is compiled. + * + * @return bool + */ + public function isCompiled() + { + return $this->compiled; + } + + /** + * Gets the service container parameter bag. + * + * @return ParameterBagInterface A ParameterBagInterface instance + */ + public function getParameterBag() + { + return $this->parameterBag; + } + + /** + * Gets a parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws InvalidArgumentException if the parameter is not defined + */ + public function getParameter($name) + { + return $this->parameterBag->get($name); + } + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return bool The presence of parameter in container + */ + public function hasParameter($name) + { + return $this->parameterBag->has($name); + } + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + */ + public function setParameter($name, $value) + { + $this->parameterBag->set($name, $value); + } + + /** + * Sets a service. + * + * Setting a service to null resets the service: has() returns false and get() + * behaves in the same way as if the service was never created. + * + * @param string $id The service identifier + * @param object $service The service instance + */ + public function set($id, $service) + { + // Runs the internal initializer; used by the dumped container to include always-needed files + if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) { + $initialize = $this->privates['service_container']; + unset($this->privates['service_container']); + $initialize(); + } + + if ('service_container' === $id) { + throw new InvalidArgumentException('You cannot set service "service_container".'); + } + + if (!(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { + if (isset($this->syntheticIds[$id]) || !isset($this->getRemovedIds()[$id])) { + // no-op + } elseif (null === $service) { + throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot unset it.', $id)); + } else { + throw new InvalidArgumentException(sprintf('The "%s" service is private, you cannot replace it.', $id)); + } + } elseif (isset($this->services[$id])) { + throw new InvalidArgumentException(sprintf('The "%s" service is already initialized, you cannot replace it.', $id)); + } + + if (isset($this->aliases[$id])) { + unset($this->aliases[$id]); + } + + if (null === $service) { + unset($this->services[$id]); + + return; + } + + $this->services[$id] = $service; + } + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return bool true if the service is defined, false otherwise + */ + public function has($id) + { + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + if (isset($this->services[$id])) { + return true; + } + if ('service_container' === $id) { + return true; + } + + return isset($this->fileMap[$id]) || isset($this->methodMap[$id]); + } + + /** + * Gets a service. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * @throws \Exception if an exception has been thrown when the service has been resolved + * + * @see Reference + */ + public function get($id, $invalidBehavior = /* self::EXCEPTION_ON_INVALID_REFERENCE */ 1) + { + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + + // Re-use shared service instance if it exists. + if (isset($this->services[$id])) { + return $this->services[$id]; + } + if ('service_container' === $id) { + return $this; + } + + if (isset($this->loading[$id])) { + throw new ServiceCircularReferenceException($id, array_keys($this->loading)); + } + + $this->loading[$id] = true; + + try { + if (isset($this->fileMap[$id])) { + return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]); + } elseif (isset($this->methodMap[$id])) { + return /* self::IGNORE_ON_UNINITIALIZED_REFERENCE */ 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); + } + } catch (\Exception $e) { + unset($this->services[$id]); + + throw $e; + } finally { + unset($this->loading[$id]); + } + + if (/* self::EXCEPTION_ON_INVALID_REFERENCE */ 1 === $invalidBehavior) { + if (!$id) { + throw new ServiceNotFoundException($id); + } + if (isset($this->syntheticIds[$id])) { + throw new ServiceNotFoundException($id, null, null, array(), sprintf('The "%s" service is synthetic, it needs to be set at boot time before it can be used.', $id)); + } + if (isset($this->getRemovedIds()[$id])) { + throw new ServiceNotFoundException($id, null, null, array(), sprintf('The "%s" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.', $id)); + } + + $alternatives = array(); + foreach ($this->getServiceIds() as $knownId) { + $lev = levenshtein($id, $knownId); + if ($lev <= \strlen($id) / 3 || false !== strpos($knownId, $id)) { + $alternatives[] = $knownId; + } + } + + throw new ServiceNotFoundException($id, null, null, $alternatives); + } + } + + /** + * Returns true if the given service has actually been initialized. + * + * @param string $id The service identifier + * + * @return bool true if service has already been initialized, false otherwise + */ + public function initialized($id) + { + if (isset($this->aliases[$id])) { + $id = $this->aliases[$id]; + } + + if ('service_container' === $id) { + return false; + } + + return isset($this->services[$id]); + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->services = array(); + } + + /** + * Gets all service ids. + * + * @return array An array of all defined service ids + */ + public function getServiceIds() + { + return array_unique(array_merge(array('service_container'), array_keys($this->fileMap), array_keys($this->methodMap), array_keys($this->services))); + } + + /** + * Gets service ids that existed at compile time. + * + * @return array + */ + public function getRemovedIds() + { + return array(); + } + + /** + * Camelizes a string. + * + * @param string $id A string to camelize + * + * @return string The camelized string + */ + public static function camelize($id) + { + return strtr(ucwords(strtr($id, array('_' => ' ', '.' => '_ ', '\\' => '_ '))), array(' ' => '')); + } + + /** + * A string to underscore. + * + * @param string $id The string to underscore + * + * @return string The underscored string + */ + public static function underscore($id) + { + return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), str_replace('_', '.', $id))); + } + + /** + * Creates a service by requiring its factory file. + * + * @return object The service created by the file + */ + protected function load($file) + { + return require $file; + } + + /** + * Fetches a variable from the environment. + * + * @param string $name The name of the environment variable + * + * @return mixed The value to use for the provided environment variable name + * + * @throws EnvNotFoundException When the environment variable is not found and has no default value + */ + protected function getEnv($name) + { + if (isset($this->resolving[$envName = "env($name)"])) { + throw new ParameterCircularReferenceException(array_keys($this->resolving)); + } + if (isset($this->envCache[$name]) || array_key_exists($name, $this->envCache)) { + return $this->envCache[$name]; + } + if (!$this->has($id = 'container.env_var_processors_locator')) { + $this->set($id, new ServiceLocator(array())); + } + if (!$this->getEnv) { + $this->getEnv = new \ReflectionMethod($this, __FUNCTION__); + $this->getEnv->setAccessible(true); + $this->getEnv = $this->getEnv->getClosure($this); + } + $processors = $this->get($id); + + if (false !== $i = strpos($name, ':')) { + $prefix = substr($name, 0, $i); + $localName = substr($name, 1 + $i); + } else { + $prefix = 'string'; + $localName = $name; + } + $processor = $processors->has($prefix) ? $processors->get($prefix) : new EnvVarProcessor($this); + + $this->resolving[$envName] = true; + try { + return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv); + } finally { + unset($this->resolving[$envName]); + } + } + + private function __clone() + { + } +} diff --git a/vendor/symfony/dependency-injection/ContainerAwareInterface.php b/vendor/symfony/dependency-injection/ContainerAwareInterface.php new file mode 100644 index 0000000..d78491b --- /dev/null +++ b/vendor/symfony/dependency-injection/ContainerAwareInterface.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\DependencyInjection; + +/** + * ContainerAwareInterface should be implemented by classes that depends on a Container. + * + * @author Fabien Potencier + */ +interface ContainerAwareInterface +{ + public function setContainer(ContainerInterface $container = null); +} diff --git a/vendor/symfony/dependency-injection/ContainerAwareTrait.php b/vendor/symfony/dependency-injection/ContainerAwareTrait.php new file mode 100644 index 0000000..ee1ea2c --- /dev/null +++ b/vendor/symfony/dependency-injection/ContainerAwareTrait.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\DependencyInjection; + +/** + * ContainerAware trait. + * + * @author Fabien Potencier + */ +trait ContainerAwareTrait +{ + /** + * @var ContainerInterface + */ + protected $container; + + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/vendor/symfony/dependency-injection/ContainerBuilder.php b/vendor/symfony/dependency-injection/ContainerBuilder.php new file mode 100644 index 0000000..8f78215 --- /dev/null +++ b/vendor/symfony/dependency-injection/ContainerBuilder.php @@ -0,0 +1,1584 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\Config\Resource\ComposerResource; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\Resource\FileExistenceResource; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\Config\Resource\ReflectionClassResource; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * ContainerBuilder is a DI container that provides an API to easily describe services. + * + * @author Fabien Potencier + */ +class ContainerBuilder extends Container implements TaggedContainerInterface +{ + /** + * @var ExtensionInterface[] + */ + private $extensions = array(); + + /** + * @var ExtensionInterface[] + */ + private $extensionsByNs = array(); + + /** + * @var Definition[] + */ + private $definitions = array(); + + /** + * @var Alias[] + */ + private $aliasDefinitions = array(); + + /** + * @var ResourceInterface[] + */ + private $resources = array(); + + private $extensionConfigs = array(); + + /** + * @var Compiler + */ + private $compiler; + + private $trackResources; + + /** + * @var InstantiatorInterface|null + */ + private $proxyInstantiator; + + /** + * @var ExpressionLanguage|null + */ + private $expressionLanguage; + + /** + * @var ExpressionFunctionProviderInterface[] + */ + private $expressionLanguageProviders = array(); + + /** + * @var string[] with tag names used by findTaggedServiceIds + */ + private $usedTags = array(); + + /** + * @var string[][] a map of env var names to their placeholders + */ + private $envPlaceholders = array(); + + /** + * @var int[] a map of env vars to their resolution counter + */ + private $envCounters = array(); + + /** + * @var string[] the list of vendor directories + */ + private $vendors; + + private $autoconfiguredInstanceof = array(); + + private $removedIds = array(); + private $alreadyLoading = array(); + + private static $internalTypes = array( + 'int' => true, + 'float' => true, + 'string' => true, + 'bool' => true, + 'resource' => true, + 'object' => true, + 'array' => true, + 'null' => true, + 'callable' => true, + 'iterable' => true, + 'mixed' => true, + ); + + public function __construct(ParameterBagInterface $parameterBag = null) + { + parent::__construct($parameterBag); + + $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface'); + $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true)); + $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false)); + $this->setAlias(ContainerInterface::class, new Alias('service_container', false)); + } + + /** + * @var \ReflectionClass[] a list of class reflectors + */ + private $classReflectors; + + /** + * Sets the track resources flag. + * + * If you are not using the loaders and therefore don't want + * to depend on the Config component, set this flag to false. + * + * @param bool $track True if you want to track resources, false otherwise + */ + public function setResourceTracking($track) + { + $this->trackResources = (bool) $track; + } + + /** + * Checks if resources are tracked. + * + * @return bool true If resources are tracked, false otherwise + */ + public function isTrackingResources() + { + return $this->trackResources; + } + + /** + * Sets the instantiator to be used when fetching proxies. + */ + public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator) + { + $this->proxyInstantiator = $proxyInstantiator; + } + + public function registerExtension(ExtensionInterface $extension) + { + $this->extensions[$extension->getAlias()] = $extension; + + if (false !== $extension->getNamespace()) { + $this->extensionsByNs[$extension->getNamespace()] = $extension; + } + } + + /** + * Returns an extension by alias or namespace. + * + * @param string $name An alias or a namespace + * + * @return ExtensionInterface An extension instance + * + * @throws LogicException if the extension is not registered + */ + public function getExtension($name) + { + if (isset($this->extensions[$name])) { + return $this->extensions[$name]; + } + + if (isset($this->extensionsByNs[$name])) { + return $this->extensionsByNs[$name]; + } + + throw new LogicException(sprintf('Container extension "%s" is not registered', $name)); + } + + /** + * Returns all registered extensions. + * + * @return ExtensionInterface[] An array of ExtensionInterface + */ + public function getExtensions() + { + return $this->extensions; + } + + /** + * Checks if we have an extension. + * + * @param string $name The name of the extension + * + * @return bool If the extension exists + */ + public function hasExtension($name) + { + return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]); + } + + /** + * Returns an array of resources loaded to build this configuration. + * + * @return ResourceInterface[] An array of resources + */ + public function getResources() + { + return array_values($this->resources); + } + + /** + * @return $this + */ + public function addResource(ResourceInterface $resource) + { + if (!$this->trackResources) { + return $this; + } + + if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) { + return $this; + } + + $this->resources[(string) $resource] = $resource; + + return $this; + } + + /** + * Sets the resources for this configuration. + * + * @param ResourceInterface[] $resources An array of resources + * + * @return $this + */ + public function setResources(array $resources) + { + if (!$this->trackResources) { + return $this; + } + + $this->resources = $resources; + + return $this; + } + + /** + * Adds the object class hierarchy as resources. + * + * @param object|string $object An object instance or class name + * + * @return $this + */ + public function addObjectResource($object) + { + if ($this->trackResources) { + if (\is_object($object)) { + $object = \get_class($object); + } + if (!isset($this->classReflectors[$object])) { + $this->classReflectors[$object] = new \ReflectionClass($object); + } + $class = $this->classReflectors[$object]; + + foreach ($class->getInterfaceNames() as $name) { + if (null === $interface = &$this->classReflectors[$name]) { + $interface = new \ReflectionClass($name); + } + $file = $interface->getFileName(); + if (false !== $file && file_exists($file)) { + $this->fileExists($file); + } + } + do { + $file = $class->getFileName(); + if (false !== $file && file_exists($file)) { + $this->fileExists($file); + } + foreach ($class->getTraitNames() as $name) { + $this->addObjectResource($name); + } + } while ($class = $class->getParentClass()); + } + + return $this; + } + + /** + * Retrieves the requested reflection class and registers it for resource tracking. + * + * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true + * + * @final + */ + public function getReflectionClass(?string $class, bool $throw = true): ?\ReflectionClass + { + if (!$class = $this->getParameterBag()->resolveValue($class)) { + return null; + } + + if (isset(self::$internalTypes[$class])) { + return null; + } + + $resource = null; + + try { + if (isset($this->classReflectors[$class])) { + $classReflector = $this->classReflectors[$class]; + } elseif ($this->trackResources) { + $resource = new ClassExistenceResource($class, false); + $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class); + } else { + $classReflector = new \ReflectionClass($class); + } + } catch (\ReflectionException $e) { + if ($throw) { + throw $e; + } + $classReflector = false; + } + + if ($this->trackResources) { + if (!$classReflector) { + $this->addResource($resource ?: new ClassExistenceResource($class, false)); + } elseif (!$classReflector->isInternal()) { + $path = $classReflector->getFileName(); + + if (!$this->inVendors($path)) { + $this->addResource(new ReflectionClassResource($classReflector, $this->vendors)); + } + } + $this->classReflectors[$class] = $classReflector; + } + + return $classReflector ?: null; + } + + /** + * Checks whether the requested file or directory exists and registers the result for resource tracking. + * + * @param string $path The file or directory path for which to check the existence + * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed, + * it will be used as pattern for tracking contents of the requested directory + * + * @return bool + * + * @final + */ + public function fileExists(string $path, $trackContents = true): bool + { + $exists = file_exists($path); + + if (!$this->trackResources || $this->inVendors($path)) { + return $exists; + } + + if (!$exists) { + $this->addResource(new FileExistenceResource($path)); + + return $exists; + } + + if (is_dir($path)) { + if ($trackContents) { + $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null)); + } else { + $this->addResource(new GlobResource($path, '/*', false)); + } + } elseif ($trackContents) { + $this->addResource(new FileResource($path)); + } + + return $exists; + } + + /** + * Loads the configuration for an extension. + * + * @param string $extension The extension alias or namespace + * @param array $values An array of values that customizes the extension + * + * @return $this + * + * @throws BadMethodCallException When this ContainerBuilder is compiled + * @throws \LogicException if the extension is not registered + */ + public function loadFromExtension($extension, array $values = null) + { + if ($this->isCompiled()) { + throw new BadMethodCallException('Cannot load from an extension on a compiled container.'); + } + + if (\func_num_args() < 2) { + $values = array(); + } + + $namespace = $this->getExtension($extension)->getAlias(); + + $this->extensionConfigs[$namespace][] = $values; + + return $this; + } + + /** + * Adds a compiler pass. + * + * @param CompilerPassInterface $pass A compiler pass + * @param string $type The type of compiler pass + * @param int $priority Used to sort the passes + * + * @return $this + */ + public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, int $priority = 0) + { + $this->getCompiler()->addPass($pass, $type, $priority); + + $this->addObjectResource($pass); + + return $this; + } + + /** + * Returns the compiler pass config which can then be modified. + * + * @return PassConfig The compiler pass config + */ + public function getCompilerPassConfig() + { + return $this->getCompiler()->getPassConfig(); + } + + /** + * Returns the compiler. + * + * @return Compiler The compiler + */ + public function getCompiler() + { + if (null === $this->compiler) { + $this->compiler = new Compiler(); + } + + return $this->compiler; + } + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + * + * @throws BadMethodCallException When this ContainerBuilder is compiled + */ + public function set($id, $service) + { + $id = (string) $id; + + if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) { + // setting a synthetic service on a compiled container is alright + throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id)); + } + + unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]); + + parent::set($id, $service); + } + + /** + * Removes a service definition. + * + * @param string $id The service identifier + */ + public function removeDefinition($id) + { + if (isset($this->definitions[$id = (string) $id])) { + unset($this->definitions[$id]); + $this->removedIds[$id] = true; + } + } + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return bool true if the service is defined, false otherwise + */ + public function has($id) + { + $id = (string) $id; + + return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id); + } + + /** + * Gets a service. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws InvalidArgumentException when no definitions are available + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * @throws \Exception + * + * @see Reference + */ + public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + { + if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + return parent::get($id); + } + + return $this->doGet($id, $invalidBehavior); + } + + private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = array()) + { + if (isset($inlineServices[$id])) { + return $inlineServices[$id]; + } + if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) { + return parent::get($id, $invalidBehavior); + } + if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { + return $service; + } + + if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) { + return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices); + } + + try { + $definition = $this->getDefinition($id); + } catch (ServiceNotFoundException $e) { + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + return; + } + + throw $e; + } + + $loading = isset($this->alreadyLoading[$id]) ? 'loading' : 'alreadyLoading'; + $this->{$loading}[$id] = true; + + try { + $service = $this->createService($definition, $inlineServices, $id); + } finally { + unset($this->{$loading}[$id]); + } + + return $service; + } + + /** + * Merges a ContainerBuilder with the current ContainerBuilder configuration. + * + * Service definitions overrides the current defined ones. + * + * But for parameters, they are overridden by the current ones. It allows + * the parameters passed to the container constructor to have precedence + * over the loaded ones. + * + * $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); + * $loader = new LoaderXXX($container); + * $loader->load('resource_name'); + * $container->register('foo', 'stdClass'); + * + * In the above example, even if the loaded resource defines a foo + * parameter, the value will still be 'bar' as defined in the ContainerBuilder + * constructor. + * + * @throws BadMethodCallException When this ContainerBuilder is compiled + */ + public function merge(self $container) + { + if ($this->isCompiled()) { + throw new BadMethodCallException('Cannot merge on a compiled container.'); + } + + $this->addDefinitions($container->getDefinitions()); + $this->addAliases($container->getAliases()); + $this->getParameterBag()->add($container->getParameterBag()->all()); + + if ($this->trackResources) { + foreach ($container->getResources() as $resource) { + $this->addResource($resource); + } + } + + foreach ($this->extensions as $name => $extension) { + if (!isset($this->extensionConfigs[$name])) { + $this->extensionConfigs[$name] = array(); + } + + $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name)); + } + + if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) { + $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders(); + $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag()); + } else { + $envPlaceholders = array(); + } + + foreach ($container->envCounters as $env => $count) { + if (!$count && !isset($envPlaceholders[$env])) { + continue; + } + if (!isset($this->envCounters[$env])) { + $this->envCounters[$env] = $count; + } else { + $this->envCounters[$env] += $count; + } + } + + foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) { + if (isset($this->autoconfiguredInstanceof[$interface])) { + throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface)); + } + + $this->autoconfiguredInstanceof[$interface] = $childDefinition; + } + } + + /** + * Returns the configuration array for the given extension. + * + * @param string $name The name of the extension + * + * @return array An array of configuration + */ + public function getExtensionConfig($name) + { + if (!isset($this->extensionConfigs[$name])) { + $this->extensionConfigs[$name] = array(); + } + + return $this->extensionConfigs[$name]; + } + + /** + * Prepends a config array to the configs of the given extension. + * + * @param string $name The name of the extension + * @param array $config The config to set + */ + public function prependExtensionConfig($name, array $config) + { + if (!isset($this->extensionConfigs[$name])) { + $this->extensionConfigs[$name] = array(); + } + + array_unshift($this->extensionConfigs[$name], $config); + } + + /** + * Compiles the container. + * + * This method passes the container to compiler + * passes whose job is to manipulate and optimize + * the container. + * + * The main compiler passes roughly do four things: + * + * * The extension configurations are merged; + * * Parameter values are resolved; + * * The parameter bag is frozen; + * * Extension loading is disabled. + * + * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current + * env vars or be replaced by uniquely identifiable placeholders. + * Set to "true" when you want to use the current ContainerBuilder + * directly, keep to "false" when the container is dumped instead. + */ + public function compile(bool $resolveEnvPlaceholders = false) + { + $compiler = $this->getCompiler(); + + if ($this->trackResources) { + foreach ($compiler->getPassConfig()->getPasses() as $pass) { + $this->addObjectResource($pass); + } + } + $bag = $this->getParameterBag(); + + if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) { + $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000); + } + + $compiler->compile($this); + + foreach ($this->definitions as $id => $definition) { + if ($this->trackResources && $definition->isLazy()) { + $this->getReflectionClass($definition->getClass()); + } + } + + $this->extensionConfigs = array(); + + if ($bag instanceof EnvPlaceholderParameterBag) { + if ($resolveEnvPlaceholders) { + $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true)); + } + + $this->envPlaceholders = $bag->getEnvPlaceholders(); + } + + parent::compile(); + + foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) { + if (!$definition->isPublic() || $definition->isPrivate()) { + $this->removedIds[$id] = true; + } + } + } + + /** + * Gets all service ids. + * + * @return array An array of all defined service ids + */ + public function getServiceIds() + { + return array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())); + } + + /** + * Gets removed service or alias ids. + * + * @return array + */ + public function getRemovedIds() + { + return $this->removedIds; + } + + /** + * Adds the service aliases. + */ + public function addAliases(array $aliases) + { + foreach ($aliases as $alias => $id) { + $this->setAlias($alias, $id); + } + } + + /** + * Sets the service aliases. + */ + public function setAliases(array $aliases) + { + $this->aliasDefinitions = array(); + $this->addAliases($aliases); + } + + /** + * Sets an alias for an existing service. + * + * @param string $alias The alias to create + * @param string|Alias $id The service to alias + * + * @return Alias + * + * @throws InvalidArgumentException if the id is not a string or an Alias + * @throws InvalidArgumentException if the alias is for itself + */ + public function setAlias($alias, $id) + { + $alias = (string) $alias; + + if (\is_string($id)) { + $id = new Alias($id); + } elseif (!$id instanceof Alias) { + throw new InvalidArgumentException('$id must be a string, or an Alias object.'); + } + + if ($alias === (string) $id) { + throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias)); + } + + unset($this->definitions[$alias], $this->removedIds[$alias]); + + return $this->aliasDefinitions[$alias] = $id; + } + + /** + * Removes an alias. + * + * @param string $alias The alias to remove + */ + public function removeAlias($alias) + { + if (isset($this->aliasDefinitions[$alias = (string) $alias])) { + unset($this->aliasDefinitions[$alias]); + $this->removedIds[$alias] = true; + } + } + + /** + * Returns true if an alias exists under the given identifier. + * + * @param string $id The service identifier + * + * @return bool true if the alias exists, false otherwise + */ + public function hasAlias($id) + { + return isset($this->aliasDefinitions[$id = (string) $id]); + } + + /** + * Gets all defined aliases. + * + * @return Alias[] An array of aliases + */ + public function getAliases() + { + return $this->aliasDefinitions; + } + + /** + * Gets an alias. + * + * @param string $id The service identifier + * + * @return Alias An Alias instance + * + * @throws InvalidArgumentException if the alias does not exist + */ + public function getAlias($id) + { + $id = (string) $id; + + if (!isset($this->aliasDefinitions[$id])) { + throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id)); + } + + return $this->aliasDefinitions[$id]; + } + + /** + * Registers a service definition. + * + * This methods allows for simple registration of service definition + * with a fluid interface. + * + * @param string $id The service identifier + * @param string $class|null The service class + * + * @return Definition A Definition instance + */ + public function register($id, $class = null) + { + return $this->setDefinition($id, new Definition($class)); + } + + /** + * Registers an autowired service definition. + * + * This method implements a shortcut for using setDefinition() with + * an autowired definition. + * + * @param string $id The service identifier + * @param null|string $class The service class + * + * @return Definition The created definition + */ + public function autowire($id, $class = null) + { + return $this->setDefinition($id, (new Definition($class))->setAutowired(true)); + } + + /** + * Adds the service definitions. + * + * @param Definition[] $definitions An array of service definitions + */ + public function addDefinitions(array $definitions) + { + foreach ($definitions as $id => $definition) { + $this->setDefinition($id, $definition); + } + } + + /** + * Sets the service definitions. + * + * @param Definition[] $definitions An array of service definitions + */ + public function setDefinitions(array $definitions) + { + $this->definitions = array(); + $this->addDefinitions($definitions); + } + + /** + * Gets all service definitions. + * + * @return Definition[] An array of Definition instances + */ + public function getDefinitions() + { + return $this->definitions; + } + + /** + * Sets a service definition. + * + * @param string $id The service identifier + * @param Definition $definition A Definition instance + * + * @return Definition the service definition + * + * @throws BadMethodCallException When this ContainerBuilder is compiled + */ + public function setDefinition($id, Definition $definition) + { + if ($this->isCompiled()) { + throw new BadMethodCallException('Adding definition to a compiled container is not allowed'); + } + + $id = (string) $id; + + unset($this->aliasDefinitions[$id], $this->removedIds[$id]); + + return $this->definitions[$id] = $definition; + } + + /** + * Returns true if a service definition exists under the given identifier. + * + * @param string $id The service identifier + * + * @return bool true if the service definition exists, false otherwise + */ + public function hasDefinition($id) + { + return isset($this->definitions[(string) $id]); + } + + /** + * Gets a service definition. + * + * @param string $id The service identifier + * + * @return Definition A Definition instance + * + * @throws ServiceNotFoundException if the service definition does not exist + */ + public function getDefinition($id) + { + $id = (string) $id; + + if (!isset($this->definitions[$id])) { + throw new ServiceNotFoundException($id); + } + + return $this->definitions[$id]; + } + + /** + * Gets a service definition by id or alias. + * + * The method "unaliases" recursively to return a Definition instance. + * + * @param string $id The service identifier or alias + * + * @return Definition A Definition instance + * + * @throws ServiceNotFoundException if the service definition does not exist + */ + public function findDefinition($id) + { + $id = (string) $id; + + $seen = array(); + while (isset($this->aliasDefinitions[$id])) { + $id = (string) $this->aliasDefinitions[$id]; + + if (isset($seen[$id])) { + $seen = array_values($seen); + $seen = \array_slice($seen, array_search($id, $seen)); + $seen[] = $id; + + throw new ServiceCircularReferenceException($id, $seen); + } + + $seen[$id] = $id; + } + + return $this->getDefinition($id); + } + + /** + * Creates a service for a service definition. + * + * @param Definition $definition A service definition instance + * @param string $id The service identifier + * @param bool $tryProxy Whether to try proxying the service with a lazy proxy + * + * @return object The service described by the service definition + * + * @throws RuntimeException When the factory definition is incomplete + * @throws RuntimeException When the service is a synthetic service + * @throws InvalidArgumentException When configure callable is not callable + */ + private function createService(Definition $definition, array &$inlineServices, $id = null, $tryProxy = true) + { + if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) { + return $inlineServices[$h]; + } + + if ($definition instanceof ChildDefinition) { + throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id)); + } + + if ($definition->isSynthetic()) { + throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id)); + } + + if ($definition->isDeprecated()) { + @trigger_error($definition->getDeprecationMessage($id), E_USER_DEPRECATED); + } + + if ($tryProxy && $definition->isLazy()) { + $proxy = $this + ->getProxyInstantiator() + ->instantiateProxy( + $this, + $definition, + $id, function () use ($definition, &$inlineServices, $id) { + return $this->createService($definition, $inlineServices, $id, false); + } + ); + $this->shareService($definition, $proxy, $id, $inlineServices); + + return $proxy; + } + + $parameterBag = $this->getParameterBag(); + + if (null !== $definition->getFile()) { + require_once $parameterBag->resolveValue($definition->getFile()); + } + + $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices); + + if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) { + return $this->services[$id]; + } + + if (null !== $factory = $definition->getFactory()) { + if (\is_array($factory)) { + $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices), $factory[1]); + } elseif (!\is_string($factory)) { + throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id)); + } + + $service = \call_user_func_array($factory, $arguments); + + if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) { + $r = new \ReflectionClass($factory[0]); + + if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) { + @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), E_USER_DEPRECATED); + } + } + } else { + $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass())); + + $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments); + + if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) { + @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), E_USER_DEPRECATED); + } + } + + if ($tryProxy || !$definition->isLazy()) { + // share only if proxying failed, or if not a proxy + $this->shareService($definition, $service, $id, $inlineServices); + } + + $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices); + foreach ($properties as $name => $value) { + $service->$name = $value; + } + + foreach ($definition->getMethodCalls() as $call) { + $this->callMethod($service, $call, $inlineServices); + } + + if ($callable = $definition->getConfigurator()) { + if (\is_array($callable)) { + $callable[0] = $parameterBag->resolveValue($callable[0]); + + if ($callable[0] instanceof Reference) { + $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices); + } elseif ($callable[0] instanceof Definition) { + $callable[0] = $this->createService($callable[0], $inlineServices); + } + } + + if (!\is_callable($callable)) { + throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', \get_class($service))); + } + + \call_user_func($callable, $service); + } + + return $service; + } + + /** + * Replaces service references by the real service instance and evaluates expressions. + * + * @param mixed $value A value + * + * @return mixed The same value with all service references replaced by + * the real service instances and all expressions evaluated + */ + public function resolveServices($value) + { + return $this->doResolveServices($value); + } + + private function doResolveServices($value, array &$inlineServices = array()) + { + if (\is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = $this->doResolveServices($v, $inlineServices); + } + } elseif ($value instanceof ServiceClosureArgument) { + $reference = $value->getValues()[0]; + $value = function () use ($reference) { + return $this->resolveServices($reference); + }; + } elseif ($value instanceof IteratorArgument) { + $value = new RewindableGenerator(function () use ($value) { + foreach ($value->getValues() as $k => $v) { + foreach (self::getServiceConditionals($v) as $s) { + if (!$this->has($s)) { + continue 2; + } + } + foreach (self::getInitializedConditionals($v) as $s) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) { + continue 2; + } + } + + yield $k => $this->resolveServices($v); + } + }, function () use ($value) { + $count = 0; + foreach ($value->getValues() as $v) { + foreach (self::getServiceConditionals($v) as $s) { + if (!$this->has($s)) { + continue 2; + } + } + foreach (self::getInitializedConditionals($v) as $s) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) { + continue 2; + } + } + + ++$count; + } + + return $count; + }); + } elseif ($value instanceof Reference) { + $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices); + } elseif ($value instanceof Definition) { + $value = $this->createService($value, $inlineServices); + } elseif ($value instanceof Parameter) { + $value = $this->getParameter((string) $value); + } elseif ($value instanceof Expression) { + $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this)); + } + + return $value; + } + + /** + * Returns service ids for a given tag. + * + * Example: + * + * $container->register('foo')->addTag('my.tag', array('hello' => 'world')); + * + * $serviceIds = $container->findTaggedServiceIds('my.tag'); + * foreach ($serviceIds as $serviceId => $tags) { + * foreach ($tags as $tag) { + * echo $tag['hello']; + * } + * } + * + * @param string $name + * @param bool $throwOnAbstract + * + * @return array An array of tags with the tagged service as key, holding a list of attribute arrays + */ + public function findTaggedServiceIds($name, $throwOnAbstract = false) + { + $this->usedTags[] = $name; + $tags = array(); + foreach ($this->getDefinitions() as $id => $definition) { + if ($definition->hasTag($name)) { + if ($throwOnAbstract && $definition->isAbstract()) { + throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name)); + } + $tags[$id] = $definition->getTag($name); + } + } + + return $tags; + } + + /** + * Returns all tags the defined services use. + * + * @return array An array of tags + */ + public function findTags() + { + $tags = array(); + foreach ($this->getDefinitions() as $id => $definition) { + $tags = array_merge(array_keys($definition->getTags()), $tags); + } + + return array_unique($tags); + } + + /** + * Returns all tags not queried by findTaggedServiceIds. + * + * @return string[] An array of tags + */ + public function findUnusedTags() + { + return array_values(array_diff($this->findTags(), $this->usedTags)); + } + + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguageProviders[] = $provider; + } + + /** + * @return ExpressionFunctionProviderInterface[] + */ + public function getExpressionLanguageProviders() + { + return $this->expressionLanguageProviders; + } + + /** + * Returns a ChildDefinition that will be used for autoconfiguring the interface/class. + * + * @param string $interface The class or interface to match + * + * @return ChildDefinition + */ + public function registerForAutoconfiguration($interface) + { + if (!isset($this->autoconfiguredInstanceof[$interface])) { + $this->autoconfiguredInstanceof[$interface] = new ChildDefinition(''); + } + + return $this->autoconfiguredInstanceof[$interface]; + } + + /** + * Returns an array of ChildDefinition[] keyed by interface. + * + * @return ChildDefinition[] + */ + public function getAutoconfiguredInstanceof() + { + return $this->autoconfiguredInstanceof; + } + + /** + * Resolves env parameter placeholders in a string or an array. + * + * @param mixed $value The value to resolve + * @param string|true|null $format A sprintf() format returning the replacement for each env var name or + * null to resolve back to the original "%env(VAR)%" format or + * true to resolve to the actual values of the referenced env vars + * @param array &$usedEnvs Env vars found while resolving are added to this array + * + * @return mixed The value with env parameters resolved if a string or an array is passed + */ + public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null) + { + if (null === $format) { + $format = '%%env(%s)%%'; + } + + $bag = $this->getParameterBag(); + if (true === $format) { + $value = $bag->resolveValue($value); + } + + if (\is_array($value)) { + $result = array(); + foreach ($value as $k => $v) { + $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs); + } + + return $result; + } + + if (!\is_string($value) || 38 > \strlen($value)) { + return $value; + } + $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; + + $completed = false; + foreach ($envPlaceholders as $env => $placeholders) { + foreach ($placeholders as $placeholder) { + if (false !== stripos($value, $placeholder)) { + if (true === $format) { + $resolved = $bag->escapeValue($this->getEnv($env)); + } else { + $resolved = sprintf($format, $env); + } + if ($placeholder === $value) { + $value = $resolved; + $completed = true; + } else { + if (!\is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type %s inside string value "%s".', $env, \gettype($resolved), $this->resolveEnvPlaceholders($value))); + } + $value = str_ireplace($placeholder, $resolved, $value); + } + $usedEnvs[$env] = $env; + $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1; + + if ($completed) { + break 2; + } + } + } + } + + return $value; + } + + /** + * Get statistics about env usage. + * + * @return int[] The number of time each env vars has been resolved + */ + public function getEnvCounters() + { + $bag = $this->getParameterBag(); + $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders; + + foreach ($envPlaceholders as $env => $placeholders) { + if (!isset($this->envCounters[$env])) { + $this->envCounters[$env] = 0; + } + } + + return $this->envCounters; + } + + /** + * @final + */ + public function log(CompilerPassInterface $pass, string $message) + { + $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message)); + } + + /** + * Returns the Service Conditionals. + * + * @param mixed $value An array of conditionals to return + * + * @return array An array of Service conditionals + * + * @internal + */ + public static function getServiceConditionals($value) + { + $services = array(); + + if (\is_array($value)) { + foreach ($value as $v) { + $services = array_unique(array_merge($services, self::getServiceConditionals($v))); + } + } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { + $services[] = (string) $value; + } + + return $services; + } + + /** + * Returns the initialized conditionals. + * + * @param mixed $value An array of conditionals to return + * + * @return array An array of uninitialized conditionals + * + * @internal + */ + public static function getInitializedConditionals($value) + { + $services = array(); + + if (\is_array($value)) { + foreach ($value as $v) { + $services = array_unique(array_merge($services, self::getInitializedConditionals($v))); + } + } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) { + $services[] = (string) $value; + } + + return $services; + } + + /** + * Computes a reasonably unique hash of a value. + * + * @param mixed $value A serializable value + * + * @return string + */ + public static function hash($value) + { + $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7); + + return str_replace(array('/', '+'), array('.', '_'), $hash); + } + + /** + * {@inheritdoc} + */ + protected function getEnv($name) + { + $value = parent::getEnv($name); + $bag = $this->getParameterBag(); + + if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) { + return $value; + } + + foreach ($bag->getEnvPlaceholders() as $env => $placeholders) { + if (isset($placeholders[$value])) { + $bag = new ParameterBag($bag->all()); + + return $bag->unescapeValue($bag->get("env($name)")); + } + } + + $this->resolving["env($name)"] = true; + try { + return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true)); + } finally { + unset($this->resolving["env($name)"]); + } + } + + /** + * Retrieves the currently set proxy instantiator or instantiates one. + */ + private function getProxyInstantiator(): InstantiatorInterface + { + if (!$this->proxyInstantiator) { + $this->proxyInstantiator = new RealServiceInstantiator(); + } + + return $this->proxyInstantiator; + } + + private function callMethod($service, $call, array &$inlineServices) + { + foreach (self::getServiceConditionals($call[1]) as $s) { + if (!$this->has($s)) { + return; + } + } + foreach (self::getInitializedConditionals($call[1]) as $s) { + if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) { + return; + } + } + + \call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices)); + } + + /** + * Shares a given service in the container. + * + * @param Definition $definition + * @param object $service + * @param string|null $id + */ + private function shareService(Definition $definition, $service, $id, array &$inlineServices) + { + $inlineServices[null !== $id ? $id : spl_object_hash($definition)] = $service; + + if (null !== $id && $definition->isShared()) { + $this->services[$id] = $service; + unset($this->loading[$id], $this->alreadyLoading[$id]); + } + } + + 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; + } + + private function inVendors($path) + { + if (null === $this->vendors) { + $resource = new ComposerResource(); + $this->vendors = $resource->getVendors(); + $this->addResource($resource); + } + $path = realpath($path) ?: $path; + + foreach ($this->vendors as $vendor) { + if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/symfony/dependency-injection/ContainerInterface.php b/vendor/symfony/dependency-injection/ContainerInterface.php new file mode 100644 index 0000000..2274ec7 --- /dev/null +++ b/vendor/symfony/dependency-injection/ContainerInterface.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +/** + * ContainerInterface is the interface implemented by service container classes. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +interface ContainerInterface extends PsrContainerInterface +{ + const EXCEPTION_ON_INVALID_REFERENCE = 1; + const NULL_ON_INVALID_REFERENCE = 2; + const IGNORE_ON_INVALID_REFERENCE = 3; + const IGNORE_ON_UNINITIALIZED_REFERENCE = 4; + + /** + * Sets a service. + * + * @param string $id The service identifier + * @param object $service The service instance + */ + public function set($id, $service); + + /** + * Gets a service. + * + * @param string $id The service identifier + * @param int $invalidBehavior The behavior when the service does not exist + * + * @return object The associated service + * + * @throws ServiceCircularReferenceException When a circular reference is detected + * @throws ServiceNotFoundException When the service is not defined + * + * @see Reference + */ + public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); + + /** + * Returns true if the given service is defined. + * + * @param string $id The service identifier + * + * @return bool true if the service is defined, false otherwise + */ + public function has($id); + + /** + * Check for whether or not a service has been initialized. + * + * @param string $id + * + * @return bool true if the service has been initialized, false otherwise + */ + public function initialized($id); + + /** + * Gets a parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws InvalidArgumentException if the parameter is not defined + */ + public function getParameter($name); + + /** + * Checks if a parameter exists. + * + * @param string $name The parameter name + * + * @return bool The presence of parameter in container + */ + public function hasParameter($name); + + /** + * Sets a parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + */ + public function setParameter($name, $value); +} diff --git a/vendor/symfony/dependency-injection/Definition.php b/vendor/symfony/dependency-injection/Definition.php new file mode 100644 index 0000000..c32d334 --- /dev/null +++ b/vendor/symfony/dependency-injection/Definition.php @@ -0,0 +1,884 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\OutOfBoundsException; + +/** + * Definition represents a service definition. + * + * @author Fabien Potencier + */ +class Definition +{ + private $class; + private $file; + private $factory; + private $shared = true; + private $deprecated = false; + private $deprecationTemplate; + private $properties = array(); + private $calls = array(); + private $instanceof = array(); + private $autoconfigured = false; + private $configurator; + private $tags = array(); + private $public = true; + private $private = true; + private $synthetic = false; + private $abstract = false; + private $lazy = false; + private $decoratedService; + private $autowired = false; + private $changes = array(); + private $bindings = array(); + private $errors = array(); + + protected $arguments = array(); + + private static $defaultDeprecationTemplate = 'The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed.'; + + /** + * @param string|null $class The service class + * @param array $arguments An array of arguments to pass to the service constructor + */ + public function __construct($class = null, array $arguments = array()) + { + if (null !== $class) { + $this->setClass($class); + } + $this->arguments = $arguments; + } + + /** + * Returns all changes tracked for the Definition object. + * + * @return array An array of changes for this Definition + */ + public function getChanges() + { + return $this->changes; + } + + /** + * Sets the tracked changes for the Definition object. + * + * @param array $changes An array of changes for this Definition + * + * @return $this + */ + public function setChanges(array $changes) + { + $this->changes = $changes; + + return $this; + } + + /** + * Sets a factory. + * + * @param string|array $factory A PHP function or an array containing a class/Reference and a method to call + * + * @return $this + */ + public function setFactory($factory) + { + $this->changes['factory'] = true; + + if (\is_string($factory) && false !== strpos($factory, '::')) { + $factory = explode('::', $factory, 2); + } + + $this->factory = $factory; + + return $this; + } + + /** + * Gets the factory. + * + * @return string|array The PHP function or an array containing a class/Reference and a method to call + */ + public function getFactory() + { + return $this->factory; + } + + /** + * Sets the service that this service is decorating. + * + * @param null|string $id The decorated service id, use null to remove decoration + * @param null|string $renamedId The new decorated service id + * @param int $priority The priority of decoration + * + * @return $this + * + * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals + */ + public function setDecoratedService($id, $renamedId = null, $priority = 0) + { + if ($renamedId && $id === $renamedId) { + throw new InvalidArgumentException(sprintf('The decorated service inner name for "%s" must be different than the service name itself.', $id)); + } + + $this->changes['decorated_service'] = true; + + if (null === $id) { + $this->decoratedService = null; + } else { + $this->decoratedService = array($id, $renamedId, (int) $priority); + } + + return $this; + } + + /** + * Gets the service that this service is decorating. + * + * @return null|array An array composed of the decorated service id, the new id for it and the priority of decoration, null if no service is decorated + */ + public function getDecoratedService() + { + return $this->decoratedService; + } + + /** + * Sets the service class. + * + * @param string $class The service class + * + * @return $this + */ + public function setClass($class) + { + $this->changes['class'] = true; + + $this->class = $class; + + return $this; + } + + /** + * Gets the service class. + * + * @return string|null The service class + */ + public function getClass() + { + return $this->class; + } + + /** + * Sets the arguments to pass to the service constructor/factory method. + * + * @return $this + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + + return $this; + } + + /** + * Sets the properties to define when creating the service. + * + * @return $this + */ + public function setProperties(array $properties) + { + $this->properties = $properties; + + return $this; + } + + /** + * Gets the properties to define when creating the service. + * + * @return array + */ + public function getProperties() + { + return $this->properties; + } + + /** + * Sets a specific property. + * + * @param string $name + * @param mixed $value + * + * @return $this + */ + public function setProperty($name, $value) + { + $this->properties[$name] = $value; + + return $this; + } + + /** + * Adds an argument to pass to the service constructor/factory method. + * + * @param mixed $argument An argument + * + * @return $this + */ + public function addArgument($argument) + { + $this->arguments[] = $argument; + + return $this; + } + + /** + * Replaces a specific argument. + * + * @param int|string $index + * @param mixed $argument + * + * @return $this + * + * @throws OutOfBoundsException When the replaced argument does not exist + */ + public function replaceArgument($index, $argument) + { + if (0 === \count($this->arguments)) { + throw new OutOfBoundsException('Cannot replace arguments if none have been configured yet.'); + } + + if (\is_int($index) && ($index < 0 || $index > \count($this->arguments) - 1)) { + throw new OutOfBoundsException(sprintf('The index "%d" is not in the range [0, %d].', $index, \count($this->arguments) - 1)); + } + + if (!array_key_exists($index, $this->arguments)) { + throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index)); + } + + $this->arguments[$index] = $argument; + + return $this; + } + + /** + * Sets a specific argument. + * + * @param int|string $key + * @param mixed $value + * + * @return $this + */ + public function setArgument($key, $value) + { + $this->arguments[$key] = $value; + + return $this; + } + + /** + * Gets the arguments to pass to the service constructor/factory method. + * + * @return array The array of arguments + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Gets an argument to pass to the service constructor/factory method. + * + * @param int|string $index + * + * @return mixed The argument value + * + * @throws OutOfBoundsException When the argument does not exist + */ + public function getArgument($index) + { + if (!array_key_exists($index, $this->arguments)) { + throw new OutOfBoundsException(sprintf('The argument "%s" doesn\'t exist.', $index)); + } + + return $this->arguments[$index]; + } + + /** + * Sets the methods to call after service initialization. + * + * @return $this + */ + public function setMethodCalls(array $calls = array()) + { + $this->calls = array(); + foreach ($calls as $call) { + $this->addMethodCall($call[0], $call[1]); + } + + return $this; + } + + /** + * Adds a method to call after service initialization. + * + * @param string $method The method name to call + * @param array $arguments An array of arguments to pass to the method call + * + * @return $this + * + * @throws InvalidArgumentException on empty $method param + */ + public function addMethodCall($method, array $arguments = array()) + { + if (empty($method)) { + throw new InvalidArgumentException('Method name cannot be empty.'); + } + $this->calls[] = array($method, $arguments); + + return $this; + } + + /** + * Removes a method to call after service initialization. + * + * @param string $method The method name to remove + * + * @return $this + */ + public function removeMethodCall($method) + { + foreach ($this->calls as $i => $call) { + if ($call[0] === $method) { + unset($this->calls[$i]); + break; + } + } + + return $this; + } + + /** + * Check if the current definition has a given method to call after service initialization. + * + * @param string $method The method name to search for + * + * @return bool + */ + public function hasMethodCall($method) + { + foreach ($this->calls as $call) { + if ($call[0] === $method) { + return true; + } + } + + return false; + } + + /** + * Gets the methods to call after service initialization. + * + * @return array An array of method calls + */ + public function getMethodCalls() + { + return $this->calls; + } + + /** + * Sets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. + * + * @param $instanceof ChildDefinition[] + * + * @return $this + */ + public function setInstanceofConditionals(array $instanceof) + { + $this->instanceof = $instanceof; + + return $this; + } + + /** + * Gets the definition templates to conditionally apply on the current definition, keyed by parent interface/class. + * + * @return ChildDefinition[] + */ + public function getInstanceofConditionals() + { + return $this->instanceof; + } + + /** + * Sets whether or not instanceof conditionals should be prepended with a global set. + * + * @param bool $autoconfigured + * + * @return $this + */ + public function setAutoconfigured($autoconfigured) + { + $this->changes['autoconfigured'] = true; + + $this->autoconfigured = $autoconfigured; + + return $this; + } + + /** + * @return bool + */ + public function isAutoconfigured() + { + return $this->autoconfigured; + } + + /** + * Sets tags for this definition. + * + * @return $this + */ + public function setTags(array $tags) + { + $this->tags = $tags; + + return $this; + } + + /** + * Returns all tags. + * + * @return array An array of tags + */ + public function getTags() + { + return $this->tags; + } + + /** + * Gets a tag by name. + * + * @param string $name The tag name + * + * @return array An array of attributes + */ + public function getTag($name) + { + return isset($this->tags[$name]) ? $this->tags[$name] : array(); + } + + /** + * Adds a tag for this definition. + * + * @param string $name The tag name + * @param array $attributes An array of attributes + * + * @return $this + */ + public function addTag($name, array $attributes = array()) + { + $this->tags[$name][] = $attributes; + + return $this; + } + + /** + * Whether this definition has a tag with the given name. + * + * @param string $name + * + * @return bool + */ + public function hasTag($name) + { + return isset($this->tags[$name]); + } + + /** + * Clears all tags for a given name. + * + * @param string $name The tag name + * + * @return $this + */ + public function clearTag($name) + { + unset($this->tags[$name]); + + return $this; + } + + /** + * Clears the tags for this definition. + * + * @return $this + */ + public function clearTags() + { + $this->tags = array(); + + return $this; + } + + /** + * Sets a file to require before creating the service. + * + * @param string $file A full pathname to include + * + * @return $this + */ + public function setFile($file) + { + $this->changes['file'] = true; + + $this->file = $file; + + return $this; + } + + /** + * Gets the file to require before creating the service. + * + * @return string|null The full pathname to include + */ + public function getFile() + { + return $this->file; + } + + /** + * Sets if the service must be shared or not. + * + * @param bool $shared Whether the service must be shared or not + * + * @return $this + */ + public function setShared($shared) + { + $this->changes['shared'] = true; + + $this->shared = (bool) $shared; + + return $this; + } + + /** + * Whether this service is shared. + * + * @return bool + */ + public function isShared() + { + return $this->shared; + } + + /** + * Sets the visibility of this service. + * + * @param bool $boolean + * + * @return $this + */ + public function setPublic($boolean) + { + $this->changes['public'] = true; + + $this->public = (bool) $boolean; + $this->private = false; + + return $this; + } + + /** + * Whether this service is public facing. + * + * @return bool + */ + public function isPublic() + { + return $this->public; + } + + /** + * Sets if this service is private. + * + * When set, the "private" state has a higher precedence than "public". + * In version 3.4, a "private" service always remains publicly accessible, + * but triggers a deprecation notice when accessed from the container, + * so that the service can be made really private in 4.0. + * + * @param bool $boolean + * + * @return $this + */ + public function setPrivate($boolean) + { + $this->private = (bool) $boolean; + + return $this; + } + + /** + * Whether this service is private. + * + * @return bool + */ + public function isPrivate() + { + return $this->private; + } + + /** + * Sets the lazy flag of this service. + * + * @param bool $lazy + * + * @return $this + */ + public function setLazy($lazy) + { + $this->changes['lazy'] = true; + + $this->lazy = (bool) $lazy; + + return $this; + } + + /** + * Whether this service is lazy. + * + * @return bool + */ + public function isLazy() + { + return $this->lazy; + } + + /** + * Sets whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @param bool $boolean + * + * @return $this + */ + public function setSynthetic($boolean) + { + $this->synthetic = (bool) $boolean; + + return $this; + } + + /** + * Whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @return bool + */ + public function isSynthetic() + { + return $this->synthetic; + } + + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @param bool $boolean + * + * @return $this + */ + public function setAbstract($boolean) + { + $this->abstract = (bool) $boolean; + + return $this; + } + + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @return bool + */ + public function isAbstract() + { + return $this->abstract; + } + + /** + * Whether this definition is deprecated, that means it should not be called + * anymore. + * + * @param bool $status + * @param string $template Template message to use if the definition is deprecated + * + * @return $this + * + * @throws InvalidArgumentException when the message template is invalid + */ + public function setDeprecated($status = true, $template = null) + { + if (null !== $template) { + if (preg_match('#[\r\n]|\*/#', $template)) { + throw new InvalidArgumentException('Invalid characters found in deprecation template.'); + } + + if (false === strpos($template, '%service_id%')) { + throw new InvalidArgumentException('The deprecation template must contain the "%service_id%" placeholder.'); + } + + $this->deprecationTemplate = $template; + } + + $this->changes['deprecated'] = true; + + $this->deprecated = (bool) $status; + + return $this; + } + + /** + * Whether this definition is deprecated, that means it should not be called + * anymore. + * + * @return bool + */ + public function isDeprecated() + { + return $this->deprecated; + } + + /** + * Message to use if this definition is deprecated. + * + * @param string $id Service id relying on this definition + * + * @return string + */ + public function getDeprecationMessage($id) + { + return str_replace('%service_id%', $id, $this->deprecationTemplate ?: self::$defaultDeprecationTemplate); + } + + /** + * Sets a configurator to call after the service is fully initialized. + * + * @param string|array $configurator A PHP callable + * + * @return $this + */ + public function setConfigurator($configurator) + { + $this->changes['configurator'] = true; + + if (\is_string($configurator) && false !== strpos($configurator, '::')) { + $configurator = explode('::', $configurator, 2); + } + + $this->configurator = $configurator; + + return $this; + } + + /** + * Gets the configurator to call after the service is fully initialized. + * + * @return callable|null The PHP callable to call + */ + public function getConfigurator() + { + return $this->configurator; + } + + /** + * Is the definition autowired? + * + * @return bool + */ + public function isAutowired() + { + return $this->autowired; + } + + /** + * Enables/disables autowiring. + * + * @param bool $autowired + * + * @return $this + */ + public function setAutowired($autowired) + { + $this->changes['autowired'] = true; + + $this->autowired = (bool) $autowired; + + return $this; + } + + /** + * Gets bindings. + * + * @return array + */ + public function getBindings() + { + return $this->bindings; + } + + /** + * Sets bindings. + * + * Bindings map $named or FQCN arguments to values that should be + * injected in the matching parameters (of the constructor, of methods + * called and of controller actions). + * + * @param array $bindings + * + * @return $this + */ + public function setBindings(array $bindings) + { + foreach ($bindings as $key => $binding) { + if (!$binding instanceof BoundArgument) { + $bindings[$key] = new BoundArgument($binding); + } + } + + $this->bindings = $bindings; + + return $this; + } + + /** + * Add an error that occurred when building this Definition. + * + * @param string $error + */ + public function addError($error) + { + $this->errors[] = $error; + } + + /** + * Returns any errors that occurred while building this Definition. + * + * @return array + */ + public function getErrors() + { + return $this->errors; + } +} diff --git a/vendor/symfony/dependency-injection/Dumper/Dumper.php b/vendor/symfony/dependency-injection/Dumper/Dumper.php new file mode 100644 index 0000000..e7407b0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Dumper/Dumper.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\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Dumper is the abstract class for all built-in dumpers. + * + * @author Fabien Potencier + */ +abstract class Dumper implements DumperInterface +{ + protected $container; + + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } +} diff --git a/vendor/symfony/dependency-injection/Dumper/DumperInterface.php b/vendor/symfony/dependency-injection/Dumper/DumperInterface.php new file mode 100644 index 0000000..dd001e4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Dumper/DumperInterface.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\DependencyInjection\Dumper; + +/** + * DumperInterface is the interface implemented by service container dumper classes. + * + * @author Fabien Potencier + */ +interface DumperInterface +{ + /** + * Dumps the service container. + * + * @param array $options An array of options + * + * @return string The representation of the service container + */ + public function dump(array $options = array()); +} diff --git a/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php b/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php new file mode 100644 index 0000000..9c35e06 --- /dev/null +++ b/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php @@ -0,0 +1,243 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; + +/** + * GraphvizDumper dumps a service container as a graphviz file. + * + * You can convert the generated dot file with the dot utility (http://www.graphviz.org/): + * + * dot -Tpng container.dot > foo.png + * + * @author Fabien Potencier + */ +class GraphvizDumper extends Dumper +{ + private $nodes; + private $edges; + private $options = array( + 'graph' => array('ratio' => 'compress'), + 'node' => array('fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'), + 'edge' => array('fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5), + 'node.instance' => array('fillcolor' => '#9999ff', 'style' => 'filled'), + 'node.definition' => array('fillcolor' => '#eeeeee'), + 'node.missing' => array('fillcolor' => '#ff9999', 'style' => 'filled'), + ); + + /** + * Dumps the service container as a graphviz graph. + * + * Available options: + * + * * graph: The default options for the whole graph + * * node: The default options for nodes + * * edge: The default options for edges + * * node.instance: The default options for services that are defined directly by object instances + * * node.definition: The default options for services that are defined via service definition instances + * * node.missing: The default options for missing services + * + * @return string The dot representation of the service container + */ + public function dump(array $options = array()) + { + foreach (array('graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing') as $key) { + if (isset($options[$key])) { + $this->options[$key] = array_merge($this->options[$key], $options[$key]); + } + } + + $this->nodes = $this->findNodes(); + + $this->edges = array(); + foreach ($this->container->getDefinitions() as $id => $definition) { + $this->edges[$id] = array_merge( + $this->findEdges($id, $definition->getArguments(), true, ''), + $this->findEdges($id, $definition->getProperties(), false, '') + ); + + foreach ($definition->getMethodCalls() as $call) { + $this->edges[$id] = array_merge( + $this->edges[$id], + $this->findEdges($id, $call[1], false, $call[0].'()') + ); + } + } + + return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__'); + } + + private function addNodes(): string + { + $code = ''; + foreach ($this->nodes as $id => $node) { + $aliases = $this->getAliases($id); + + $code .= sprintf(" node_%s [label=\"%s\\n%s\\n\", shape=%s%s];\n", $this->dotize($id), $id.($aliases ? ' ('.implode(', ', $aliases).')' : ''), $node['class'], $this->options['node']['shape'], $this->addAttributes($node['attributes'])); + } + + return $code; + } + + private function addEdges(): string + { + $code = ''; + foreach ($this->edges as $id => $edges) { + foreach ($edges as $edge) { + $code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"%s];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed', $edge['lazy'] ? ' color="#9999ff"' : ''); + } + } + + return $code; + } + + /** + * Finds all edges belonging to a specific service id. + */ + private function findEdges(string $id, array $arguments, bool $required, string $name, bool $lazy = false): array + { + $edges = array(); + foreach ($arguments as $argument) { + if ($argument instanceof Parameter) { + $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null; + } elseif (\is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) { + $argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null; + } + + if ($argument instanceof Reference) { + $lazyEdge = $lazy; + + if (!$this->container->has((string) $argument)) { + $this->nodes[(string) $argument] = array('name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']); + } elseif ('service_container' !== (string) $argument) { + $lazyEdge = $lazy || $this->container->getDefinition((string) $argument)->isLazy(); + } + + $edges[] = array('name' => $name, 'required' => $required, 'to' => $argument, 'lazy' => $lazyEdge); + } elseif ($argument instanceof ArgumentInterface) { + $edges = array_merge($edges, $this->findEdges($id, $argument->getValues(), $required, $name, true)); + } elseif (\is_array($argument)) { + $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name, $lazy)); + } + } + + return $edges; + } + + private function findNodes(): array + { + $nodes = array(); + + $container = $this->cloneContainer(); + + foreach ($container->getDefinitions() as $id => $definition) { + $class = $definition->getClass(); + + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + try { + $class = $this->container->getParameterBag()->resolveValue($class); + } catch (ParameterNotFoundException $e) { + } + + $nodes[$id] = array('class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], array('style' => $definition->isShared() ? 'filled' : 'dotted'))); + $container->setDefinition($id, new Definition('stdClass')); + } + + foreach ($container->getServiceIds() as $id) { + if (array_key_exists($id, $container->getAliases())) { + continue; + } + + if (!$container->hasDefinition($id)) { + $nodes[$id] = array('class' => str_replace('\\', '\\\\', \get_class($container->get($id))), 'attributes' => $this->options['node.instance']); + } + } + + return $nodes; + } + + private function cloneContainer() + { + $parameterBag = new ParameterBag($this->container->getParameterBag()->all()); + + $container = new ContainerBuilder($parameterBag); + $container->setDefinitions($this->container->getDefinitions()); + $container->setAliases($this->container->getAliases()); + $container->setResources($this->container->getResources()); + foreach ($this->container->getExtensions() as $extension) { + $container->registerExtension($extension); + } + + return $container; + } + + private function startDot(): string + { + return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n", + $this->addOptions($this->options['graph']), + $this->addOptions($this->options['node']), + $this->addOptions($this->options['edge']) + ); + } + + private function endDot(): string + { + return "}\n"; + } + + private function addAttributes(array $attributes): string + { + $code = array(); + foreach ($attributes as $k => $v) { + $code[] = sprintf('%s="%s"', $k, $v); + } + + return $code ? ', '.implode(', ', $code) : ''; + } + + private function addOptions(array $options): string + { + $code = array(); + foreach ($options as $k => $v) { + $code[] = sprintf('%s="%s"', $k, $v); + } + + return implode(' ', $code); + } + + private function dotize(string $id): string + { + return preg_replace('/\W/i', '_', $id); + } + + private function getAliases(string $id): array + { + $aliases = array(); + foreach ($this->container->getAliases() as $alias => $origin) { + if ($id == $origin) { + $aliases[] = $alias; + } + } + + return $aliases; + } +} diff --git a/vendor/symfony/dependency-injection/Dumper/PhpDumper.php b/vendor/symfony/dependency-injection/Dumper/PhpDumper.php new file mode 100644 index 0000000..6f51b0b --- /dev/null +++ b/vendor/symfony/dependency-injection/Dumper/PhpDumper.php @@ -0,0 +1,1833 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\EnvParameterException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\HttpKernel\Kernel; + +/** + * PhpDumper dumps a service container as a PHP class. + * + * @author Fabien Potencier + * @author Johannes M. Schmitt + */ +class PhpDumper extends Dumper +{ + /** + * Characters that might appear in the generated variable name as first character. + */ + const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; + + /** + * Characters that might appear in the generated variable name as any but the first character. + */ + const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; + + private $definitionVariables; + private $referenceVariables; + private $variableCount; + private $reservedVariables = array('instance', 'class'); + private $expressionLanguage; + private $targetDirRegex; + private $targetDirMaxMatches; + private $docStar; + private $serviceIdToMethodNameMap; + private $usedMethodNames; + private $namespace; + private $asFiles; + private $hotPathTag; + private $inlineRequires; + private $inlinedRequires = array(); + private $circularReferences = array(); + + /** + * @var ProxyDumper + */ + private $proxyDumper; + + /** + * {@inheritdoc} + */ + public function __construct(ContainerBuilder $container) + { + if (!$container->isCompiled()) { + throw new LogicException('Cannot dump an uncompiled container.'); + } + + parent::__construct($container); + } + + /** + * Sets the dumper to be used when dumping proxies in the generated container. + */ + public function setProxyDumper(ProxyDumper $proxyDumper) + { + $this->proxyDumper = $proxyDumper; + } + + /** + * Dumps the service container as a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * * namespace: The class namespace + * * as_files: To split the container in several files + * + * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set + * + * @throws EnvParameterException When an env var exists but has not been dumped + */ + public function dump(array $options = array()) + { + $this->targetDirRegex = null; + $this->inlinedRequires = array(); + $options = array_merge(array( + 'class' => 'ProjectServiceContainer', + 'base_class' => 'Container', + 'namespace' => '', + 'as_files' => false, + 'debug' => true, + 'hot_path_tag' => 'container.hot_path', + 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', + 'build_time' => time(), + ), $options); + + $this->namespace = $options['namespace']; + $this->asFiles = $options['as_files']; + $this->hotPathTag = $options['hot_path_tag']; + $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']); + + if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { + $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); + $baseClassWithNamespace = $baseClass; + } elseif ('Container' === $baseClass) { + $baseClassWithNamespace = Container::class; + } else { + $baseClassWithNamespace = $baseClass; + } + + $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); + + (new AnalyzeServiceReferencesPass())->process($this->container); + $this->circularReferences = array(); + $checkedNodes = array(); + foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + $currentPath = array($id => $id); + $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); + } + $this->container->getCompiler()->getServiceReferenceGraph()->clear(); + + $this->docStar = $options['debug'] ? '*' : ''; + + if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) { + // Build a regexp where the first root dirs are mandatory, + // but every other sub-dir is optional up to the full path in $dir + // Mandate at least 2 root dirs and not more that 5 optional dirs. + + $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir)); + $i = \count($dir); + + if (3 <= $i) { + $regex = ''; + $lastOptionalDir = $i > 8 ? $i - 5 : 3; + $this->targetDirMaxMatches = $i - $lastOptionalDir; + + while (--$i >= $lastOptionalDir) { + $regex = sprintf('(%s%s)?', preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); + } + + do { + $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; + } while (0 < --$i); + + $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; + } + } + + $code = + $this->startClass($options['class'], $baseClass, $baseClassWithNamespace). + $this->addServices(). + $this->addDefaultParametersMethod(). + $this->endClass() + ; + + if ($this->asFiles) { + $fileStart = <<container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if ($ids = array_keys($ids)) { + sort($ids); + $c = "doExport($id)." => true,\n"; + } + $files['removed-ids.php'] = $c .= ");\n"; + } + + foreach ($this->generateServiceFiles() as $file => $c) { + $files[$file] = $fileStart.$c; + } + foreach ($this->generateProxyClasses() as $file => $c) { + $files[$file] = " $c) { + $code["Container{$hash}/{$file}"] = $c; + } + array_pop($code); + $code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "namespace ? "\nnamespace {$this->namespace};\n" : ''; + $time = $options['build_time']; + $id = hash('crc32', $hash.$time); + + $code[$options['class'].'.php'] = << '$hash', + 'container.build_id' => '$id', + 'container.build_time' => $time, +), __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); + +EOF; + } else { + foreach ($this->generateProxyClasses() as $c) { + $code .= $c; + } + } + + $this->targetDirRegex = null; + $this->inlinedRequires = array(); + $this->circularReferences = array(); + + $unusedEnvs = array(); + foreach ($this->container->getEnvCounters() as $env => $use) { + if (!$use) { + $unusedEnvs[] = $env; + } + } + if ($unusedEnvs) { + throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.'); + } + + return $code; + } + + /** + * Retrieves the currently set proxy dumper or instantiates one. + */ + private function getProxyDumper(): ProxyDumper + { + if (!$this->proxyDumper) { + $this->proxyDumper = new NullDumper(); + } + + return $this->proxyDumper; + } + + private function addServiceLocalTempVariables(string $cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, \SplObjectStorage $allInlinedDefinitions): string + { + $allCalls = $calls = $behavior = array(); + + foreach ($allInlinedDefinitions as $def) { + $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $allCalls, false, $cId, $behavior, $allInlinedDefinitions[$def]); + } + + $isPreInstance = isset($inlinedDefinitions[$definition]) && isset($this->circularReferences[$cId]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared(); + foreach ($inlinedDefinitions as $def) { + $this->getServiceCallsFromArguments(array($def->getArguments(), $def->getFactory()), $calls, $isPreInstance, $cId); + if ($def !== $definition) { + $arguments = array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $calls, $isPreInstance && !$this->hasReference($cId, $arguments, true), $cId); + } + } + if (!isset($inlinedDefinitions[$definition])) { + $arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $calls, false, $cId); + } + + $code = ''; + foreach ($calls as $id => $callCount) { + if ('service_container' === $id || $id === $cId || isset($this->referenceVariables[$id])) { + continue; + } + if ($callCount <= 1 && $allCalls[$id] <= 1) { + continue; + } + + $name = $this->getNextVariableName(); + $this->referenceVariables[$id] = new Variable($name); + + $reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $behavior[$id] ? new Reference($id, $behavior[$id]) : null; + $code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($id, $reference)); + } + + if ('' !== $code) { + if ($isPreInstance) { + $code .= sprintf(<<<'EOTXT' + + if (isset($this->%s['%s'])) { + return $this->%1$s['%2$s']; + } + +EOTXT + , $definition->isPublic() ? 'services' : 'privates', $cId); + } + + $code .= "\n"; + } + + return $code; + } + + private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath) + { + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $id = $node->getId(); + + if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) { + // no-op + } elseif (isset($currentPath[$id])) { + foreach (array_reverse($currentPath) as $parentId) { + $this->circularReferences[$parentId][$id] = $id; + $id = $parentId; + } + } elseif (!isset($checkedNodes[$id])) { + $checkedNodes[$id] = true; + $currentPath[$id] = $id; + $this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath); + unset($currentPath[$id]); + } + } + } + + private function collectLineage($class, array &$lineage) + { + if (isset($lineage[$class])) { + return; + } + if (!$r = $this->container->getReflectionClass($class, false)) { + return; + } + if ($this->container instanceof $class) { + return; + } + $file = $r->getFileName(); + if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) { + return; + } + + if ($parent = $r->getParentClass()) { + $this->collectLineage($parent->name, $lineage); + } + + foreach ($r->getInterfaces() as $parent) { + $this->collectLineage($parent->name, $lineage); + } + + foreach ($r->getTraits() as $parent) { + $this->collectLineage($parent->name, $lineage); + } + + $lineage[$class] = substr($exportedFile, 1, -1); + } + + private function generateProxyClasses() + { + $alreadyGenerated = array(); + $definitions = $this->container->getDefinitions(); + $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); + $proxyDumper = $this->getProxyDumper(); + ksort($definitions); + foreach ($definitions as $definition) { + if (!$proxyDumper->isProxyCandidate($definition)) { + continue; + } + if (isset($alreadyGenerated[$class = $definition->getClass()])) { + continue; + } + $alreadyGenerated[$class] = true; + // register class' reflector for resource tracking + $this->container->getReflectionClass($class); + $proxyCode = "\n".$proxyDumper->getProxyCode($definition); + if ($strip) { + $proxyCode = " $proxyCode; + } + } + + private function addServiceInclude(string $cId, Definition $definition, \SplObjectStorage $inlinedDefinitions): string + { + $code = ''; + + if ($this->inlineRequires && !$this->isHotPath($definition)) { + $lineage = $calls = $behavior = array(); + foreach ($inlinedDefinitions as $def) { + if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) { + $this->collectLineage($class, $lineage); + } + $arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()); + $this->getServiceCallsFromArguments($arguments, $calls, false, $cId, $behavior, $inlinedDefinitions[$def]); + } + + foreach ($calls as $id => $callCount) { + if ('service_container' !== $id && $id !== $cId + && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior[$id] + && $this->container->has($id) + && $this->isTrivialInstance($def = $this->container->findDefinition($id)) + && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass()) + ) { + $this->collectLineage($class, $lineage); + } + } + + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + $code .= sprintf(" include_once %s;\n", $file); + } + } + + foreach ($inlinedDefinitions as $def) { + if ($file = $def->getFile()) { + $code .= sprintf(" include_once %s;\n", $this->dumpValue($file)); + } + } + + if ('' !== $code) { + $code .= "\n"; + } + + return $code; + } + + /** + * Generates the inline definition of a service. + * + * @throws RuntimeException When the factory definition is incomplete + * @throws ServiceCircularReferenceException When a circular reference is detected + */ + private function addServiceInlinedDefinitions(string $id, Definition $definition, \SplObjectStorage $inlinedDefinitions, bool &$isSimpleInstance): string + { + $code = ''; + + foreach ($inlinedDefinitions as $def) { + if ($definition === $def) { + continue; + } + if ($inlinedDefinitions[$def] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) { + continue; + } + if (isset($this->definitionVariables[$def])) { + $name = $this->definitionVariables[$def]; + } else { + $name = $this->getNextVariableName(); + $this->definitionVariables[$def] = new Variable($name); + } + + // a construct like: + // $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a); + // this is an indication for a wrong implementation, you can circumvent this problem + // by setting up your service structure like this: + // $b = new ServiceB(); + // $a = new ServiceA(ServiceB $b); + // $b->setServiceA(ServiceA $a); + if (isset($inlinedDefinitions[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) { + throw new ServiceCircularReferenceException($id, array($id)); + } + + $code .= $this->addNewInstance($def, '$'.$name, ' = ', $id); + + if (!$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { + $code .= $this->addServiceProperties($def, $name); + $code .= $this->addServiceMethodCalls($def, $name); + $code .= $this->addServiceConfigurator($def, $name); + } else { + $isSimpleInstance = false; + } + + $code .= "\n"; + } + + return $code; + } + + /** + * @throws InvalidArgumentException + * @throws RuntimeException + */ + private function addServiceInstance(string $id, Definition $definition, string $isSimpleInstance): string + { + $class = $this->dumpValue($definition->getClass()); + + if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); + } + + $isProxyCandidate = $this->getProxyDumper()->isProxyCandidate($definition); + $instantiation = ''; + + if (!$isProxyCandidate && $definition->isShared()) { + $instantiation = sprintf('$this->%s[\'%s\'] = %s', $this->container->getDefinition($id)->isPublic() ? 'services' : 'privates', $id, $isSimpleInstance ? '' : '$instance'); + } elseif (!$isSimpleInstance) { + $instantiation = '$instance'; + } + + $return = ''; + if ($isSimpleInstance) { + $return = 'return '; + } else { + $instantiation .= ' = '; + } + + $code = $this->addNewInstance($definition, $return, $instantiation, $id); + + if (!$isSimpleInstance) { + $code .= "\n"; + } + + return $code; + } + + private function isTrivialInstance(Definition $definition): bool + { + if ($definition->isSynthetic() || $definition->getFile() || $definition->getMethodCalls() || $definition->getProperties() || $definition->getConfigurator()) { + return false; + } + if ($definition->isDeprecated() || $definition->isLazy() || $definition->getFactory() || 3 < \count($definition->getArguments())) { + return false; + } + + foreach ($definition->getArguments() as $arg) { + if (!$arg || $arg instanceof Parameter) { + continue; + } + if (\is_array($arg) && 3 >= \count($arg)) { + foreach ($arg as $k => $v) { + if ($this->dumpValue($k) !== $this->dumpValue($k, false)) { + return false; + } + if (!$v || $v instanceof Parameter) { + continue; + } + if ($v instanceof Reference && $this->container->has($id = (string) $v) && $this->container->findDefinition($id)->isSynthetic()) { + continue; + } + if (!is_scalar($v) || $this->dumpValue($v) !== $this->dumpValue($v, false)) { + return false; + } + } + } elseif ($arg instanceof Reference && $this->container->has($id = (string) $arg) && $this->container->findDefinition($id)->isSynthetic()) { + continue; + } elseif (!is_scalar($arg) || $this->dumpValue($arg) !== $this->dumpValue($arg, false)) { + return false; + } + } + + if (false !== strpos($this->dumpLiteralClass($this->dumpValue($definition->getClass())), '$')) { + return false; + } + + return true; + } + + private function addServiceMethodCalls(Definition $definition, string $variableName = 'instance'): string + { + $calls = ''; + foreach ($definition->getMethodCalls() as $call) { + $arguments = array(); + foreach ($call[1] as $value) { + $arguments[] = $this->dumpValue($value); + } + + $calls .= $this->wrapServiceConditionals($call[1], sprintf(" \$%s->%s(%s);\n", $variableName, $call[0], implode(', ', $arguments))); + } + + return $calls; + } + + private function addServiceProperties(Definition $definition, $variableName = 'instance') + { + $code = ''; + foreach ($definition->getProperties() as $name => $value) { + $code .= sprintf(" \$%s->%s = %s;\n", $variableName, $name, $this->dumpValue($value)); + } + + return $code; + } + + /** + * @throws ServiceCircularReferenceException when the container contains a circular reference + */ + private function addServiceInlinedDefinitionsSetup(string $id, Definition $definition, \SplObjectStorage $inlinedDefinitions, bool $isSimpleInstance): string + { + $this->referenceVariables[$id] = new Variable('instance'); + + $code = ''; + foreach ($inlinedDefinitions as $def) { + if ($definition === $def || !$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true)) { + continue; + } + + // if the instance is simple, the return statement has already been generated + // so, the only possible way to get there is because of a circular reference + if ($isSimpleInstance) { + throw new ServiceCircularReferenceException($id, array($id)); + } + + $name = (string) $this->definitionVariables[$def]; + $code .= $this->addServiceProperties($def, $name); + $code .= $this->addServiceMethodCalls($def, $name); + $code .= $this->addServiceConfigurator($def, $name); + } + + if ('' !== $code && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) { + $code .= "\n"; + } + + return $code; + } + + private function addServiceConfigurator(Definition $definition, string $variableName = 'instance'): string + { + if (!$callable = $definition->getConfigurator()) { + return ''; + } + + if (\is_array($callable)) { + if ($callable[0] instanceof Reference + || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { + return sprintf(" %s->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + } + + $class = $this->dumpValue($callable[0]); + // If the class is a string we can optimize away + if (0 === strpos($class, "'") && false === strpos($class, '$')) { + return sprintf(" %s::%s(\$%s);\n", $this->dumpLiteralClass($class), $callable[1], $variableName); + } + + if (0 === strpos($class, 'new ')) { + return sprintf(" (%s)->%s(\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + } + + return sprintf(" [%s, '%s'](\$%s);\n", $this->dumpValue($callable[0]), $callable[1], $variableName); + } + + return sprintf(" %s(\$%s);\n", $callable, $variableName); + } + + private function addService(string $id, Definition $definition, string &$file = null): string + { + $this->definitionVariables = new \SplObjectStorage(); + $this->referenceVariables = array(); + $this->variableCount = 0; + + $return = array(); + + if ($class = $definition->getClass()) { + $class = $this->container->resolveEnvPlaceholders($class); + $return[] = sprintf(0 === strpos($class, '%') ? '@return object A %1$s instance' : '@return \%s', ltrim($class, '\\')); + } elseif ($definition->getFactory()) { + $factory = $definition->getFactory(); + if (\is_string($factory)) { + $return[] = sprintf('@return object An instance returned by %s()', $factory); + } elseif (\is_array($factory) && (\is_string($factory[0]) || $factory[0] instanceof Definition || $factory[0] instanceof Reference)) { + if (\is_string($factory[0]) || $factory[0] instanceof Reference) { + $return[] = sprintf('@return object An instance returned by %s::%s()', (string) $factory[0], $factory[1]); + } elseif ($factory[0] instanceof Definition) { + $return[] = sprintf('@return object An instance returned by %s::%s()', $factory[0]->getClass(), $factory[1]); + } + } + } + + if ($definition->isDeprecated()) { + if ($return && 0 === strpos($return[\count($return) - 1], '@return')) { + $return[] = ''; + } + + $return[] = sprintf('@deprecated %s', $definition->getDeprecationMessage($id)); + } + + $return = str_replace("\n * \n", "\n *\n", implode("\n * ", $return)); + $return = $this->container->resolveEnvPlaceholders($return); + + $shared = $definition->isShared() ? ' shared' : ''; + $public = $definition->isPublic() ? 'public' : 'private'; + $autowired = $definition->isAutowired() ? ' autowired' : ''; + + if ($definition->isLazy()) { + $lazyInitialization = '$lazyLoad = true'; + } else { + $lazyInitialization = ''; + } + + $asFile = $this->asFiles && $definition->isShared() && !$this->isHotPath($definition); + $methodName = $this->generateMethodName($id); + if ($asFile) { + $file = $methodName.'.php'; + $code = " // Returns the $public '$id'$shared$autowired service.\n\n"; + } else { + $code = <<docStar} + * Gets the $public '$id'$shared$autowired service. + * + * $return + */ + protected function {$methodName}($lazyInitialization) + { + +EOF; + } + + if ($this->getProxyDumper()->isProxyCandidate($definition)) { + $factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)'; + $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName)); + } + + if ($definition->isDeprecated()) { + $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); + } + + $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); + $constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory())); + $otherDefinitions = new \SplObjectStorage(); + + foreach ($inlinedDefinitions as $def) { + if ($def === $definition || isset($constructorDefinitions[$def])) { + $constructorDefinitions[$def] = $inlinedDefinitions[$def]; + } else { + $otherDefinitions[$def] = $inlinedDefinitions[$def]; + } + } + + $isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator(); + + $code .= + $this->addServiceInclude($id, $definition, $inlinedDefinitions). + $this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $inlinedDefinitions). + $this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance). + $this->addServiceInstance($id, $definition, $isSimpleInstance). + $this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $inlinedDefinitions). + $this->addServiceInlinedDefinitions($id, $definition, $otherDefinitions, $isSimpleInstance). + $this->addServiceInlinedDefinitionsSetup($id, $definition, $inlinedDefinitions, $isSimpleInstance). + $this->addServiceProperties($definition). + $this->addServiceMethodCalls($definition). + $this->addServiceConfigurator($definition). + (!$isSimpleInstance ? "\n return \$instance;\n" : '') + ; + + if ($asFile) { + $code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code))); + } else { + $code .= " }\n"; + } + + $this->definitionVariables = null; + $this->referenceVariables = null; + + return $code; + } + + private function addServices(): string + { + $publicServices = $privateServices = ''; + $definitions = $this->container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if ($definition->isSynthetic() || ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition))) { + continue; + } + if ($definition->isPublic()) { + $publicServices .= $this->addService($id, $definition); + } elseif (!$this->isTrivialInstance($definition)) { + $privateServices .= $this->addService($id, $definition); + } + } + + return $publicServices.$privateServices; + } + + private function generateServiceFiles() + { + $definitions = $this->container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if (!$definition->isSynthetic() && $definition->isShared() && !$this->isHotPath($definition)) { + $code = $this->addService($id, $definition, $file); + yield $file => $code; + } + } + } + + private function addNewInstance(Definition $definition, $return, $instantiation, $id) + { + $class = $this->dumpValue($definition->getClass()); + $return = ' '.$return.$instantiation; + + $arguments = array(); + foreach ($definition->getArguments() as $value) { + $arguments[] = $this->dumpValue($value); + } + + if (null !== $definition->getFactory()) { + $callable = $definition->getFactory(); + if (\is_array($callable)) { + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $callable[1])) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $callable[1] ?: 'n/a')); + } + + if ($callable[0] instanceof Reference + || ($callable[0] instanceof Definition && $this->definitionVariables->contains($callable[0]))) { + return $return.sprintf("%s->%s(%s);\n", $this->dumpValue($callable[0]), $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + + $class = $this->dumpValue($callable[0]); + // If the class is a string we can optimize away + if (0 === strpos($class, "'") && false === strpos($class, '$')) { + if ("''" === $class) { + throw new RuntimeException(sprintf('Cannot dump definition: The "%s" service is defined to be created by a factory but is missing the service reference, did you forget to define the factory service id or class?', $id)); + } + + return $return.sprintf("%s::%s(%s);\n", $this->dumpLiteralClass($class), $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + + if (0 === strpos($class, 'new ')) { + return $return.sprintf("(%s)->%s(%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + + return $return.sprintf("[%s, '%s'](%s);\n", $class, $callable[1], $arguments ? implode(', ', $arguments) : ''); + } + + return $return.sprintf("%s(%s);\n", $this->dumpLiteralClass($this->dumpValue($callable)), $arguments ? implode(', ', $arguments) : ''); + } + + if (false !== strpos($class, '$')) { + return sprintf(" \$class = %s;\n\n%snew \$class(%s);\n", $class, $return, implode(', ', $arguments)); + } + + return $return.sprintf("new %s(%s);\n", $this->dumpLiteralClass($class), implode(', ', $arguments)); + } + + private function startClass(string $class, string $baseClass, string $baseClassWithNamespace): string + { + $namespaceLine = !$this->asFiles && $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; + + $code = <<docStar} + * This class has been auto-generated + * by the Symfony Dependency Injection Component. + * + * @final since Symfony 3.3 + */ +class $class extends $baseClass +{ + private \$parameters; + private \$targetDirs = array(); + + /*{$this->docStar} + * @internal but protected for BC on cache:clear + */ + protected \$privates = array(); + + public function __construct() + { + +EOF; + if (null !== $this->targetDirRegex) { + $dir = $this->asFiles ? '$this->targetDirs[0] = \\dirname($containerDir)' : '__DIR__'; + $code .= <<targetDirMaxMatches}; ++\$i) { + \$this->targetDirs[\$i] = \$dir = \\dirname(\$dir); + } + +EOF; + } + if ($this->asFiles) { + $code = str_replace('$parameters', "\$buildParameters;\n private \$containerDir;\n private \$parameters", $code); + $code = str_replace('__construct()', '__construct(array $buildParameters = array(), $containerDir = __DIR__)', $code); + $code .= " \$this->buildParameters = \$buildParameters;\n"; + $code .= " \$this->containerDir = \$containerDir;\n"; + } + + if (Container::class !== $baseClassWithNamespace) { + $r = $this->container->getReflectionClass($baseClassWithNamespace, false); + if (null !== $r + && (null !== $constructor = $r->getConstructor()) + && 0 === $constructor->getNumberOfRequiredParameters() + && Container::class !== $constructor->getDeclaringClass()->name + ) { + $code .= " parent::__construct();\n"; + $code .= " \$this->parameterBag = null;\n\n"; + } + } + + if ($this->container->getParameterBag()->all()) { + $code .= " \$this->parameters = \$this->getDefaultParameters();\n\n"; + } + $code .= " \$this->services = \$this->privates = array();\n"; + + $code .= $this->addSyntheticIds(); + $code .= $this->addMethodMap(); + $code .= $this->asFiles ? $this->addFileMap() : ''; + $code .= $this->addAliases(); + $code .= $this->addInlineRequires(); + $code .= <<privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + +EOF; + $code .= $this->addRemovedIds(); + + if ($this->asFiles) { + $code .= <<containerDir.\\DIRECTORY_SEPARATOR.\$file; + } + +EOF; + } + + $proxyDumper = $this->getProxyDumper(); + foreach ($this->container->getDefinitions() as $definition) { + if (!$proxyDumper->isProxyCandidate($definition)) { + continue; + } + if ($this->asFiles) { + $proxyLoader = '$this->load("{$class}.php")'; + } elseif ($this->namespace) { + $proxyLoader = 'class_alias("'.$this->namespace.'\\\\{$class}", $class, false)'; + } else { + $proxyLoader = ''; + } + if ($proxyLoader) { + $proxyLoader = "class_exists(\$class, false) || {$proxyLoader};\n\n "; + } + $code .= <<container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if ($definition->isSynthetic() && 'service_container' !== $id) { + $code .= ' '.$this->doExport($id)." => true,\n"; + } + } + + return $code ? " \$this->syntheticIds = array(\n{$code} );\n" : ''; + } + + private function addRemovedIds(): string + { + $ids = $this->container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if (!$ids) { + return ''; + } + if ($this->asFiles) { + $code = "require \$this->containerDir.\\DIRECTORY_SEPARATOR.'removed-ids.php'"; + } else { + $code = ''; + $ids = array_keys($ids); + sort($ids); + foreach ($ids as $id) { + $code .= ' '.$this->doExport($id)." => true,\n"; + } + + $code = "array(\n{$code} )"; + } + + return <<container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || !$definition->isShared() || $this->isHotPath($definition))) { + $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; + } + } + + return $code ? " \$this->methodMap = array(\n{$code} );\n" : ''; + } + + private function addFileMap(): string + { + $code = ''; + $definitions = $this->container->getDefinitions(); + ksort($definitions); + foreach ($definitions as $id => $definition) { + if (!$definition->isSynthetic() && $definition->isPublic() && $definition->isShared() && !$this->isHotPath($definition)) { + $code .= sprintf(" %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id)); + } + } + + return $code ? " \$this->fileMap = array(\n{$code} );\n" : ''; + } + + private function addAliases(): string + { + if (!$aliases = $this->container->getAliases()) { + return "\n \$this->aliases = array();\n"; + } + + $code = " \$this->aliases = array(\n"; + ksort($aliases); + foreach ($aliases as $alias => $id) { + $id = (string) $id; + while (isset($aliases[$id])) { + $id = (string) $aliases[$id]; + } + $code .= ' '.$this->doExport($alias).' => '.$this->doExport($id).",\n"; + } + + return $code." );\n"; + } + + private function addInlineRequires(): string + { + if (!$this->hotPathTag || !$this->inlineRequires) { + return ''; + } + + $lineage = array(); + + foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) { + $definition = $this->container->getDefinition($id); + $inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition)); + + foreach ($inlinedDefinitions as $def) { + if (\is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) { + $this->collectLineage($class, $lineage); + } + } + } + + $code = ''; + + foreach ($lineage as $file) { + if (!isset($this->inlinedRequires[$file])) { + $this->inlinedRequires[$file] = true; + $code .= sprintf("\n include_once %s;", $file); + } + } + + return $code ? sprintf("\n \$this->privates['service_container'] = function () {%s\n };\n", $code) : ''; + } + + private function addDefaultParametersMethod(): string + { + if (!$this->container->getParameterBag()->all()) { + return ''; + } + + $php = array(); + $dynamicPhp = array(); + + foreach ($this->container->getParameterBag()->all() as $key => $value) { + if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) { + throw new InvalidArgumentException(sprintf('Parameter name cannot use env parameters: %s.', $resolvedKey)); + } + $export = $this->exportParameters(array($value)); + $export = explode('0 => ', substr(rtrim($export, " )\n"), 7, -1), 2); + + if (preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $export[1])) { + $dynamicPhp[$key] = sprintf('%scase %s: $value = %s; break;', $export[0], $this->export($key), $export[1]); + } else { + $php[] = sprintf('%s%s => %s,', $export[0], $this->export($key), $export[1]); + } + } + $parameters = sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', 8)); + + $code = <<<'EOF' + + public function getParameter($name) + { + $name = (string) $name; + if (isset($this->buildParameters[$name])) { + return $this->buildParameters[$name]; + } + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + if (isset($this->buildParameters[$name])) { + return true; + } + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + foreach ($this->buildParameters as $name => $value) { + $parameters[$name] = $value; + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + +EOF; + if (!$this->asFiles) { + $code = preg_replace('/^.*buildParameters.*\n.*\n.*\n/m', '', $code); + } + + if ($dynamicPhp) { + $loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8); + $getDynamicParameter = <<<'EOF' + switch ($name) { +%s + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; +EOF; + $getDynamicParameter = sprintf($getDynamicParameter, implode("\n", $dynamicPhp)); + } else { + $loadedDynamicParameters = 'array()'; + $getDynamicParameter = str_repeat(' ', 8).'throw new InvalidArgumentException(sprintf(\'The dynamic parameter "%s" must be defined.\', $name));'; + } + + $code .= <<docStar} + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter(\$name) + { +{$getDynamicParameter} + } + + /*{$this->docStar} + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return $parameters; + } + +EOF; + + return $code; + } + + /** + * @throws InvalidArgumentException + */ + private function exportParameters(array $parameters, string $path = '', int $indent = 12): string + { + $php = array(); + foreach ($parameters as $key => $value) { + if (\is_array($value)) { + $value = $this->exportParameters($value, $path.'/'.$key, $indent + 4); + } elseif ($value instanceof ArgumentInterface) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain special arguments. "%s" found in "%s".', \get_class($value), $path.'/'.$key)); + } elseif ($value instanceof Variable) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain variable references. Variable "%s" found in "%s".', $value, $path.'/'.$key)); + } elseif ($value instanceof Definition) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain service definitions. Definition for "%s" found in "%s".', $value->getClass(), $path.'/'.$key)); + } elseif ($value instanceof Reference) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain references to other services (reference to service "%s" found in "%s").', $value, $path.'/'.$key)); + } elseif ($value instanceof Expression) { + throw new InvalidArgumentException(sprintf('You cannot dump a container with parameters that contain expressions. Expression "%s" found in "%s".', $value, $path.'/'.$key)); + } else { + $value = $this->export($value); + } + + $php[] = sprintf('%s%s => %s,', str_repeat(' ', $indent), $this->export($key), $value); + } + + return sprintf("array(\n%s\n%s)", implode("\n", $php), str_repeat(' ', $indent - 4)); + } + + private function endClass(): string + { + return <<<'EOF' +} + +EOF; + } + + private function wrapServiceConditionals($value, string $code): string + { + if (!$condition = $this->getServiceConditionals($value)) { + return $code; + } + + // re-indent the wrapped code + $code = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code))); + + return sprintf(" if (%s) {\n%s }\n", $condition, $code); + } + + private function getServiceConditionals($value): string + { + $conditions = array(); + foreach (ContainerBuilder::getInitializedConditionals($value) as $service) { + if (!$this->container->hasDefinition($service)) { + return 'false'; + } + $conditions[] = sprintf("isset(\$this->%s['%s'])", $this->container->getDefinition($service)->isPublic() ? 'services' : 'privates', $service); + } + foreach (ContainerBuilder::getServiceConditionals($value) as $service) { + if ($this->container->hasDefinition($service) && !$this->container->getDefinition($service)->isPublic()) { + continue; + } + + $conditions[] = sprintf("\$this->has('%s')", $service); + } + + if (!$conditions) { + return ''; + } + + return implode(' && ', $conditions); + } + + private function getServiceCallsFromArguments(array $arguments, array &$calls, bool $isPreInstance, string $callerId, array &$behavior = array(), int $step = 1) + { + foreach ($arguments as $argument) { + if (\is_array($argument)) { + $this->getServiceCallsFromArguments($argument, $calls, $isPreInstance, $callerId, $behavior, $step); + } elseif ($argument instanceof Reference) { + $id = (string) $argument; + + if (!isset($calls[$id])) { + $calls[$id] = (int) ($isPreInstance && isset($this->circularReferences[$callerId][$id])); + } + if (!isset($behavior[$id])) { + $behavior[$id] = $argument->getInvalidBehavior(); + } else { + $behavior[$id] = min($behavior[$id], $argument->getInvalidBehavior()); + } + + $calls[$id] += $step; + } + } + } + + private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null): \SplObjectStorage + { + if (null === $definitions) { + $definitions = new \SplObjectStorage(); + } + + foreach ($arguments as $argument) { + if (\is_array($argument)) { + $this->getDefinitionsFromArguments($argument, $definitions); + } elseif (!$argument instanceof Definition) { + // no-op + } elseif (isset($definitions[$argument])) { + $definitions[$argument] = 1 + $definitions[$argument]; + } else { + $definitions[$argument] = 1; + $this->getDefinitionsFromArguments($argument->getArguments(), $definitions); + $this->getDefinitionsFromArguments(array($argument->getFactory()), $definitions); + $this->getDefinitionsFromArguments($argument->getProperties(), $definitions); + $this->getDefinitionsFromArguments($argument->getMethodCalls(), $definitions); + $this->getDefinitionsFromArguments(array($argument->getConfigurator()), $definitions); + // move current definition last in the list + $nbOccurences = $definitions[$argument]; + unset($definitions[$argument]); + $definitions[$argument] = $nbOccurences; + } + } + + return $definitions; + } + + private function hasReference(string $id, array $arguments, bool $deep = false, array &$visited = array()): bool + { + if (!isset($this->circularReferences[$id])) { + return false; + } + + foreach ($arguments as $argument) { + if (\is_array($argument)) { + if ($this->hasReference($id, $argument, $deep, $visited)) { + return true; + } + + continue; + } elseif ($argument instanceof Reference) { + $argumentId = (string) $argument; + if ($id === $argumentId) { + return true; + } + + if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$id][$argumentId])) { + continue; + } + + $visited[$argumentId] = true; + + $service = $this->container->getDefinition($argumentId); + } elseif ($argument instanceof Definition) { + $service = $argument; + } else { + continue; + } + + // if the proxy manager is enabled, disable searching for references in lazy services, + // as these services will be instantiated lazily and don't have direct related references. + if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) { + continue; + } + + if ($this->hasReference($id, array($service->getArguments(), $service->getFactory(), $service->getProperties(), $service->getMethodCalls(), $service->getConfigurator()), $deep, $visited)) { + return true; + } + } + + return false; + } + + /** + * @throws RuntimeException + */ + private function dumpValue($value, bool $interpolate = true): string + { + if (\is_array($value)) { + if ($value && $interpolate && false !== $param = array_search($value, $this->container->getParameterBag()->all(), true)) { + return $this->dumpValue("%$param%"); + } + $code = array(); + foreach ($value as $k => $v) { + $code[] = sprintf('%s => %s', $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate)); + } + + return sprintf('array(%s)', implode(', ', $code)); + } elseif ($value instanceof ArgumentInterface) { + $scope = array($this->definitionVariables, $this->referenceVariables, $this->variableCount); + $this->definitionVariables = $this->referenceVariables = null; + + try { + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + $code = $this->dumpValue($value, $interpolate); + + $returnedType = ''; + if ($value instanceof TypedReference) { + $returnedType = sprintf(': %s\%s', ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior() ? '' : '?', $value->getType()); + } + + $code = sprintf('return %s;', $code); + + return sprintf("function ()%s {\n %s\n }", $returnedType, $code); + } + + if ($value instanceof IteratorArgument) { + $operands = array(0); + $code = array(); + $code[] = 'new RewindableGenerator(function () {'; + + if (!$values = $value->getValues()) { + $code[] = ' return new \EmptyIterator();'; + } else { + $countCode = array(); + $countCode[] = 'function () {'; + + foreach ($values as $k => $v) { + ($c = $this->getServiceConditionals($v)) ? $operands[] = "(int) ($c)" : ++$operands[0]; + $v = $this->wrapServiceConditionals($v, sprintf(" yield %s => %s;\n", $this->dumpValue($k, $interpolate), $this->dumpValue($v, $interpolate))); + foreach (explode("\n", $v) as $v) { + if ($v) { + $code[] = ' '.$v; + } + } + } + + $countCode[] = sprintf(' return %s;', implode(' + ', $operands)); + $countCode[] = ' }'; + } + + $code[] = sprintf(' }, %s)', \count($operands) > 1 ? implode("\n", $countCode) : $operands[0]); + + return implode("\n", $code); + } + } finally { + list($this->definitionVariables, $this->referenceVariables, $this->variableCount) = $scope; + } + } elseif ($value instanceof Definition) { + if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) { + return $this->dumpValue($this->definitionVariables[$value], $interpolate); + } + if ($value->getMethodCalls()) { + throw new RuntimeException('Cannot dump definitions which have method calls.'); + } + if ($value->getProperties()) { + throw new RuntimeException('Cannot dump definitions which have properties.'); + } + if (null !== $value->getConfigurator()) { + throw new RuntimeException('Cannot dump definitions which have a configurator.'); + } + + $arguments = array(); + foreach ($value->getArguments() as $argument) { + $arguments[] = $this->dumpValue($argument); + } + + if (null !== $value->getFactory()) { + $factory = $value->getFactory(); + + if (\is_string($factory)) { + return sprintf('%s(%s)', $this->dumpLiteralClass($this->dumpValue($factory)), implode(', ', $arguments)); + } + + if (\is_array($factory)) { + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $factory[1])) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid factory method (%s)', $factory[1] ?: 'n/a')); + } + + $class = $this->dumpValue($factory[0]); + if (\is_string($factory[0])) { + return sprintf('%s::%s(%s)', $this->dumpLiteralClass($class), $factory[1], implode(', ', $arguments)); + } + + if ($factory[0] instanceof Definition) { + if (0 === strpos($class, 'new ')) { + return sprintf('(%s)->%s(%s)', $class, $factory[1], implode(', ', $arguments)); + } + + return sprintf("[%s, '%s'](%s)", $class, $factory[1], implode(', ', $arguments)); + } + + if ($factory[0] instanceof Reference) { + return sprintf('%s->%s(%s)', $class, $factory[1], implode(', ', $arguments)); + } + } + + throw new RuntimeException('Cannot dump definition because of invalid factory'); + } + + $class = $value->getClass(); + if (null === $class) { + throw new RuntimeException('Cannot dump definitions which have no class nor factory.'); + } + + return sprintf('new %s(%s)', $this->dumpLiteralClass($this->dumpValue($class)), implode(', ', $arguments)); + } elseif ($value instanceof Variable) { + return '$'.$value; + } elseif ($value instanceof Reference) { + $id = (string) $value; + if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) { + return $this->dumpValue($this->referenceVariables[$id], $interpolate); + } + + return $this->getServiceCall($id, $value); + } elseif ($value instanceof Expression) { + return $this->getExpressionLanguage()->compile((string) $value, array('this' => 'container')); + } elseif ($value instanceof Parameter) { + return $this->dumpParameter($value); + } elseif (true === $interpolate && \is_string($value)) { + if (preg_match('/^%([^%]+)%$/', $value, $match)) { + // we do this to deal with non string values (Boolean, integer, ...) + // the preg_replace_callback converts them to strings + return $this->dumpParameter($match[1]); + } else { + $replaceParameters = function ($match) { + return "'.".$this->dumpParameter($match[2]).".'"; + }; + + $code = str_replace('%%', '%', preg_replace_callback('/(?export($value))); + + return $code; + } + } elseif (\is_object($value) || \is_resource($value)) { + throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); + } + + return $this->export($value); + } + + /** + * Dumps a string to a literal (aka PHP Code) class value. + * + * @throws RuntimeException + */ + private function dumpLiteralClass(string $class): string + { + if (false !== strpos($class, '$')) { + return sprintf('${($_ = %s) && false ?: "_"}', $class); + } + if (0 !== strpos($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a')); + } + + $class = substr(str_replace('\\\\', '\\', $class), 1, -1); + + return 0 === strpos($class, '\\') ? $class : '\\'.$class; + } + + private function dumpParameter(string $name): string + { + if ($this->container->hasParameter($name)) { + $value = $this->container->getParameter($name); + $dumpedValue = $this->dumpValue($value, false); + + if (!$value || !\is_array($value)) { + return $dumpedValue; + } + + if (!preg_match("/\\\$this->(?:getEnv\('(?:\w++:)*+\w++'\)|targetDirs\[\d++\])/", $dumpedValue)) { + return sprintf("\$this->parameters['%s']", $name); + } + } + + return sprintf("\$this->getParameter('%s')", $name); + } + + private function getServiceCall(string $id, Reference $reference = null): string + { + while ($this->container->hasAlias($id)) { + $id = (string) $this->container->getAlias($id); + } + + if ('service_container' === $id) { + return '$this'; + } + + if ($this->container->hasDefinition($id) && $definition = $this->container->getDefinition($id)) { + if ($definition->isSynthetic()) { + $code = sprintf('$this->get(\'%s\'%s)', $id, null !== $reference ? ', '.$reference->getInvalidBehavior() : ''); + } elseif (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + $code = 'null'; + if (!$definition->isShared()) { + return $code; + } + } elseif ($this->isTrivialInstance($definition)) { + $code = substr($this->addNewInstance($definition, '', '', $id), 8, -2); + if ($definition->isShared()) { + $code = sprintf('$this->%s[\'%s\'] = %s', $definition->isPublic() ? 'services' : 'privates', $id, $code); + } + } elseif ($this->asFiles && $definition->isShared() && !$this->isHotPath($definition)) { + $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id)); + } else { + $code = sprintf('$this->%s()', $this->generateMethodName($id)); + } + if ($definition->isShared()) { + $code = sprintf('($this->%s[\'%s\'] ?? %s)', $definition->isPublic() ? 'services' : 'privates', $id, $code); + } + + return $code; + } + if (null !== $reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $reference->getInvalidBehavior()) { + return 'null'; + } + if (null !== $reference && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $reference->getInvalidBehavior()) { + $code = sprintf('$this->get(\'%s\', /* ContainerInterface::NULL_ON_INVALID_REFERENCE */ %d)', $id, ContainerInterface::NULL_ON_INVALID_REFERENCE); + } else { + $code = sprintf('$this->get(\'%s\')', $id); + } + + return sprintf('($this->services[\'%s\'] ?? %s)', $id, $code); + } + + /** + * Initializes the method names map to avoid conflicts with the Container methods. + */ + private function initializeMethodNamesMap(string $class) + { + $this->serviceIdToMethodNameMap = array(); + $this->usedMethodNames = array(); + + if ($reflectionClass = $this->container->getReflectionClass($class)) { + foreach ($reflectionClass->getMethods() as $method) { + $this->usedMethodNames[strtolower($method->getName())] = true; + } + } + } + + /** + * @throws InvalidArgumentException + */ + private function generateMethodName(string $id): string + { + if (isset($this->serviceIdToMethodNameMap[$id])) { + return $this->serviceIdToMethodNameMap[$id]; + } + + $i = strrpos($id, '\\'); + $name = Container::camelize(false !== $i && isset($id[1 + $i]) ? substr($id, 1 + $i) : $id); + $name = preg_replace('/[^a-zA-Z0-9_\x7f-\xff]/', '', $name); + $methodName = 'get'.$name.'Service'; + $suffix = 1; + + while (isset($this->usedMethodNames[strtolower($methodName)])) { + ++$suffix; + $methodName = 'get'.$name.$suffix.'Service'; + } + + $this->serviceIdToMethodNameMap[$id] = $methodName; + $this->usedMethodNames[strtolower($methodName)] = true; + + return $methodName; + } + + private function getNextVariableName(): string + { + $firstChars = self::FIRST_CHARS; + $firstCharsLength = \strlen($firstChars); + $nonFirstChars = self::NON_FIRST_CHARS; + $nonFirstCharsLength = \strlen($nonFirstChars); + + while (true) { + $name = ''; + $i = $this->variableCount; + + if ('' === $name) { + $name .= $firstChars[$i % $firstCharsLength]; + $i = (int) ($i / $firstCharsLength); + } + + while ($i > 0) { + --$i; + $name .= $nonFirstChars[$i % $nonFirstCharsLength]; + $i = (int) ($i / $nonFirstCharsLength); + } + + ++$this->variableCount; + + // check that the name is not reserved + if (\in_array($name, $this->reservedVariables, true)) { + continue; + } + + return $name; + } + } + + 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.'); + } + $providers = $this->container->getExpressionLanguageProviders(); + $this->expressionLanguage = new ExpressionLanguage(null, $providers, function ($arg) { + $id = '""' === substr_replace($arg, '', 1, -1) ? stripcslashes(substr($arg, 1, -1)) : null; + + if (null !== $id && ($this->container->hasAlias($id) || $this->container->hasDefinition($id))) { + return $this->getServiceCall($id); + } + + return sprintf('$this->get(%s)', $arg); + }); + + if ($this->container->isTrackingResources()) { + foreach ($providers as $provider) { + $this->container->addObjectResource($provider); + } + } + } + + return $this->expressionLanguage; + } + + private function isHotPath(Definition $definition) + { + return $this->hotPathTag && $definition->hasTag($this->hotPathTag) && !$definition->isDeprecated(); + } + + private function export($value) + { + if (null !== $this->targetDirRegex && \is_string($value) && preg_match($this->targetDirRegex, $value, $matches, PREG_OFFSET_CAPTURE)) { + $prefix = $matches[0][1] ? $this->doExport(substr($value, 0, $matches[0][1]), true).'.' : ''; + $suffix = $matches[0][1] + \strlen($matches[0][0]); + $suffix = isset($value[$suffix]) ? '.'.$this->doExport(substr($value, $suffix), true) : ''; + $dirname = $this->asFiles ? '$this->containerDir' : '__DIR__'; + $offset = 1 + $this->targetDirMaxMatches - \count($matches); + + if ($this->asFiles || 0 < $offset) { + $dirname = sprintf('$this->targetDirs[%d]', $offset); + } + + if ($prefix || $suffix) { + return sprintf('(%s%s%s)', $prefix, $dirname, $suffix); + } + + return $dirname; + } + + return $this->doExport($value, true); + } + + private function doExport($value, $resolveEnv = false) + { + if (\is_string($value) && false !== strpos($value, "\n")) { + $cleanParts = explode("\n", $value); + $cleanParts = array_map(function ($part) { return var_export($part, true); }, $cleanParts); + $export = implode('."\n".', $cleanParts); + } else { + $export = var_export($value, true); + } + + if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) { + $export = $resolvedExport; + if (".''" === substr($export, -3)) { + $export = substr($export, 0, -3); + if ("'" === $export[1]) { + $export = substr_replace($export, '', 18, 7); + } + } + if ("'" === $export[1]) { + $export = substr($export, 3); + } + } + + return $export; + } +} diff --git a/vendor/symfony/dependency-injection/Dumper/XmlDumper.php b/vendor/symfony/dependency-injection/Dumper/XmlDumper.php new file mode 100644 index 0000000..d945975 --- /dev/null +++ b/vendor/symfony/dependency-injection/Dumper/XmlDumper.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\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * XmlDumper dumps a service container as an XML string. + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class XmlDumper extends Dumper +{ + /** + * @var \DOMDocument + */ + private $document; + + /** + * Dumps the service container as an XML string. + * + * @return string An xml string representing of the service container + */ + public function dump(array $options = array()) + { + $this->document = new \DOMDocument('1.0', 'utf-8'); + $this->document->formatOutput = true; + + $container = $this->document->createElementNS('http://symfony.com/schema/dic/services', 'container'); + $container->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + $container->setAttribute('xsi:schemaLocation', 'http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd'); + + $this->addParameters($container); + $this->addServices($container); + + $this->document->appendChild($container); + $xml = $this->document->saveXML(); + $this->document = null; + + return $this->container->resolveEnvPlaceholders($xml); + } + + private function addParameters(\DOMElement $parent) + { + $data = $this->container->getParameterBag()->all(); + if (!$data) { + return; + } + + if ($this->container->isCompiled()) { + $data = $this->escape($data); + } + + $parameters = $this->document->createElement('parameters'); + $parent->appendChild($parameters); + $this->convertParameters($data, 'parameter', $parameters); + } + + private function addMethodCalls(array $methodcalls, \DOMElement $parent) + { + foreach ($methodcalls as $methodcall) { + $call = $this->document->createElement('call'); + $call->setAttribute('method', $methodcall[0]); + if (\count($methodcall[1])) { + $this->convertParameters($methodcall[1], 'argument', $call); + } + $parent->appendChild($call); + } + } + + /** + * Adds a service. + * + * @param Definition $definition + * @param string $id + * @param \DOMElement $parent + */ + private function addService($definition, $id, \DOMElement $parent) + { + $service = $this->document->createElement('service'); + if (null !== $id) { + $service->setAttribute('id', $id); + } + if ($class = $definition->getClass()) { + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + $service->setAttribute('class', $class); + } + if (!$definition->isShared()) { + $service->setAttribute('shared', 'false'); + } + if (!$definition->isPrivate()) { + $service->setAttribute('public', $definition->isPublic() ? 'true' : 'false'); + } + if ($definition->isSynthetic()) { + $service->setAttribute('synthetic', 'true'); + } + if ($definition->isLazy()) { + $service->setAttribute('lazy', 'true'); + } + if (null !== $decorated = $definition->getDecoratedService()) { + list($decorated, $renamedId, $priority) = $decorated; + $service->setAttribute('decorates', $decorated); + if (null !== $renamedId) { + $service->setAttribute('decoration-inner-name', $renamedId); + } + if (0 !== $priority) { + $service->setAttribute('decoration-priority', $priority); + } + } + + foreach ($definition->getTags() as $name => $tags) { + foreach ($tags as $attributes) { + $tag = $this->document->createElement('tag'); + $tag->setAttribute('name', $name); + foreach ($attributes as $key => $value) { + $tag->setAttribute($key, $value); + } + $service->appendChild($tag); + } + } + + if ($definition->getFile()) { + $file = $this->document->createElement('file'); + $file->appendChild($this->document->createTextNode($definition->getFile())); + $service->appendChild($file); + } + + if ($parameters = $definition->getArguments()) { + $this->convertParameters($parameters, 'argument', $service); + } + + if ($parameters = $definition->getProperties()) { + $this->convertParameters($parameters, 'property', $service, 'name'); + } + + $this->addMethodCalls($definition->getMethodCalls(), $service); + + if ($callable = $definition->getFactory()) { + $factory = $this->document->createElement('factory'); + + if (\is_array($callable) && $callable[0] instanceof Definition) { + $this->addService($callable[0], null, $factory); + $factory->setAttribute('method', $callable[1]); + } elseif (\is_array($callable)) { + if (null !== $callable[0]) { + $factory->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + } + $factory->setAttribute('method', $callable[1]); + } else { + $factory->setAttribute('function', $callable); + } + $service->appendChild($factory); + } + + if ($definition->isDeprecated()) { + $deprecated = $this->document->createElement('deprecated'); + $deprecated->appendChild($this->document->createTextNode($definition->getDeprecationMessage('%service_id%'))); + + $service->appendChild($deprecated); + } + + if ($definition->isAutowired()) { + $service->setAttribute('autowire', 'true'); + } + + if ($definition->isAutoconfigured()) { + $service->setAttribute('autoconfigure', 'true'); + } + + if ($definition->isAbstract()) { + $service->setAttribute('abstract', 'true'); + } + + if ($callable = $definition->getConfigurator()) { + $configurator = $this->document->createElement('configurator'); + + if (\is_array($callable) && $callable[0] instanceof Definition) { + $this->addService($callable[0], null, $configurator); + $configurator->setAttribute('method', $callable[1]); + } elseif (\is_array($callable)) { + $configurator->setAttribute($callable[0] instanceof Reference ? 'service' : 'class', $callable[0]); + $configurator->setAttribute('method', $callable[1]); + } else { + $configurator->setAttribute('function', $callable); + } + $service->appendChild($configurator); + } + + $parent->appendChild($service); + } + + /** + * Adds a service alias. + * + * @param string $alias + * @param Alias $id + * @param \DOMElement $parent + */ + private function addServiceAlias($alias, Alias $id, \DOMElement $parent) + { + $service = $this->document->createElement('service'); + $service->setAttribute('id', $alias); + $service->setAttribute('alias', $id); + if (!$id->isPrivate()) { + $service->setAttribute('public', $id->isPublic() ? 'true' : 'false'); + } + $parent->appendChild($service); + } + + private function addServices(\DOMElement $parent) + { + $definitions = $this->container->getDefinitions(); + if (!$definitions) { + return; + } + + $services = $this->document->createElement('services'); + foreach ($definitions as $id => $definition) { + $this->addService($definition, $id, $services); + } + + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $id) { + while (isset($aliases[(string) $id])) { + $id = $aliases[(string) $id]; + } + $this->addServiceAlias($alias, $id, $services); + } + $parent->appendChild($services); + } + + /** + * Converts parameters. + * + * @param array $parameters + * @param string $type + * @param \DOMElement $parent + * @param string $keyAttribute + */ + private function convertParameters(array $parameters, $type, \DOMElement $parent, $keyAttribute = 'key') + { + $withKeys = array_keys($parameters) !== range(0, \count($parameters) - 1); + foreach ($parameters as $key => $value) { + $element = $this->document->createElement($type); + if ($withKeys) { + $element->setAttribute($keyAttribute, $key); + } + + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + } + if (\is_array($value)) { + $element->setAttribute('type', 'collection'); + $this->convertParameters($value, $type, $element, 'key'); + } elseif ($value instanceof TaggedIteratorArgument) { + $element->setAttribute('type', 'tagged'); + $element->setAttribute('tag', $value->getTag()); + } elseif ($value instanceof IteratorArgument) { + $element->setAttribute('type', 'iterator'); + $this->convertParameters($value->getValues(), $type, $element, 'key'); + } elseif ($value instanceof Reference) { + $element->setAttribute('type', 'service'); + $element->setAttribute('id', (string) $value); + $behaviour = $value->getInvalidBehavior(); + if (ContainerInterface::NULL_ON_INVALID_REFERENCE == $behaviour) { + $element->setAttribute('on-invalid', 'null'); + } elseif (ContainerInterface::IGNORE_ON_INVALID_REFERENCE == $behaviour) { + $element->setAttribute('on-invalid', 'ignore'); + } elseif (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE == $behaviour) { + $element->setAttribute('on-invalid', 'ignore_uninitialized'); + } + } elseif ($value instanceof Definition) { + $element->setAttribute('type', 'service'); + $this->addService($value, null, $element); + } elseif ($value instanceof Expression) { + $element->setAttribute('type', 'expression'); + $text = $this->document->createTextNode(self::phpToXml((string) $value)); + $element->appendChild($text); + } else { + if (\in_array($value, array('null', 'true', 'false'), true)) { + $element->setAttribute('type', 'string'); + } + $text = $this->document->createTextNode(self::phpToXml($value)); + $element->appendChild($text); + } + $parent->appendChild($element); + } + } + + /** + * Escapes arguments. + * + * @return array + */ + private function escape(array $arguments) + { + $args = array(); + foreach ($arguments as $k => $v) { + if (\is_array($v)) { + $args[$k] = $this->escape($v); + } elseif (\is_string($v)) { + $args[$k] = str_replace('%', '%%', $v); + } else { + $args[$k] = $v; + } + } + + return $args; + } + + /** + * Converts php types to xml types. + * + * @param mixed $value Value to convert + * + * @return string + * + * @throws RuntimeException When trying to dump object or resource + */ + public static function phpToXml($value) + { + switch (true) { + case null === $value: + return 'null'; + case true === $value: + return 'true'; + case false === $value: + return 'false'; + case $value instanceof Parameter: + return '%'.$value.'%'; + case \is_object($value) || \is_resource($value): + throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); + default: + return (string) $value; + } + } +} diff --git a/vendor/symfony/dependency-injection/Dumper/YamlDumper.php b/vendor/symfony/dependency-injection/Dumper/YamlDumper.php new file mode 100644 index 0000000..0370cb9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Dumper/YamlDumper.php @@ -0,0 +1,319 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\Yaml\Dumper as YmlDumper; +use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Tag\TaggedValue; +use Symfony\Component\Yaml\Yaml; + +/** + * YamlDumper dumps a service container as a YAML string. + * + * @author Fabien Potencier + */ +class YamlDumper extends Dumper +{ + private $dumper; + + /** + * Dumps the service container as an YAML string. + * + * @return string A YAML string representing of the service container + */ + public function dump(array $options = array()) + { + if (!class_exists('Symfony\Component\Yaml\Dumper')) { + throw new RuntimeException('Unable to dump the container as the Symfony Yaml Component is not installed.'); + } + + if (null === $this->dumper) { + $this->dumper = new YmlDumper(); + } + + return $this->container->resolveEnvPlaceholders($this->addParameters()."\n".$this->addServices()); + } + + private function addService(string $id, Definition $definition): string + { + $code = " $id:\n"; + if ($class = $definition->getClass()) { + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + $code .= sprintf(" class: %s\n", $this->dumper->dump($class)); + } + + if (!$definition->isPrivate()) { + $code .= sprintf(" public: %s\n", $definition->isPublic() ? 'true' : 'false'); + } + + $tagsCode = ''; + foreach ($definition->getTags() as $name => $tags) { + foreach ($tags as $attributes) { + $att = array(); + foreach ($attributes as $key => $value) { + $att[] = sprintf('%s: %s', $this->dumper->dump($key), $this->dumper->dump($value)); + } + $att = $att ? ', '.implode(', ', $att) : ''; + + $tagsCode .= sprintf(" - { name: %s%s }\n", $this->dumper->dump($name), $att); + } + } + if ($tagsCode) { + $code .= " tags:\n".$tagsCode; + } + + if ($definition->getFile()) { + $code .= sprintf(" file: %s\n", $this->dumper->dump($definition->getFile())); + } + + if ($definition->isSynthetic()) { + $code .= " synthetic: true\n"; + } + + if ($definition->isDeprecated()) { + $code .= sprintf(" deprecated: %s\n", $this->dumper->dump($definition->getDeprecationMessage('%service_id%'))); + } + + if ($definition->isAutowired()) { + $code .= " autowire: true\n"; + } + + if ($definition->isAutoconfigured()) { + $code .= " autoconfigure: true\n"; + } + + if ($definition->isAbstract()) { + $code .= " abstract: true\n"; + } + + if ($definition->isLazy()) { + $code .= " lazy: true\n"; + } + + if ($definition->getArguments()) { + $code .= sprintf(" arguments: %s\n", $this->dumper->dump($this->dumpValue($definition->getArguments()), 0)); + } + + if ($definition->getProperties()) { + $code .= sprintf(" properties: %s\n", $this->dumper->dump($this->dumpValue($definition->getProperties()), 0)); + } + + if ($definition->getMethodCalls()) { + $code .= sprintf(" calls:\n%s\n", $this->dumper->dump($this->dumpValue($definition->getMethodCalls()), 1, 12)); + } + + if (!$definition->isShared()) { + $code .= " shared: false\n"; + } + + if (null !== $decorated = $definition->getDecoratedService()) { + list($decorated, $renamedId, $priority) = $decorated; + $code .= sprintf(" decorates: %s\n", $decorated); + if (null !== $renamedId) { + $code .= sprintf(" decoration_inner_name: %s\n", $renamedId); + } + if (0 !== $priority) { + $code .= sprintf(" decoration_priority: %s\n", $priority); + } + } + + if ($callable = $definition->getFactory()) { + $code .= sprintf(" factory: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); + } + + if ($callable = $definition->getConfigurator()) { + $code .= sprintf(" configurator: %s\n", $this->dumper->dump($this->dumpCallable($callable), 0)); + } + + return $code; + } + + private function addServiceAlias(string $alias, Alias $id): string + { + if ($id->isPrivate()) { + return sprintf(" %s: '@%s'\n", $alias, $id); + } + + return sprintf(" %s:\n alias: %s\n public: %s\n", $alias, $id, $id->isPublic() ? 'true' : 'false'); + } + + private function addServices(): string + { + if (!$this->container->getDefinitions()) { + return ''; + } + + $code = "services:\n"; + foreach ($this->container->getDefinitions() as $id => $definition) { + $code .= $this->addService($id, $definition); + } + + $aliases = $this->container->getAliases(); + foreach ($aliases as $alias => $id) { + while (isset($aliases[(string) $id])) { + $id = $aliases[(string) $id]; + } + $code .= $this->addServiceAlias($alias, $id); + } + + return $code; + } + + private function addParameters(): string + { + if (!$this->container->getParameterBag()->all()) { + return ''; + } + + $parameters = $this->prepareParameters($this->container->getParameterBag()->all(), $this->container->isCompiled()); + + return $this->dumper->dump(array('parameters' => $parameters), 2); + } + + /** + * Dumps callable to YAML format. + * + * @param callable $callable + * + * @return callable + */ + private function dumpCallable($callable) + { + if (\is_array($callable)) { + if ($callable[0] instanceof Reference) { + $callable = array($this->getServiceCall((string) $callable[0], $callable[0]), $callable[1]); + } else { + $callable = array($callable[0], $callable[1]); + } + } + + return $callable; + } + + /** + * Dumps the value to YAML format. + * + * @param mixed $value + * + * @return mixed + * + * @throws RuntimeException When trying to dump object or resource + */ + private function dumpValue($value) + { + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + } + if ($value instanceof ArgumentInterface) { + if ($value instanceof TaggedIteratorArgument) { + return new TaggedValue('tagged', $value->getTag()); + } + if ($value instanceof IteratorArgument) { + $tag = 'iterator'; + } else { + throw new RuntimeException(sprintf('Unspecified Yaml tag for type "%s".', \get_class($value))); + } + + return new TaggedValue($tag, $this->dumpValue($value->getValues())); + } + + if (\is_array($value)) { + $code = array(); + foreach ($value as $k => $v) { + $code[$k] = $this->dumpValue($v); + } + + return $code; + } elseif ($value instanceof Reference) { + return $this->getServiceCall((string) $value, $value); + } elseif ($value instanceof Parameter) { + return $this->getParameterCall((string) $value); + } elseif ($value instanceof Expression) { + return $this->getExpressionCall((string) $value); + } elseif ($value instanceof Definition) { + return new TaggedValue('service', (new Parser())->parse("_:\n".$this->addService('_', $value), Yaml::PARSE_CUSTOM_TAGS)['_']['_']); + } elseif (\is_object($value) || \is_resource($value)) { + throw new RuntimeException('Unable to dump a service container if a parameter is an object or a resource.'); + } + + return $value; + } + + private function getServiceCall(string $id, Reference $reference = null): string + { + if (null !== $reference) { + switch ($reference->getInvalidBehavior()) { + case ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE: break; + case ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE: return sprintf('@!%s', $id); + default: return sprintf('@?%s', $id); + } + } + + return sprintf('@%s', $id); + } + + private function getParameterCall(string $id): string + { + return sprintf('%%%s%%', $id); + } + + private function getExpressionCall($expression) + { + return sprintf('@=%s', $expression); + } + + private function prepareParameters(array $parameters, bool $escape = true): array + { + $filtered = array(); + foreach ($parameters as $key => $value) { + if (\is_array($value)) { + $value = $this->prepareParameters($value, $escape); + } elseif ($value instanceof Reference || \is_string($value) && 0 === strpos($value, '@')) { + $value = '@'.$value; + } + + $filtered[$key] = $value; + } + + return $escape ? $this->escape($filtered) : $filtered; + } + + private function escape(array $arguments): array + { + $args = array(); + foreach ($arguments as $k => $v) { + if (\is_array($v)) { + $args[$k] = $this->escape($v); + } elseif (\is_string($v)) { + $args[$k] = str_replace('%', '%%', $v); + } else { + $args[$k] = $v; + } + } + + return $args; + } +} diff --git a/vendor/symfony/dependency-injection/EnvVarProcessor.php b/vendor/symfony/dependency-injection/EnvVarProcessor.php new file mode 100644 index 0000000..1356807 --- /dev/null +++ b/vendor/symfony/dependency-injection/EnvVarProcessor.php @@ -0,0 +1,163 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * @author Nicolas Grekas + */ +class EnvVarProcessor implements EnvVarProcessorInterface +{ + private $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public static function getProvidedTypes() + { + return array( + 'base64' => 'string', + 'bool' => 'bool', + 'const' => 'bool|int|float|string|array', + 'file' => 'string', + 'float' => 'float', + 'int' => 'int', + 'json' => 'array', + 'resolve' => 'string', + 'string' => 'string', + ); + } + + /** + * {@inheritdoc} + */ + public function getEnv($prefix, $name, \Closure $getEnv) + { + $i = strpos($name, ':'); + + if ('file' === $prefix) { + if (!is_scalar($file = $getEnv($name))) { + throw new RuntimeException(sprintf('Invalid file name: env var "%s" is non-scalar.', $name)); + } + if (!file_exists($file)) { + throw new RuntimeException(sprintf('Env "file:%s" not found: %s does not exist.', $name, $file)); + } + + return file_get_contents($file); + } + + if (false !== $i || 'string' !== $prefix) { + if (null === $env = $getEnv($name)) { + return; + } + } elseif (isset($_ENV[$name])) { + $env = $_ENV[$name]; + } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { + $env = $_SERVER[$name]; + } elseif (false === ($env = getenv($name)) || null === $env) { // null is a possible value because of thread safety issues + if (!$this->container->hasParameter("env($name)")) { + throw new EnvNotFoundException($name); + } + + if (null === $env = $this->container->getParameter("env($name)")) { + return; + } + } + + if (!is_scalar($env)) { + throw new RuntimeException(sprintf('Non-scalar env var "%s" cannot be cast to %s.', $name, $prefix)); + } + + if ('string' === $prefix) { + return (string) $env; + } + + if ('bool' === $prefix) { + return (bool) self::phpize($env); + } + + if ('int' === $prefix) { + if (!is_numeric($env = self::phpize($env))) { + throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to int.', $name)); + } + + return (int) $env; + } + + if ('float' === $prefix) { + if (!is_numeric($env = self::phpize($env))) { + throw new RuntimeException(sprintf('Non-numeric env var "%s" cannot be cast to float.', $name)); + } + + return (float) $env; + } + + if ('const' === $prefix) { + if (!\defined($env)) { + throw new RuntimeException(sprintf('Env var "%s" maps to undefined constant "%s".', $name, $env)); + } + + return \constant($env); + } + + if ('base64' === $prefix) { + return base64_decode($env); + } + + if ('json' === $prefix) { + $env = json_decode($env, true); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new RuntimeException(sprintf('Invalid JSON in env var "%s": '.json_last_error_msg(), $name)); + } + + if (!\is_array($env)) { + throw new RuntimeException(sprintf('Invalid JSON env var "%s": array expected, %s given.', $name, \gettype($env))); + } + + return $env; + } + + if ('resolve' === $prefix) { + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($name) { + if (!isset($match[1])) { + return '%'; + } + $value = $this->container->getParameter($match[1]); + if (!is_scalar($value)) { + throw new RuntimeException(sprintf('Parameter "%s" found when resolving env var "%s" must be scalar, "%s" given.', $match[1], $name, \gettype($value))); + } + + return $value; + }, $env); + } + + throw new RuntimeException(sprintf('Unsupported env var prefix "%s".', $prefix)); + } + + private static function phpize($value) + { + if (!class_exists(XmlUtils::class)) { + throw new RuntimeException('The Symfony Config component is required to cast env vars to "bool", "int" or "float".'); + } + + return XmlUtils::phpize($value); + } +} diff --git a/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php b/vendor/symfony/dependency-injection/EnvVarProcessorInterface.php new file mode 100644 index 0000000..654fe55 --- /dev/null +++ b/vendor/symfony/dependency-injection/EnvVarProcessorInterface.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\DependencyInjection; + +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * The EnvVarProcessorInterface is implemented by objects that manage environment-like variables. + * + * @author Nicolas Grekas + */ +interface EnvVarProcessorInterface +{ + /** + * Returns the value of the given variable as managed by the current instance. + * + * @param string $prefix The namespace of the variable + * @param string $name The name of the variable within the namespace + * @param \Closure $getEnv A closure that allows fetching more env vars + * + * @return mixed + * + * @throws RuntimeException on error + */ + public function getEnv($prefix, $name, \Closure $getEnv); + + /** + * @return string[] The PHP-types managed by getEnv(), keyed by prefixes + */ + public static function getProvidedTypes(); +} diff --git a/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php b/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.php new file mode 100644 index 0000000..f198cd2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/AutowiringFailedException.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\DependencyInjection\Exception; + +/** + * Thrown when a definition cannot be autowired. + */ +class AutowiringFailedException extends RuntimeException +{ + private $serviceId; + + public function __construct(string $serviceId, string $message = '', int $code = 0, \Exception $previous = null) + { + $this->serviceId = $serviceId; + + parent::__construct($message, $code, $previous); + } + + public function getServiceId() + { + return $this->serviceId; + } +} diff --git a/vendor/symfony/dependency-injection/Exception/BadMethodCallException.php b/vendor/symfony/dependency-injection/Exception/BadMethodCallException.php new file mode 100644 index 0000000..959238e --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/BadMethodCallException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base BadMethodCallException for Dependency Injection component. + */ +class BadMethodCallException extends \BadMethodCallException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php b/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.php new file mode 100644 index 0000000..6ed18e0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/EnvNotFoundException.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\DependencyInjection\Exception; + +/** + * This exception is thrown when an environment variable is not found. + * + * @author Nicolas Grekas + */ +class EnvNotFoundException extends InvalidArgumentException +{ + public function __construct(string $name) + { + parent::__construct(sprintf('Environment variable not found: "%s".', $name)); + } +} diff --git a/vendor/symfony/dependency-injection/Exception/EnvParameterException.php b/vendor/symfony/dependency-injection/Exception/EnvParameterException.php new file mode 100644 index 0000000..c758ce1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/EnvParameterException.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\DependencyInjection\Exception; + +/** + * This exception wraps exceptions whose messages contain a reference to an env parameter. + * + * @author Nicolas Grekas + */ +class EnvParameterException extends InvalidArgumentException +{ + public function __construct(array $envs, \Exception $previous = null, string $message = 'Incompatible use of dynamic environment variables "%s" found in parameters.') + { + parent::__construct(sprintf($message, implode('", "', $envs)), 0, $previous); + } +} diff --git a/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php b/vendor/symfony/dependency-injection/Exception/ExceptionInterface.php new file mode 100644 index 0000000..5bec478 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/ExceptionInterface.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\DependencyInjection\Exception; + +use Psr\Container\ContainerExceptionInterface; + +/** + * Base ExceptionInterface for Dependency Injection component. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +interface ExceptionInterface extends ContainerExceptionInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Exception/InvalidArgumentException.php b/vendor/symfony/dependency-injection/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..119bb7d --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/InvalidArgumentException.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\DependencyInjection\Exception; + +/** + * Base InvalidArgumentException for Dependency Injection component. + * + * @author Bulat Shakirzyanov + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Exception/LogicException.php b/vendor/symfony/dependency-injection/Exception/LogicException.php new file mode 100644 index 0000000..17a070c --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/LogicException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base LogicException for Dependency Injection component. + */ +class LogicException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Exception/OutOfBoundsException.php b/vendor/symfony/dependency-injection/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..a61f143 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/OutOfBoundsException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Exception; + +/** + * Base OutOfBoundsException for Dependency Injection component. + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php b/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.php new file mode 100644 index 0000000..e17e402 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/ParameterCircularReferenceException.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\DependencyInjection\Exception; + +/** + * This exception is thrown when a circular reference in a parameter is detected. + * + * @author Fabien Potencier + */ +class ParameterCircularReferenceException extends RuntimeException +{ + private $parameters; + + public function __construct(array $parameters, \Exception $previous = null) + { + parent::__construct(sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], implode('" > "', $parameters), $parameters[0]), 0, $previous); + + $this->parameters = $parameters; + } + + public function getParameters() + { + return $this->parameters; + } +} diff --git a/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php b/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.php new file mode 100644 index 0000000..9eaa43b --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/ParameterNotFoundException.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\DependencyInjection\Exception; + +/** + * This exception is thrown when a non-existent parameter is used. + * + * @author Fabien Potencier + */ +class ParameterNotFoundException extends InvalidArgumentException +{ + private $key; + private $sourceId; + private $sourceKey; + private $alternatives; + private $nonNestedAlternative; + + /** + * @param string $key The requested parameter key + * @param string $sourceId The service id that references the non-existent parameter + * @param string $sourceKey The parameter key that references the non-existent parameter + * @param \Exception $previous The previous exception + * @param string[] $alternatives Some parameter name alternatives + * @param string|null $nonNestedAlternative The alternative parameter name when the user expected dot notation for nested parameters + */ + public function __construct(string $key, string $sourceId = null, string $sourceKey = null, \Exception $previous = null, array $alternatives = array(), string $nonNestedAlternative = null) + { + $this->key = $key; + $this->sourceId = $sourceId; + $this->sourceKey = $sourceKey; + $this->alternatives = $alternatives; + $this->nonNestedAlternative = $nonNestedAlternative; + + parent::__construct('', 0, $previous); + + $this->updateRepr(); + } + + public function updateRepr() + { + if (null !== $this->sourceId) { + $this->message = sprintf('The service "%s" has a dependency on a non-existent parameter "%s".', $this->sourceId, $this->key); + } elseif (null !== $this->sourceKey) { + $this->message = sprintf('The parameter "%s" has a dependency on a non-existent parameter "%s".', $this->sourceKey, $this->key); + } else { + $this->message = sprintf('You have requested a non-existent parameter "%s".', $this->key); + } + + if ($this->alternatives) { + if (1 == \count($this->alternatives)) { + $this->message .= ' Did you mean this: "'; + } else { + $this->message .= ' Did you mean one of these: "'; + } + $this->message .= implode('", "', $this->alternatives).'"?'; + } elseif (null !== $this->nonNestedAlternative) { + $this->message .= ' You cannot access nested array items, do you want to inject "'.$this->nonNestedAlternative.'" instead?'; + } + } + + public function getKey() + { + return $this->key; + } + + public function getSourceId() + { + return $this->sourceId; + } + + public function getSourceKey() + { + return $this->sourceKey; + } + + public function setSourceId($sourceId) + { + $this->sourceId = $sourceId; + + $this->updateRepr(); + } + + public function setSourceKey($sourceKey) + { + $this->sourceKey = $sourceKey; + + $this->updateRepr(); + } +} diff --git a/vendor/symfony/dependency-injection/Exception/RuntimeException.php b/vendor/symfony/dependency-injection/Exception/RuntimeException.php new file mode 100644 index 0000000..5c24541 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/RuntimeException.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\DependencyInjection\Exception; + +/** + * Base RuntimeException for Dependency Injection component. + * + * @author Johannes M. Schmitt + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php b/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.php new file mode 100644 index 0000000..5936b3a --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/ServiceCircularReferenceException.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\DependencyInjection\Exception; + +/** + * This exception is thrown when a circular reference is detected. + * + * @author Johannes M. Schmitt + */ +class ServiceCircularReferenceException extends RuntimeException +{ + private $serviceId; + private $path; + + public function __construct(string $serviceId, array $path, \Exception $previous = null) + { + parent::__construct(sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, implode(' -> ', $path)), 0, $previous); + + $this->serviceId = $serviceId; + $this->path = $path; + } + + public function getServiceId() + { + return $this->serviceId; + } + + public function getPath() + { + return $this->path; + } +} diff --git a/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php b/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.php new file mode 100644 index 0000000..d867ec6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Exception/ServiceNotFoundException.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\DependencyInjection\Exception; + +use Psr\Container\NotFoundExceptionInterface; + +/** + * This exception is thrown when a non-existent service is requested. + * + * @author Johannes M. Schmitt + */ +class ServiceNotFoundException extends InvalidArgumentException implements NotFoundExceptionInterface +{ + private $id; + private $sourceId; + private $alternatives; + + public function __construct(string $id, string $sourceId = null, \Exception $previous = null, array $alternatives = array(), string $msg = null) + { + if (null !== $msg) { + // no-op + } elseif (null === $sourceId) { + $msg = sprintf('You have requested a non-existent service "%s".', $id); + } else { + $msg = sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id); + } + + if ($alternatives) { + if (1 == \count($alternatives)) { + $msg .= ' Did you mean this: "'; + } else { + $msg .= ' Did you mean one of these: "'; + } + $msg .= implode('", "', $alternatives).'"?'; + } + + parent::__construct($msg, 0, $previous); + + $this->id = $id; + $this->sourceId = $sourceId; + $this->alternatives = $alternatives; + } + + public function getId() + { + return $this->id; + } + + public function getSourceId() + { + return $this->sourceId; + } + + public function getAlternatives() + { + return $this->alternatives; + } +} diff --git a/vendor/symfony/dependency-injection/ExpressionLanguage.php b/vendor/symfony/dependency-injection/ExpressionLanguage.php new file mode 100644 index 0000000..a64561c --- /dev/null +++ b/vendor/symfony/dependency-injection/ExpressionLanguage.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\DependencyInjection; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; + +/** + * Adds some function to the default ExpressionLanguage. + * + * @author Fabien Potencier + * + * @see ExpressionLanguageProvider + */ +class ExpressionLanguage extends BaseExpressionLanguage +{ + /** + * {@inheritdoc} + */ + public function __construct(CacheItemPoolInterface $cache = null, array $providers = array(), callable $serviceCompiler = null) + { + // prepend the default provider to let users override it easily + array_unshift($providers, new ExpressionLanguageProvider($serviceCompiler)); + + parent::__construct($cache, $providers); + } +} diff --git a/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php b/vendor/symfony/dependency-injection/ExpressionLanguageProvider.php new file mode 100644 index 0000000..e2084aa --- /dev/null +++ b/vendor/symfony/dependency-injection/ExpressionLanguageProvider.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\DependencyInjection; + +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + +/** + * Define some ExpressionLanguage functions. + * + * To get a service, use service('request'). + * To get a parameter, use parameter('kernel.debug'). + * + * @author Fabien Potencier + */ +class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface +{ + private $serviceCompiler; + + public function __construct(callable $serviceCompiler = null) + { + $this->serviceCompiler = $serviceCompiler; + } + + public function getFunctions() + { + return array( + new ExpressionFunction('service', $this->serviceCompiler ?: function ($arg) { + return sprintf('$this->get(%s)', $arg); + }, function (array $variables, $value) { + return $variables['container']->get($value); + }), + + new ExpressionFunction('parameter', function ($arg) { + return sprintf('$this->getParameter(%s)', $arg); + }, function (array $variables, $value) { + return $variables['container']->getParameter($value); + }), + ); + } +} diff --git a/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php b/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.php new file mode 100644 index 0000000..c3bd842 --- /dev/null +++ b/vendor/symfony/dependency-injection/Extension/ConfigurationExtensionInterface.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\DependencyInjection\Extension; + +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * ConfigurationExtensionInterface is the interface implemented by container extension classes. + * + * @author Kevin Bond + */ +interface ConfigurationExtensionInterface +{ + /** + * Returns extension configuration. + * + * @return ConfigurationInterface|null The configuration or null + */ + public function getConfiguration(array $config, ContainerBuilder $container); +} diff --git a/vendor/symfony/dependency-injection/Extension/Extension.php b/vendor/symfony/dependency-injection/Extension/Extension.php new file mode 100644 index 0000000..1bafdbe --- /dev/null +++ b/vendor/symfony/dependency-injection/Extension/Extension.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\DependencyInjection\Extension; + +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\BadMethodCallException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Provides useful features shared by many extensions. + * + * @author Fabien Potencier + */ +abstract class Extension implements ExtensionInterface, ConfigurationExtensionInterface +{ + private $processedConfigs = array(); + + /** + * {@inheritdoc} + */ + public function getXsdValidationBasePath() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function getNamespace() + { + return 'http://example.org/schema/dic/'.$this->getAlias(); + } + + /** + * Returns the recommended alias to use in XML. + * + * This alias is also the mandatory prefix to use when using YAML. + * + * This convention is to remove the "Extension" postfix from the class + * name and then lowercase and underscore the result. So: + * + * AcmeHelloExtension + * + * becomes + * + * acme_hello + * + * This can be overridden in a sub-class to specify the alias manually. + * + * @return string The alias + * + * @throws BadMethodCallException When the extension name does not follow conventions + */ + public function getAlias() + { + $className = \get_class($this); + if ('Extension' != substr($className, -9)) { + throw new BadMethodCallException('This extension does not follow the naming convention; you must overwrite the getAlias() method.'); + } + $classBaseName = substr(strrchr($className, '\\'), 1, -9); + + return Container::underscore($classBaseName); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + $class = \get_class($this); + $class = substr_replace($class, '\Configuration', strrpos($class, '\\')); + $class = $container->getReflectionClass($class); + $constructor = $class ? $class->getConstructor() : null; + + if ($class && (!$constructor || !$constructor->getNumberOfRequiredParameters())) { + return $class->newInstance(); + } + } + + final protected function processConfiguration(ConfigurationInterface $configuration, array $configs) + { + $processor = new Processor(); + + return $this->processedConfigs[] = $processor->processConfiguration($configuration, $configs); + } + + /** + * @internal + */ + final public function getProcessedConfigs() + { + try { + return $this->processedConfigs; + } finally { + $this->processedConfigs = array(); + } + } + + /** + * @return bool Whether the configuration is enabled + * + * @throws InvalidArgumentException When the config is not enableable + */ + protected function isConfigEnabled(ContainerBuilder $container, array $config) + { + if (!array_key_exists('enabled', $config)) { + throw new InvalidArgumentException("The config array has no 'enabled' key."); + } + + return (bool) $container->getParameterBag()->resolveValue($config['enabled']); + } +} diff --git a/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php b/vendor/symfony/dependency-injection/Extension/ExtensionInterface.php new file mode 100644 index 0000000..18de312 --- /dev/null +++ b/vendor/symfony/dependency-injection/Extension/ExtensionInterface.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\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * ExtensionInterface is the interface implemented by container extension classes. + * + * @author Fabien Potencier + */ +interface ExtensionInterface +{ + /** + * Loads a specific configuration. + * + * @throws \InvalidArgumentException When provided tag is not defined in this extension + */ + public function load(array $configs, ContainerBuilder $container); + + /** + * Returns the namespace to be used for this extension (XML namespace). + * + * @return string The XML namespace + */ + public function getNamespace(); + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath(); + + /** + * Returns the recommended alias to use in XML. + * + * This alias is also the mandatory prefix to use when using YAML. + * + * @return string The alias + */ + public function getAlias(); +} diff --git a/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php b/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.php new file mode 100644 index 0000000..5bd18d7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Extension/PrependExtensionInterface.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\DependencyInjection\Extension; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +interface PrependExtensionInterface +{ + /** + * Allow an extension to prepend the extension configurations. + */ + public function prepend(ContainerBuilder $container); +} diff --git a/vendor/symfony/dependency-injection/LICENSE b/vendor/symfony/dependency-injection/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/dependency-injection/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/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php b/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.php new file mode 100644 index 0000000..417ab90 --- /dev/null +++ b/vendor/symfony/dependency-injection/LazyProxy/Instantiator/InstantiatorInterface.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\DependencyInjection\LazyProxy\Instantiator; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; + +/** + * Lazy proxy instantiator, capable of instantiating a proxy given a container, the + * service definitions and a callback that produces the real service instance. + * + * @author Marco Pivetta + */ +interface InstantiatorInterface +{ + /** + * Instantiates a proxy object. + * + * @param ContainerInterface $container The container from which the service is being requested + * @param Definition $definition The definition of the requested service + * @param string $id Identifier of the requested service + * @param callable $realInstantiator Zero-argument callback that is capable of producing the real service instance + * + * @return object + */ + public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator); +} diff --git a/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php b/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.php new file mode 100644 index 0000000..3b0b57e --- /dev/null +++ b/vendor/symfony/dependency-injection/LazyProxy/Instantiator/RealServiceInstantiator.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\DependencyInjection\LazyProxy\Instantiator; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; + +/** + * {@inheritdoc} + * + * Noop proxy instantiator - simply produces the real service instead of a proxy instance. + * + * @author Marco Pivetta + */ +class RealServiceInstantiator implements InstantiatorInterface +{ + /** + * {@inheritdoc} + */ + public function instantiateProxy(ContainerInterface $container, Definition $definition, $id, $realInstantiator) + { + return \call_user_func($realInstantiator); + } +} diff --git a/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php b/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.php new file mode 100644 index 0000000..60787b7 --- /dev/null +++ b/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/DumperInterface.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\DependencyInjection\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Lazy proxy dumper capable of generating the instantiation logic PHP code for proxied services. + * + * @author Marco Pivetta + */ +interface DumperInterface +{ + /** + * Inspects whether the given definitions should produce proxy instantiation logic in the dumped container. + * + * @return bool + */ + public function isProxyCandidate(Definition $definition); + + /** + * Generates the code to be used to instantiate a proxy in the dumped factory code. + * + * @param Definition $definition + * @param string $id Service identifier + * @param string $factoryCode The code to execute to create the service + * + * @return string + */ + public function getProxyFactoryCode(Definition $definition, $id, $factoryCode); + + /** + * Generates the code for the lazy proxy. + * + * @return string + */ + public function getProxyCode(Definition $definition); +} diff --git a/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php b/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.php new file mode 100644 index 0000000..57c644c --- /dev/null +++ b/vendor/symfony/dependency-injection/LazyProxy/PhpDumper/NullDumper.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\DependencyInjection\LazyProxy\PhpDumper; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * Null dumper, negates any proxy code generation for any given service definition. + * + * @author Marco Pivetta + * + * @final + */ +class NullDumper implements DumperInterface +{ + /** + * {@inheritdoc} + */ + public function isProxyCandidate(Definition $definition) + { + return false; + } + + /** + * {@inheritdoc} + */ + public function getProxyFactoryCode(Definition $definition, $id, $factoryCode = null) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function getProxyCode(Definition $definition) + { + return ''; + } +} diff --git a/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php b/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.php new file mode 100644 index 0000000..65e432d --- /dev/null +++ b/vendor/symfony/dependency-injection/LazyProxy/ProxyHelper.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\DependencyInjection\LazyProxy; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class ProxyHelper +{ + /** + * @return string|null The FQCN or builtin name of the type hint, or null when the type hint references an invalid self|parent context + */ + public static function getTypeHint(\ReflectionFunctionAbstract $r, \ReflectionParameter $p = null, $noBuiltin = false) + { + if ($p instanceof \ReflectionParameter) { + $type = $p->getType(); + } else { + $type = $r->getReturnType(); + } + if (!$type) { + return; + } + if (!\is_string($type)) { + $name = $type->getName(); + + if ($type->isBuiltin()) { + return $noBuiltin ? null : $name; + } + } + $lcName = strtolower($name); + $prefix = $noBuiltin ? '' : '\\'; + + if ('self' !== $lcName && 'parent' !== $lcName) { + return $prefix.$name; + } + if (!$r instanceof \ReflectionMethod) { + return; + } + if ('self' === $lcName) { + return $prefix.$r->getDeclaringClass()->name; + } + if ($parent = $r->getDeclaringClass()->getParentClass()) { + return $prefix.$parent->name; + } + } +} diff --git a/vendor/symfony/dependency-injection/Loader/ClosureLoader.php b/vendor/symfony/dependency-injection/Loader/ClosureLoader.php new file mode 100644 index 0000000..183cacc --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/ClosureLoader.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\DependencyInjection\Loader; + +use Symfony\Component\Config\Loader\Loader; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * ClosureLoader loads service definitions from a PHP closure. + * + * The Closure has access to the container as its first argument. + * + * @author Fabien Potencier + */ +class ClosureLoader extends Loader +{ + private $container; + + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + \call_user_func($resource, $this->container); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return $resource instanceof \Closure; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.php new file mode 100644 index 0000000..d11347a --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/AbstractConfigurator.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 Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; + +abstract class AbstractConfigurator +{ + const FACTORY = 'unknown'; + + /** @internal */ + protected $definition; + + public function __call($method, $args) + { + if (method_exists($this, 'set'.$method)) { + return \call_user_func_array(array($this, 'set'.$method), $args); + } + + throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', \get_class($this), $method)); + } + + /** + * Checks that a value is valid, optionally replacing Definition and Reference configurators by their configure value. + * + * @param mixed $value + * @param bool $allowServices whether Definition and Reference are allowed; by default, only scalars and arrays are + * + * @return mixed the value, optionally cast to a Definition/Reference + */ + public static function processValue($value, $allowServices = false) + { + if (\is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = static::processValue($v, $allowServices); + } + + return $value; + } + + if ($value instanceof ReferenceConfigurator) { + return new Reference($value->id, $value->invalidBehavior); + } + + if ($value instanceof InlineServiceConfigurator) { + $def = $value->definition; + $value->definition = null; + + return $def; + } + + if ($value instanceof self) { + throw new InvalidArgumentException(sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY)); + } + + switch (true) { + case null === $value: + case is_scalar($value): + return $value; + + case $value instanceof ArgumentInterface: + case $value instanceof Definition: + case $value instanceof Expression: + case $value instanceof Parameter: + case $value instanceof Reference: + if ($allowServices) { + return $value; + } + } + + throw new InvalidArgumentException(sprintf('Cannot use values of type "%s" in service configuration files.', \is_object($value) ? \get_class($value) : \gettype($value))); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.php new file mode 100644 index 0000000..3d89e2a --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/AbstractServiceConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +abstract class AbstractServiceConfigurator extends AbstractConfigurator +{ + protected $parent; + protected $id; + private $defaultTags = array(); + + public function __construct(ServicesConfigurator $parent, Definition $definition, string $id = null, array $defaultTags = array()) + { + $this->parent = $parent; + $this->definition = $definition; + $this->id = $id; + $this->defaultTags = $defaultTags; + } + + public function __destruct() + { + // default tags should be added last + foreach ($this->defaultTags as $name => $attributes) { + foreach ($attributes as $attributes) { + $this->definition->addTag($name, $attributes); + } + } + $this->defaultTags = array(); + } + + /** + * Registers a service. + */ + final public function set(string $id, string $class = null): ServiceConfigurator + { + $this->__destruct(); + + return $this->parent->set($id, $class); + } + + /** + * Creates an alias. + */ + final public function alias(string $id, string $referencedId): AliasConfigurator + { + $this->__destruct(); + + return $this->parent->alias($id, $referencedId); + } + + /** + * Registers a PSR-4 namespace using a glob pattern. + */ + final public function load(string $namespace, string $resource): PrototypeConfigurator + { + $this->__destruct(); + + return $this->parent->load($namespace, $resource); + } + + /** + * Gets an already defined service definition. + * + * @throws ServiceNotFoundException if the service definition does not exist + */ + final public function get(string $id): ServiceConfigurator + { + $this->__destruct(); + + return $this->parent->get($id); + } + + /** + * Registers a service. + */ + final public function __invoke(string $id, string $class = null): ServiceConfigurator + { + $this->__destruct(); + + return $this->parent->set($id, $class); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.php new file mode 100644 index 0000000..cb00f58 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/AliasConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Alias; + +/** + * @author Nicolas Grekas + */ +class AliasConfigurator extends AbstractServiceConfigurator +{ + const FACTORY = 'alias'; + + use Traits\PublicTrait; + + public function __construct(ServicesConfigurator $parent, Alias $alias) + { + $this->parent = $parent; + $this->definition = $alias; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php new file mode 100644 index 0000000..eb867f1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * @author Nicolas Grekas + */ +class ContainerConfigurator extends AbstractConfigurator +{ + const FACTORY = 'container'; + + private $container; + private $loader; + private $instanceof; + private $path; + private $file; + + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof, string $path, string $file) + { + $this->container = $container; + $this->loader = $loader; + $this->instanceof = &$instanceof; + $this->path = $path; + $this->file = $file; + } + + final public function extension(string $namespace, array $config) + { + if (!$this->container->hasExtension($namespace)) { + $extensions = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf( + 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', + $namespace, + $this->file, + $namespace, + $extensions ? sprintf('"%s"', implode('", "', $extensions)) : 'none' + )); + } + + $this->container->loadFromExtension($namespace, static::processValue($config)); + } + + final public function import(string $resource, string $type = null, bool $ignoreErrors = false) + { + $this->loader->setCurrentDir(\dirname($this->path)); + $this->loader->import($resource, $type, $ignoreErrors, $this->file); + } + + final public function parameters(): ParametersConfigurator + { + return new ParametersConfigurator($this->container); + } + + final public function services(): ServicesConfigurator + { + return new ServicesConfigurator($this->container, $this->loader, $this->instanceof); + } +} + +/** + * Creates a service reference. + */ +function ref(string $id): ReferenceConfigurator +{ + return new ReferenceConfigurator($id); +} + +/** + * Creates an inline service. + */ +function inline(string $class = null): InlineServiceConfigurator +{ + return new InlineServiceConfigurator(new Definition($class)); +} + +/** + * Creates a lazy iterator. + * + * @param ReferenceConfigurator[] $values + */ +function iterator(array $values): IteratorArgument +{ + return new IteratorArgument(AbstractConfigurator::processValue($values, true)); +} + +/** + * Creates a lazy iterator by tag name. + */ +function tagged(string $tag): TaggedIteratorArgument +{ + return new TaggedIteratorArgument($tag); +} + +/** + * Creates an expression. + */ +function expr(string $expression): Expression +{ + return new Expression($expression); +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.php new file mode 100644 index 0000000..07f6b72 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/DefaultsConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas + */ +class DefaultsConfigurator extends AbstractServiceConfigurator +{ + const FACTORY = 'defaults'; + + use Traits\AutoconfigureTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\PublicTrait; + + /** + * Adds a tag for this definition. + * + * @return $this + * + * @throws InvalidArgumentException when an invalid tag name or attribute is provided + */ + final public function tag(string $name, array $attributes = array()) + { + if ('' === $name) { + throw new InvalidArgumentException('The tag name in "_defaults" must be a non-empty string.'); + } + + foreach ($attributes as $attribute => $value) { + if (null !== $value && !is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type.', $name, $attribute)); + } + } + + $this->definition->addTag($name, $attributes); + + return $this; + } + + /** + * Defines an instanceof-conditional to be applied to following service definitions. + * + * @return InstanceofConfigurator + */ + final public function instanceof(string $fqcn) + { + return $this->parent->instanceof($fqcn); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.php new file mode 100644 index 0000000..362b374 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/InlineServiceConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Nicolas Grekas + */ +class InlineServiceConfigurator extends AbstractConfigurator +{ + const FACTORY = 'inline'; + + use Traits\ArgumentTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\FactoryTrait; + use Traits\FileTrait; + use Traits\LazyTrait; + use Traits\ParentTrait; + use Traits\TagTrait; + + public function __construct(Definition $definition) + { + $this->definition = $definition; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.php new file mode 100644 index 0000000..78a8e3c --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/InstanceofConfigurator.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\DependencyInjection\Loader\Configurator; + +/** + * @author Nicolas Grekas + */ +class InstanceofConfigurator extends AbstractServiceConfigurator +{ + const FACTORY = 'instanceof'; + + use Traits\AutowireTrait; + use Traits\CallTrait; + use Traits\ConfiguratorTrait; + use Traits\LazyTrait; + use Traits\PropertyTrait; + use Traits\PublicTrait; + use Traits\ShareTrait; + use Traits\TagTrait; + + /** + * Defines an instanceof-conditional to be applied to following service definitions. + */ + final public function instanceof(string $fqcn): self + { + return $this->parent->instanceof($fqcn); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.php new file mode 100644 index 0000000..bacc60f --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/ParametersConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Nicolas Grekas + */ +class ParametersConfigurator extends AbstractConfigurator +{ + const FACTORY = 'parameters'; + + private $container; + + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } + + /** + * Creates a parameter. + * + * @return $this + */ + final public function set(string $name, $value) + { + $this->container->setParameter($name, static::processValue($value, true)); + + return $this; + } + + /** + * Creates a parameter. + * + * @return $this + */ + final public function __invoke(string $name, $value) + { + return $this->set($name, $value); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.php new file mode 100644 index 0000000..573dcc5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/PrototypeConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +/** + * @author Nicolas Grekas + */ +class PrototypeConfigurator extends AbstractServiceConfigurator +{ + const FACTORY = 'load'; + + use Traits\AbstractTrait; + use Traits\ArgumentTrait; + use Traits\AutoconfigureTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\CallTrait; + use Traits\ConfiguratorTrait; + use Traits\DeprecateTrait; + use Traits\FactoryTrait; + use Traits\LazyTrait; + use Traits\ParentTrait; + use Traits\PropertyTrait; + use Traits\PublicTrait; + use Traits\ShareTrait; + use Traits\TagTrait; + + private $loader; + private $resource; + private $exclude; + private $allowParent; + + public function __construct(ServicesConfigurator $parent, PhpFileLoader $loader, Definition $defaults, string $namespace, string $resource, bool $allowParent) + { + $definition = new Definition(); + $definition->setPublic($defaults->isPublic()); + $definition->setAutowired($defaults->isAutowired()); + $definition->setAutoconfigured($defaults->isAutoconfigured()); + $definition->setBindings($defaults->getBindings()); + $definition->setChanges(array()); + + $this->loader = $loader; + $this->resource = $resource; + $this->allowParent = $allowParent; + + parent::__construct($parent, $definition, $namespace, $defaults->getTags()); + } + + public function __destruct() + { + parent::__destruct(); + + if ($this->loader) { + $this->loader->registerClasses($this->definition, $this->id, $this->resource, $this->exclude); + } + $this->loader = null; + } + + /** + * Excludes files from registration using a glob pattern. + * + * @return $this + */ + final public function exclude(string $exclude) + { + $this->exclude = $exclude; + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.php new file mode 100644 index 0000000..590b60a --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/ReferenceConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * @author Nicolas Grekas + */ +class ReferenceConfigurator extends AbstractConfigurator +{ + /** @internal */ + protected $id; + + /** @internal */ + protected $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + + public function __construct(string $id) + { + $this->id = $id; + } + + /** + * @return $this + */ + final public function ignoreOnInvalid() + { + $this->invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + + return $this; + } + + /** + * @return $this + */ + final public function nullOnInvalid() + { + $this->invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + + return $this; + } + + /** + * @return $this + */ + final public function ignoreOnUninitialized() + { + $this->invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; + + return $this; + } + + public function __toString() + { + return $this->id; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.php new file mode 100644 index 0000000..8fc60fb --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/ServiceConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +/** + * @author Nicolas Grekas + */ +class ServiceConfigurator extends AbstractServiceConfigurator +{ + const FACTORY = 'services'; + + use Traits\AbstractTrait; + use Traits\ArgumentTrait; + use Traits\AutoconfigureTrait; + use Traits\AutowireTrait; + use Traits\BindTrait; + use Traits\CallTrait; + use Traits\ClassTrait; + use Traits\ConfiguratorTrait; + use Traits\DecorateTrait; + use Traits\DeprecateTrait; + use Traits\FactoryTrait; + use Traits\FileTrait; + use Traits\LazyTrait; + use Traits\ParentTrait; + use Traits\PropertyTrait; + use Traits\PublicTrait; + use Traits\ShareTrait; + use Traits\SyntheticTrait; + use Traits\TagTrait; + + private $container; + private $instanceof; + private $allowParent; + + public function __construct(ContainerBuilder $container, array $instanceof, bool $allowParent, ServicesConfigurator $parent, Definition $definition, $id, array $defaultTags) + { + $this->container = $container; + $this->instanceof = $instanceof; + $this->allowParent = $allowParent; + + parent::__construct($parent, $definition, $id, $defaultTags); + } + + public function __destruct() + { + parent::__destruct(); + + if (!$this->definition instanceof ChildDefinition) { + $this->container->setDefinition($this->id, $this->definition->setInstanceofConditionals($this->instanceof)); + } else { + $this->container->setDefinition($this->id, $this->definition); + } + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php b/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.php new file mode 100644 index 0000000..686a405 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/ServicesConfigurator.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\DependencyInjection\Loader\Configurator; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +/** + * @author Nicolas Grekas + */ +class ServicesConfigurator extends AbstractConfigurator +{ + const FACTORY = 'services'; + + private $defaults; + private $container; + private $loader; + private $instanceof; + + public function __construct(ContainerBuilder $container, PhpFileLoader $loader, array &$instanceof) + { + $this->defaults = new Definition(); + $this->container = $container; + $this->loader = $loader; + $this->instanceof = &$instanceof; + $instanceof = array(); + } + + /** + * Defines a set of defaults for following service definitions. + */ + final public function defaults(): DefaultsConfigurator + { + return new DefaultsConfigurator($this, $this->defaults = new Definition()); + } + + /** + * Defines an instanceof-conditional to be applied to following service definitions. + */ + final public function instanceof(string $fqcn): InstanceofConfigurator + { + $this->instanceof[$fqcn] = $definition = new ChildDefinition(''); + + return new InstanceofConfigurator($this, $definition, $fqcn); + } + + /** + * Registers a service. + */ + final public function set(string $id, string $class = null): ServiceConfigurator + { + $defaults = $this->defaults; + $allowParent = !$defaults->getChanges() && empty($this->instanceof); + + $definition = new Definition(); + $definition->setPublic($defaults->isPublic()); + $definition->setAutowired($defaults->isAutowired()); + $definition->setAutoconfigured($defaults->isAutoconfigured()); + $definition->setBindings($defaults->getBindings()); + $definition->setChanges(array()); + + $configurator = new ServiceConfigurator($this->container, $this->instanceof, $allowParent, $this, $definition, $id, $defaults->getTags()); + + return null !== $class ? $configurator->class($class) : $configurator; + } + + /** + * Creates an alias. + */ + final public function alias(string $id, string $referencedId): AliasConfigurator + { + $ref = static::processValue($referencedId, true); + $alias = new Alias((string) $ref, $this->defaults->isPublic()); + $this->container->setAlias($id, $alias); + + return new AliasConfigurator($this, $alias); + } + + /** + * Registers a PSR-4 namespace using a glob pattern. + */ + final public function load(string $namespace, string $resource): PrototypeConfigurator + { + $allowParent = !$this->defaults->getChanges() && empty($this->instanceof); + + return new PrototypeConfigurator($this, $this->loader, $this->defaults, $namespace, $resource, $allowParent); + } + + /** + * Gets an already defined service definition. + * + * @throws ServiceNotFoundException if the service definition does not exist + */ + final public function get(string $id): ServiceConfigurator + { + $allowParent = !$this->defaults->getChanges() && empty($this->instanceof); + $definition = $this->container->getDefinition($id); + + return new ServiceConfigurator($this->container, $definition->getInstanceofConditionals(), $allowParent, $this, $definition, $id, array()); + } + + /** + * Registers a service. + */ + final public function __invoke(string $id, string $class = null): ServiceConfigurator + { + return $this->set($id, $class); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.php new file mode 100644 index 0000000..57ddb48 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AbstractTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait AbstractTrait +{ + /** + * Whether this definition is abstract, that means it merely serves as a + * template for other definitions. + * + * @return $this + */ + final public function abstract(bool $abstract = true) + { + $this->definition->setAbstract($abstract); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.php new file mode 100644 index 0000000..7ec8c51 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ArgumentTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait ArgumentTrait +{ + /** + * Sets the arguments to pass to the service constructor/factory method. + * + * @param array $arguments An array of arguments + * + * @return $this + */ + final public function args(array $arguments) + { + $this->definition->setArguments(static::processValue($arguments, true)); + + return $this; + } + + /** + * Sets one argument to pass to the service constructor/factory method. + * + * @param string|int $key + * @param mixed $value + * + * @return $this + */ + final public function arg($key, $value) + { + $this->definition->setArgument($key, static::processValue($value, true)); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.php new file mode 100644 index 0000000..a923b3f --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutoconfigureTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait AutoconfigureTrait +{ + /** + * Sets whether or not instanceof conditionals should be prepended with a global set. + * + * @return $this + * + * @throws InvalidArgumentException when a parent is already set + */ + final public function autoconfigure(bool $autoconfigured = true) + { + if ($autoconfigured && $this->definition instanceof ChildDefinition) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service.', $this->id)); + } + $this->definition->setAutoconfigured($autoconfigured); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.php new file mode 100644 index 0000000..4c90af8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/AutowireTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait AutowireTrait +{ + /** + * Enables/disables autowiring. + * + * @return $this + */ + final public function autowire(bool $autowired = true) + { + $this->definition->setAutowired($autowired); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.php new file mode 100644 index 0000000..4511ed6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/BindTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +trait BindTrait +{ + /** + * Sets bindings. + * + * Bindings map $named or FQCN arguments to values that should be + * injected in the matching parameters (of the constructor, of methods + * called and of controller actions). + * + * @param string $nameOrFqcn A parameter name with its "$" prefix, or a FQCN + * @param mixed $valueOrRef The value or reference to bind + * + * @return $this + */ + final public function bind($nameOrFqcn, $valueOrRef) + { + $valueOrRef = static::processValue($valueOrRef, true); + if (isset($nameOrFqcn[0]) && '$' !== $nameOrFqcn[0] && !$valueOrRef instanceof Reference) { + throw new InvalidArgumentException(sprintf('Invalid binding for service "%s": named arguments must start with a "$", and FQCN must map to references. Neither applies to binding "%s".', $this->id, $nameOrFqcn)); + } + $bindings = $this->definition->getBindings(); + $bindings[$nameOrFqcn] = $valueOrRef; + $this->definition->setBindings($bindings); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.php new file mode 100644 index 0000000..abc14e2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/CallTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait CallTrait +{ + /** + * Adds a method to call after service initialization. + * + * @param string $method The method name to call + * @param array $arguments An array of arguments to pass to the method call + * + * @return $this + * + * @throws InvalidArgumentException on empty $method param + */ + final public function call($method, array $arguments = array()) + { + $this->definition->addMethodCall($method, static::processValue($arguments, true)); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.php new file mode 100644 index 0000000..ef44afe --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ClassTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait ClassTrait +{ + /** + * Sets the service class. + * + * @return $this + */ + final public function class($class) + { + $this->definition->setClass($class); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.php new file mode 100644 index 0000000..a38283b --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ConfiguratorTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait ConfiguratorTrait +{ + /** + * Sets a configurator to call after the service is fully initialized. + * + * @param string|array $configurator A PHP callable reference + * + * @return $this + */ + final public function configurator($configurator) + { + $this->definition->setConfigurator(static::processValue($configurator, true)); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.php new file mode 100644 index 0000000..0891fd9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DecorateTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait DecorateTrait +{ + /** + * Sets the service that this service is decorating. + * + * @param null|string $id The decorated service id, use null to remove decoration + * @param null|string $renamedId The new decorated service id + * @param int $priority The priority of decoration + * + * @return $this + * + * @throws InvalidArgumentException in case the decorated service id and the new decorated service id are equals + */ + final public function decorate($id, $renamedId = null, $priority = 0) + { + $this->definition->setDecoratedService($id, $renamedId, $priority); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.php new file mode 100644 index 0000000..b14a655 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/DeprecateTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait DeprecateTrait +{ + /** + * Whether this definition is deprecated, that means it should not be called anymore. + * + * @param string $template Template message to use if the definition is deprecated + * + * @return $this + * + * @throws InvalidArgumentException when the message template is invalid + */ + final public function deprecate($template = null) + { + $this->definition->setDeprecated(true, $template); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.php new file mode 100644 index 0000000..0d50fb7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FactoryTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait FactoryTrait +{ + /** + * Sets a factory. + * + * @param string|array $factory A PHP callable reference + * + * @return $this + */ + final public function factory($factory) + { + if (\is_string($factory) && 1 === substr_count($factory, ':')) { + $factoryParts = explode(':', $factory); + + throw new InvalidArgumentException(sprintf('Invalid factory "%s": the `service:method` notation is not available when using PHP-based DI configuration. Use "[ref(\'%s\'), \'%s\']" instead.', $factory, $factoryParts[0], $factoryParts[1])); + } + + $this->definition->setFactory(static::processValue($factory, true)); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.php new file mode 100644 index 0000000..895f530 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/FileTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait FileTrait +{ + /** + * Sets a file to require before creating the service. + * + * @param string $file A full pathname to include + * + * @return $this + */ + final public function file($file) + { + $this->definition->setFile($file); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.php new file mode 100644 index 0000000..2af867c --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/LazyTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait LazyTrait +{ + /** + * Sets the lazy flag of this service. + * + * @return $this + */ + final public function lazy(bool $lazy = true) + { + $this->definition->setLazy($lazy); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.php new file mode 100644 index 0000000..4eba03d --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ParentTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait ParentTrait +{ + /** + * Sets the Definition to inherit from. + * + * @return $this + * + * @throws InvalidArgumentException when parent cannot be set + */ + final public function parent(string $parent) + { + if (!$this->allowParent) { + throw new InvalidArgumentException(sprintf('A parent cannot be defined when either "_instanceof" or "_defaults" are also defined for service prototype "%s".', $this->id)); + } + + if ($this->definition instanceof ChildDefinition) { + $this->definition->setParent($parent); + } elseif ($this->definition->isAutoconfigured()) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service.', $this->id)); + } elseif ($this->definition->getBindings()) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also "bind" arguments.', $this->id)); + } else { + // cast Definition to ChildDefinition + $definition = serialize($this->definition); + $definition = substr_replace($definition, '53', 2, 2); + $definition = substr_replace($definition, 'Child', 44, 0); + $definition = unserialize($definition); + + $this->definition = $definition->setParent($parent); + } + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.php new file mode 100644 index 0000000..50c2ee8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PropertyTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait PropertyTrait +{ + /** + * Sets a specific property. + * + * @return $this + */ + final public function property(string $name, $value) + { + $this->definition->setProperty($name, static::processValue($value, true)); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.php new file mode 100644 index 0000000..9886523 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/PublicTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait PublicTrait +{ + /** + * @return $this + */ + final public function public() + { + $this->definition->setPublic(true); + + return $this; + } + + /** + * @return $this + */ + final public function private() + { + $this->definition->setPublic(false); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.php new file mode 100644 index 0000000..1c2f97b --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/ShareTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait ShareTrait +{ + /** + * Sets if the service must be shared or not. + * + * @param bool $shared Whether the service must be shared or not + * + * @return $this + */ + final public function share($shared = true) + { + $this->definition->setShared($shared); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.php new file mode 100644 index 0000000..bf124f0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/SyntheticTrait.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\DependencyInjection\Loader\Configurator\Traits; + +trait SyntheticTrait +{ + /** + * Sets whether this definition is synthetic, that is not constructed by the + * container, but dynamically injected. + * + * @return $this + */ + final public function synthetic(bool $synthetic = true) + { + $this->definition->setSynthetic($synthetic); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.php new file mode 100644 index 0000000..c165b65 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/Configurator/Traits/TagTrait.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\DependencyInjection\Loader\Configurator\Traits; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +trait TagTrait +{ + /** + * Adds a tag for this definition. + * + * @param string $name The tag name + * @param array $attributes An array of attributes + * + * @return $this + */ + final public function tag($name, array $attributes = array()) + { + if (!\is_string($name) || '' === $name) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" must be a non-empty string.', $this->id)); + } + + foreach ($attributes as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('A tag attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s".', $this->id, $name, $attribute)); + } + } + + $this->definition->addTag($name, $attributes); + + return $this; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php b/vendor/symfony/dependency-injection/Loader/DirectoryLoader.php new file mode 100644 index 0000000..a57cac3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/DirectoryLoader.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\DependencyInjection\Loader; + +/** + * DirectoryLoader is a recursive loader to go through directories. + * + * @author Sebastien Lavoie + */ +class DirectoryLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($file, $type = null) + { + $file = rtrim($file, '/'); + $path = $this->locator->locate($file); + $this->container->fileExists($path, false); + + foreach (scandir($path) as $dir) { + if ('.' !== $dir[0]) { + if (is_dir($path.'/'.$dir)) { + $dir .= '/'; // append / to allow recursion + } + + $this->setCurrentDir($path); + + $this->import($dir, null, false, $path); + } + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if ('directory' === $type) { + return true; + } + + return null === $type && \is_string($resource) && '/' === substr($resource, -1); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/FileLoader.php b/vendor/symfony/dependency-injection/Loader/FileLoader.php new file mode 100644 index 0000000..858f2bc --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/FileLoader.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\DependencyInjection\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Loader\FileLoader as BaseFileLoader; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * FileLoader is the abstract class used by all built-in loaders that are file based. + * + * @author Fabien Potencier + */ +abstract class FileLoader extends BaseFileLoader +{ + protected $container; + protected $isLoadingInstanceof = false; + protected $instanceof = array(); + + public function __construct(ContainerBuilder $container, FileLocatorInterface $locator) + { + $this->container = $container; + + parent::__construct($locator); + } + + /** + * Registers a set of classes as services using PSR-4 for discovery. + * + * @param Definition $prototype A definition to use as template + * @param string $namespace The namespace prefix of classes in the scanned directory + * @param string $resource The directory to look for classes, glob-patterns allowed + * @param string $exclude A globed path of files to exclude + */ + public function registerClasses(Definition $prototype, $namespace, $resource, $exclude = null) + { + if ('\\' !== substr($namespace, -1)) { + throw new InvalidArgumentException(sprintf('Namespace prefix must end with a "\\": %s.', $namespace)); + } + if (!preg_match('/^(?:[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+\\\\)++$/', $namespace)) { + throw new InvalidArgumentException(sprintf('Namespace is not a valid PSR-4 prefix: %s.', $namespace)); + } + + $classes = $this->findClasses($namespace, $resource, $exclude); + // prepare for deep cloning + $serializedPrototype = serialize($prototype); + $interfaces = array(); + $singlyImplemented = array(); + + foreach ($classes as $class => $errorMessage) { + if (interface_exists($class, false)) { + $interfaces[] = $class; + } else { + $this->setDefinition($class, $definition = unserialize($serializedPrototype)); + if (null !== $errorMessage) { + $definition->addError($errorMessage); + + continue; + } + foreach (class_implements($class, false) as $interface) { + $singlyImplemented[$interface] = isset($singlyImplemented[$interface]) ? false : $class; + } + } + } + foreach ($interfaces as $interface) { + if (!empty($singlyImplemented[$interface])) { + $this->container->setAlias($interface, $singlyImplemented[$interface]) + ->setPublic(false); + } + } + } + + /** + * Registers a definition in the container with its instanceof-conditionals. + * + * @param string $id + * @param Definition $definition + */ + protected function setDefinition($id, Definition $definition) + { + if ($this->isLoadingInstanceof) { + if (!$definition instanceof ChildDefinition) { + throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, \get_class($definition))); + } + $this->instanceof[$id] = $definition; + } else { + $this->container->setDefinition($id, $definition instanceof ChildDefinition ? $definition : $definition->setInstanceofConditionals($this->instanceof)); + } + } + + private function findClasses($namespace, $pattern, $excludePattern) + { + $parameterBag = $this->container->getParameterBag(); + + $excludePaths = array(); + $excludePrefix = null; + if ($excludePattern) { + $excludePattern = $parameterBag->unescapeValue($parameterBag->resolveValue($excludePattern)); + foreach ($this->glob($excludePattern, true, $resource) as $path => $info) { + if (null === $excludePrefix) { + $excludePrefix = $resource->getPrefix(); + } + + // normalize Windows slashes + $excludePaths[str_replace('\\', '/', $path)] = true; + } + } + + $pattern = $parameterBag->unescapeValue($parameterBag->resolveValue($pattern)); + $classes = array(); + $extRegexp = '/\\.php$/'; + $prefixLen = null; + foreach ($this->glob($pattern, true, $resource) as $path => $info) { + if (null === $prefixLen) { + $prefixLen = \strlen($resource->getPrefix()); + + if ($excludePrefix && 0 !== strpos($excludePrefix, $resource->getPrefix())) { + throw new InvalidArgumentException(sprintf('Invalid "exclude" pattern when importing classes for "%s": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $namespace, $excludePattern, $pattern)); + } + } + + if (isset($excludePaths[str_replace('\\', '/', $path)])) { + continue; + } + + if (!preg_match($extRegexp, $path, $m) || !$info->isReadable()) { + continue; + } + $class = $namespace.ltrim(str_replace('/', '\\', substr($path, $prefixLen, -\strlen($m[0]))), '\\'); + + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $class)) { + continue; + } + + try { + $r = $this->container->getReflectionClass($class); + } catch (\ReflectionException $e) { + $classes[$class] = sprintf( + 'While discovering services from namespace "%s", an error was thrown when processing the class "%s": "%s".', + $namespace, + $class, + $e->getMessage() + ); + continue; + } + // check to make sure the expected class exists + if (!$r) { + throw new InvalidArgumentException(sprintf('Expected to find class "%s" in file "%s" while importing services from resource "%s", but it was not found! Check the namespace prefix used with the resource.', $class, $path, $pattern)); + } + + if ($r->isInstantiable() || $r->isInterface()) { + $classes[$class] = null; + } + } + + // track only for new & removed files + if ($resource instanceof GlobResource) { + $this->container->addResource($resource); + } else { + foreach ($resource as $path) { + $this->container->fileExists($path, false); + } + } + + return $classes; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php b/vendor/symfony/dependency-injection/Loader/GlobFileLoader.php new file mode 100644 index 0000000..4b25610 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/GlobFileLoader.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\DependencyInjection\Loader; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Nicolas Grekas + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + foreach ($this->glob($resource, false, $globResource) as $path => $info) { + $this->import($path); + } + + $this->container->addResource($globResource); + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'glob' === $type; + } +} diff --git a/vendor/symfony/dependency-injection/Loader/IniFileLoader.php b/vendor/symfony/dependency-injection/Loader/IniFileLoader.php new file mode 100644 index 0000000..ed37091 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/IniFileLoader.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\DependencyInjection\Loader; + +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * IniFileLoader loads parameters from INI files. + * + * @author Fabien Potencier + */ +class IniFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $path = $this->locator->locate($resource); + + $this->container->fileExists($path); + + // first pass to catch parsing errors + $result = parse_ini_file($path, true); + if (false === $result || array() === $result) { + throw new InvalidArgumentException(sprintf('The "%s" file is not valid.', $resource)); + } + + // real raw parsing + $result = parse_ini_file($path, true, INI_SCANNER_RAW); + + if (isset($result['parameters']) && \is_array($result['parameters'])) { + foreach ($result['parameters'] as $key => $value) { + $this->container->setParameter($key, $this->phpize($value)); + } + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if (!\is_string($resource)) { + return false; + } + + if (null === $type && 'ini' === pathinfo($resource, PATHINFO_EXTENSION)) { + return true; + } + + return 'ini' === $type; + } + + /** + * Note that the following features are not supported: + * * strings with escaped quotes are not supported "foo\"bar"; + * * string concatenation ("foo" "bar"). + */ + private function phpize($value) + { + // trim on the right as comments removal keep whitespaces + $value = rtrim($value); + $lowercaseValue = strtolower($value); + + switch (true) { + case \defined($value): + return \constant($value); + case 'yes' === $lowercaseValue || 'on' === $lowercaseValue: + return true; + case 'no' === $lowercaseValue || 'off' === $lowercaseValue || 'none' === $lowercaseValue: + return false; + case isset($value[1]) && ( + ("'" === $value[0] && "'" === $value[\strlen($value) - 1]) || + ('"' === $value[0] && '"' === $value[\strlen($value) - 1]) + ): + // quoted string + return substr($value, 1, -1); + default: + return XmlUtils::phpize($value); + } + } +} diff --git a/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php b/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php new file mode 100644 index 0000000..ff8df43 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/PhpFileLoader.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + +/** + * PhpFileLoader loads service definitions from a PHP file. + * + * The PHP file is required and the $container variable can be + * used within the file to change the container. + * + * @author Fabien Potencier + */ +class PhpFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + // the container and loader variables are exposed to the included file below + $container = $this->container; + $loader = $this; + + $path = $this->locator->locate($resource); + $this->setCurrentDir(\dirname($path)); + $this->container->fileExists($path); + + // the closure forbids access to the private scope in the included file + $load = \Closure::bind(function ($path) use ($container, $loader, $resource, $type) { + return include $path; + }, $this, ProtectedPhpFileLoader::class); + + $callback = $load($path); + + if ($callback instanceof \Closure) { + $callback(new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource), $this->container, $this); + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if (!\is_string($resource)) { + return false; + } + + if (null === $type && 'php' === pathinfo($resource, PATHINFO_EXTENSION)) { + return true; + } + + return 'php' === $type; + } +} + +/** + * @internal + */ +final class ProtectedPhpFileLoader extends PhpFileLoader +{ +} diff --git a/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php b/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php new file mode 100644 index 0000000..ad5b46c --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/XmlFileLoader.php @@ -0,0 +1,720 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; + +/** + * XmlFileLoader loads XML files service definitions. + * + * @author Fabien Potencier + */ +class XmlFileLoader extends FileLoader +{ + const NS = 'http://symfony.com/schema/dic/services'; + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $path = $this->locator->locate($resource); + + $xml = $this->parseFileToDOM($path); + + $this->container->fileExists($path); + + $defaults = $this->getServiceDefaults($xml, $path); + + // anonymous services + $this->processAnonymousServices($xml, $path, $defaults); + + // imports + $this->parseImports($xml, $path); + + // parameters + $this->parseParameters($xml, $path); + + // extensions + $this->loadFromExtensions($xml); + + // services + try { + $this->parseDefinitions($xml, $path, $defaults); + } finally { + $this->instanceof = array(); + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if (!\is_string($resource)) { + return false; + } + + if (null === $type && 'xml' === pathinfo($resource, PATHINFO_EXTENSION)) { + return true; + } + + return 'xml' === $type; + } + + /** + * Parses parameters. + * + * @param \DOMDocument $xml + * @param string $file + */ + private function parseParameters(\DOMDocument $xml, $file) + { + if ($parameters = $this->getChildren($xml->documentElement, 'parameters')) { + $this->container->getParameterBag()->add($this->getArgumentsAsPhp($parameters[0], 'parameter', $file)); + } + } + + /** + * Parses imports. + * + * @param \DOMDocument $xml + * @param string $file + */ + private function parseImports(\DOMDocument $xml, $file) + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (false === $imports = $xpath->query('//container:imports/container:import')) { + return; + } + + $defaultDirectory = \dirname($file); + foreach ($imports as $import) { + $this->setCurrentDir($defaultDirectory); + $this->import($import->getAttribute('resource'), XmlUtils::phpize($import->getAttribute('type')) ?: null, (bool) XmlUtils::phpize($import->getAttribute('ignore-errors')), $file); + } + } + + /** + * Parses multiple definitions. + * + * @param \DOMDocument $xml + * @param string $file + */ + private function parseDefinitions(\DOMDocument $xml, $file, $defaults) + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (false === $services = $xpath->query('//container:services/container:service|//container:services/container:prototype')) { + return; + } + $this->setCurrentDir(\dirname($file)); + + $this->instanceof = array(); + $this->isLoadingInstanceof = true; + $instanceof = $xpath->query('//container:services/container:instanceof'); + foreach ($instanceof as $service) { + $this->setDefinition((string) $service->getAttribute('id'), $this->parseDefinition($service, $file, array())); + } + + $this->isLoadingInstanceof = false; + foreach ($services as $service) { + if (null !== $definition = $this->parseDefinition($service, $file, $defaults)) { + if ('prototype' === $service->tagName) { + $this->registerClasses($definition, (string) $service->getAttribute('namespace'), (string) $service->getAttribute('resource'), (string) $service->getAttribute('exclude')); + } else { + $this->setDefinition((string) $service->getAttribute('id'), $definition); + } + } + } + } + + /** + * Get service defaults. + * + * @return array + */ + private function getServiceDefaults(\DOMDocument $xml, $file) + { + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + if (null === $defaultsNode = $xpath->query('//container:services/container:defaults')->item(0)) { + return array(); + } + $defaults = array( + 'tags' => $this->getChildren($defaultsNode, 'tag'), + 'bind' => array_map(function ($v) { return new BoundArgument($v); }, $this->getArgumentsAsPhp($defaultsNode, 'bind', $file)), + ); + + foreach ($defaults['tags'] as $tag) { + if ('' === $tag->getAttribute('name')) { + throw new InvalidArgumentException(sprintf('The tag name for tag "" in %s must be a non-empty string.', $file)); + } + } + + if ($defaultsNode->hasAttribute('autowire')) { + $defaults['autowire'] = XmlUtils::phpize($defaultsNode->getAttribute('autowire')); + } + if ($defaultsNode->hasAttribute('public')) { + $defaults['public'] = XmlUtils::phpize($defaultsNode->getAttribute('public')); + } + if ($defaultsNode->hasAttribute('autoconfigure')) { + $defaults['autoconfigure'] = XmlUtils::phpize($defaultsNode->getAttribute('autoconfigure')); + } + + return $defaults; + } + + /** + * Parses an individual Definition. + * + * @param \DOMElement $service + * @param string $file + * @param array $defaults + * + * @return Definition|null + */ + private function parseDefinition(\DOMElement $service, $file, array $defaults) + { + if ($alias = $service->getAttribute('alias')) { + $this->validateAlias($service, $file); + + $this->container->setAlias((string) $service->getAttribute('id'), $alias = new Alias($alias)); + if ($publicAttr = $service->getAttribute('public')) { + $alias->setPublic(XmlUtils::phpize($publicAttr)); + } elseif (isset($defaults['public'])) { + $alias->setPublic($defaults['public']); + } + + return; + } + + if ($this->isLoadingInstanceof) { + $definition = new ChildDefinition(''); + } elseif ($parent = $service->getAttribute('parent')) { + if (!empty($this->instanceof)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.', $service->getAttribute('id'))); + } + + foreach ($defaults as $k => $v) { + if ('tags' === $k) { + // since tags are never inherited from parents, there is no confusion + // thus we can safely add them as defaults to ChildDefinition + continue; + } + if ('bind' === $k) { + if ($defaults['bind']) { + throw new InvalidArgumentException(sprintf('Bound values on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file.', $service->getAttribute('id'))); + } + + continue; + } + if (!$service->hasAttribute($k)) { + throw new InvalidArgumentException(sprintf('Attribute "%s" on service "%s" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.', $k, $service->getAttribute('id'))); + } + } + + $definition = new ChildDefinition($parent); + } else { + $definition = new Definition(); + + if (isset($defaults['public'])) { + $definition->setPublic($defaults['public']); + } + if (isset($defaults['autowire'])) { + $definition->setAutowired($defaults['autowire']); + } + if (isset($defaults['autoconfigure'])) { + $definition->setAutoconfigured($defaults['autoconfigure']); + } + + $definition->setChanges(array()); + } + + foreach (array('class', 'public', 'shared', 'synthetic', 'lazy', 'abstract') as $key) { + if ($value = $service->getAttribute($key)) { + $method = 'set'.$key; + $definition->$method(XmlUtils::phpize($value)); + } + } + + if ($value = $service->getAttribute('autowire')) { + $definition->setAutowired(XmlUtils::phpize($value)); + } + + if ($value = $service->getAttribute('autoconfigure')) { + if (!$definition instanceof ChildDefinition) { + $definition->setAutoconfigured(XmlUtils::phpize($value)); + } elseif ($value = XmlUtils::phpize($value)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service.', $service->getAttribute('id'))); + } + } + + if ($files = $this->getChildren($service, 'file')) { + $definition->setFile($files[0]->nodeValue); + } + + if ($deprecated = $this->getChildren($service, 'deprecated')) { + $definition->setDeprecated(true, $deprecated[0]->nodeValue ?: null); + } + + $definition->setArguments($this->getArgumentsAsPhp($service, 'argument', $file, false, $definition instanceof ChildDefinition)); + $definition->setProperties($this->getArgumentsAsPhp($service, 'property', $file)); + + if ($factories = $this->getChildren($service, 'factory')) { + $factory = $factories[0]; + if ($function = $factory->getAttribute('function')) { + $definition->setFactory($function); + } else { + if ($childService = $factory->getAttribute('service')) { + $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); + } else { + $class = $factory->hasAttribute('class') ? $factory->getAttribute('class') : null; + } + + $definition->setFactory(array($class, $factory->getAttribute('method'))); + } + } + + if ($configurators = $this->getChildren($service, 'configurator')) { + $configurator = $configurators[0]; + if ($function = $configurator->getAttribute('function')) { + $definition->setConfigurator($function); + } else { + if ($childService = $configurator->getAttribute('service')) { + $class = new Reference($childService, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE); + } else { + $class = $configurator->getAttribute('class'); + } + + $definition->setConfigurator(array($class, $configurator->getAttribute('method'))); + } + } + + foreach ($this->getChildren($service, 'call') as $call) { + $definition->addMethodCall($call->getAttribute('method'), $this->getArgumentsAsPhp($call, 'argument', $file)); + } + + $tags = $this->getChildren($service, 'tag'); + + if (!empty($defaults['tags'])) { + $tags = array_merge($tags, $defaults['tags']); + } + + foreach ($tags as $tag) { + $parameters = array(); + foreach ($tag->attributes as $name => $node) { + if ('name' === $name) { + continue; + } + + if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) { + $parameters[$normalizedName] = XmlUtils::phpize($node->nodeValue); + } + // keep not normalized key + $parameters[$name] = XmlUtils::phpize($node->nodeValue); + } + + if ('' === $tag->getAttribute('name')) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', (string) $service->getAttribute('id'), $file)); + } + + $definition->addTag($tag->getAttribute('name'), $parameters); + } + + $bindings = $this->getArgumentsAsPhp($service, 'bind', $file); + if (isset($defaults['bind'])) { + // deep clone, to avoid multiple process of the same instance in the passes + $bindings = array_merge(unserialize(serialize($defaults['bind'])), $bindings); + } + if ($bindings) { + $definition->setBindings($bindings); + } + + if ($value = $service->getAttribute('decorates')) { + $renameId = $service->hasAttribute('decoration-inner-name') ? $service->getAttribute('decoration-inner-name') : null; + $priority = $service->hasAttribute('decoration-priority') ? $service->getAttribute('decoration-priority') : 0; + $definition->setDecoratedService($value, $renameId, $priority); + } + + return $definition; + } + + /** + * Parses a XML file to a \DOMDocument. + * + * @param string $file Path to a file + * + * @return \DOMDocument + * + * @throws InvalidArgumentException When loading of XML file returns error + */ + private function parseFileToDOM($file) + { + try { + $dom = XmlUtils::loadFile($file, array($this, 'validateSchema')); + } catch (\InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Unable to parse file "%s".', $file), $e->getCode(), $e); + } + + $this->validateExtensions($dom, $file); + + return $dom; + } + + /** + * Processes anonymous services. + * + * @param \DOMDocument $xml + * @param string $file + * @param array $defaults + */ + private function processAnonymousServices(\DOMDocument $xml, $file, $defaults) + { + $definitions = array(); + $count = 0; + $suffix = ContainerBuilder::hash($file); + + $xpath = new \DOMXPath($xml); + $xpath->registerNamespace('container', self::NS); + + // anonymous services as arguments/properties + if (false !== $nodes = $xpath->query('//container:argument[@type="service"][not(@id)]|//container:property[@type="service"][not(@id)]|//container:bind[not(@id)]|//container:factory[not(@service)]|//container:configurator[not(@service)]')) { + foreach ($nodes as $node) { + if ($services = $this->getChildren($node, 'service')) { + // give it a unique name + $id = sprintf('%d_%s', ++$count, preg_replace('/^.*\\\\/', '', $services[0]->getAttribute('class')).'~'.$suffix); + $node->setAttribute('id', $id); + $node->setAttribute('service', $id); + + $definitions[$id] = array($services[0], $file); + $services[0]->setAttribute('id', $id); + + // anonymous services are always private + // we could not use the constant false here, because of XML parsing + $services[0]->setAttribute('public', 'false'); + } + } + } + + // anonymous services "in the wild" + if (false !== $nodes = $xpath->query('//container:services/container:service[not(@id)]')) { + foreach ($nodes as $node) { + throw new InvalidArgumentException(sprintf('Top-level services must have "id" attribute, none found in %s at line %d.', $file, $node->getLineNo())); + } + } + + // resolve definitions + uksort($definitions, 'strnatcmp'); + foreach (array_reverse($definitions) as $id => list($domElement, $file)) { + if (null !== $definition = $this->parseDefinition($domElement, $file, array())) { + $this->setDefinition($id, $definition); + } + } + } + + /** + * Returns arguments as valid php types. + * + * @param \DOMElement $node + * @param string $name + * @param string $file + * @param bool $lowercase + * + * @return mixed + */ + private function getArgumentsAsPhp(\DOMElement $node, $name, $file, $lowercase = true, $isChildDefinition = false) + { + $arguments = array(); + foreach ($this->getChildren($node, $name) as $arg) { + if ($arg->hasAttribute('name')) { + $arg->setAttribute('key', $arg->getAttribute('name')); + } + + // this is used by ChildDefinition to overwrite a specific + // argument of the parent definition + if ($arg->hasAttribute('index')) { + $key = ($isChildDefinition ? 'index_' : '').$arg->getAttribute('index'); + } elseif (!$arg->hasAttribute('key')) { + // Append an empty argument, then fetch its key to overwrite it later + $arguments[] = null; + $keys = array_keys($arguments); + $key = array_pop($keys); + } else { + $key = $arg->getAttribute('key'); + } + + $onInvalid = $arg->getAttribute('on-invalid'); + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + if ('ignore' == $onInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } elseif ('ignore_uninitialized' == $onInvalid) { + $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; + } elseif ('null' == $onInvalid) { + $invalidBehavior = ContainerInterface::NULL_ON_INVALID_REFERENCE; + } + + switch ($arg->getAttribute('type')) { + case 'service': + if (!$arg->getAttribute('id')) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="service" has no or empty "id" attribute in "%s".', $name, $file)); + } + + $arguments[$key] = new Reference($arg->getAttribute('id'), $invalidBehavior); + break; + case 'expression': + if (!class_exists(Expression::class)) { + throw new \LogicException(sprintf('The type="expression" attribute cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".')); + } + + $arguments[$key] = new Expression($arg->nodeValue); + break; + case 'collection': + $arguments[$key] = $this->getArgumentsAsPhp($arg, $name, $file, false); + break; + case 'iterator': + $arg = $this->getArgumentsAsPhp($arg, $name, $file, false); + try { + $arguments[$key] = new IteratorArgument($arg); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="iterator" only accepts collections of type="service" references in "%s".', $name, $file)); + } + break; + case 'tagged': + if (!$arg->getAttribute('tag')) { + throw new InvalidArgumentException(sprintf('Tag "<%s>" with type="tagged" has no or empty "tag" attribute in "%s".', $name, $file)); + } + $arguments[$key] = new TaggedIteratorArgument($arg->getAttribute('tag')); + break; + case 'string': + $arguments[$key] = $arg->nodeValue; + break; + case 'constant': + $arguments[$key] = \constant(trim($arg->nodeValue)); + break; + default: + $arguments[$key] = XmlUtils::phpize($arg->nodeValue); + } + } + + return $arguments; + } + + /** + * Get child elements by name. + * + * @param \DOMNode $node + * @param mixed $name + * + * @return array + */ + private function getChildren(\DOMNode $node, $name) + { + $children = array(); + foreach ($node->childNodes as $child) { + if ($child instanceof \DOMElement && $child->localName === $name && self::NS === $child->namespaceURI) { + $children[] = $child; + } + } + + return $children; + } + + /** + * Validates a documents XML schema. + * + * @param \DOMDocument $dom + * + * @return bool + * + * @throws RuntimeException When extension references a non-existent XSD file + */ + public function validateSchema(\DOMDocument $dom) + { + $schemaLocations = array('http://symfony.com/schema/dic/services' => str_replace('\\', '/', __DIR__.'/schema/dic/services/services-1.0.xsd')); + + if ($element = $dom->documentElement->getAttributeNS('http://www.w3.org/2001/XMLSchema-instance', 'schemaLocation')) { + $items = preg_split('/\s+/', $element); + for ($i = 0, $nb = \count($items); $i < $nb; $i += 2) { + if (!$this->container->hasExtension($items[$i])) { + continue; + } + + if (($extension = $this->container->getExtension($items[$i])) && false !== $extension->getXsdValidationBasePath()) { + $path = str_replace($extension->getNamespace(), str_replace('\\', '/', $extension->getXsdValidationBasePath()).'/', $items[$i + 1]); + + if (!is_file($path)) { + throw new RuntimeException(sprintf('Extension "%s" references a non-existent XSD file "%s"', \get_class($extension), $path)); + } + + $schemaLocations[$items[$i]] = $path; + } + } + } + + $tmpfiles = array(); + $imports = ''; + foreach ($schemaLocations as $namespace => $location) { + $parts = explode('/', $location); + $locationstart = 'file:///'; + if (0 === stripos($location, 'phar://')) { + $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); + if ($tmpfile) { + copy($location, $tmpfile); + $tmpfiles[] = $tmpfile; + $parts = explode('/', str_replace('\\', '/', $tmpfile)); + } else { + array_shift($parts); + $locationstart = 'phar:///'; + } + } + $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; + $location = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); + + $imports .= sprintf(' '."\n", $namespace, $location); + } + + $source = << + + + +$imports + +EOF + ; + + $disableEntities = libxml_disable_entity_loader(false); + $valid = @$dom->schemaValidateSource($source); + libxml_disable_entity_loader($disableEntities); + + foreach ($tmpfiles as $tmpfile) { + @unlink($tmpfile); + } + + return $valid; + } + + /** + * Validates an alias. + * + * @param \DOMElement $alias + * @param string $file + */ + private function validateAlias(\DOMElement $alias, $file) + { + foreach ($alias->attributes as $name => $node) { + if (!\in_array($name, array('alias', 'id', 'public'))) { + throw new InvalidArgumentException(sprintf('Invalid attribute "%s" defined for alias "%s" in "%s".', $name, $alias->getAttribute('id'), $file)); + } + } + + foreach ($alias->childNodes as $child) { + if ($child instanceof \DOMElement && self::NS === $child->namespaceURI) { + throw new InvalidArgumentException(sprintf('Invalid child element "%s" defined for alias "%s" in "%s".', $child->localName, $alias->getAttribute('id'), $file)); + } + } + } + + /** + * Validates an extension. + * + * @param \DOMDocument $dom + * @param string $file + * + * @throws InvalidArgumentException When no extension is found corresponding to a tag + */ + private function validateExtensions(\DOMDocument $dom, $file) + { + foreach ($dom->documentElement->childNodes as $node) { + if (!$node instanceof \DOMElement || 'http://symfony.com/schema/dic/services' === $node->namespaceURI) { + continue; + } + + // can it be handled by an extension? + if (!$this->container->hasExtension($node->namespaceURI)) { + $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getNamespace(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf( + 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', + $node->tagName, + $file, + $node->namespaceURI, + $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' + )); + } + } + } + + /** + * Loads from an extension. + * + * @param \DOMDocument $xml + */ + private function loadFromExtensions(\DOMDocument $xml) + { + foreach ($xml->documentElement->childNodes as $node) { + if (!$node instanceof \DOMElement || self::NS === $node->namespaceURI) { + continue; + } + + $values = static::convertDomElementToArray($node); + if (!\is_array($values)) { + $values = array(); + } + + $this->container->loadFromExtension($node->namespaceURI, $values); + } + } + + /** + * Converts a \DOMElement object to a PHP array. + * + * The following rules applies during the conversion: + * + * * Each tag is converted to a key value or an array + * if there is more than one "value" + * + * * The content of a tag is set under a "value" key (bar) + * if the tag also has some nested tags + * + * * The attributes are converted to keys () + * + * * The nested-tags are converted to keys (bar) + * + * @param \DOMElement $element A \DOMElement instance + * + * @return array A PHP array + */ + public static function convertDomElementToArray(\DOMElement $element) + { + return XmlUtils::convertDomElementToArray($element); + } +} diff --git a/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php b/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php new file mode 100644 index 0000000..49ce8fb --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/YamlFileLoader.php @@ -0,0 +1,802 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Loader; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\Yaml\Tag\TaggedValue; +use Symfony\Component\Yaml\Yaml; + +/** + * YamlFileLoader loads YAML files service definitions. + * + * @author Fabien Potencier + */ +class YamlFileLoader extends FileLoader +{ + private static $serviceKeywords = array( + 'alias' => 'alias', + 'parent' => 'parent', + 'class' => 'class', + 'shared' => 'shared', + 'synthetic' => 'synthetic', + 'lazy' => 'lazy', + 'public' => 'public', + 'abstract' => 'abstract', + 'deprecated' => 'deprecated', + 'factory' => 'factory', + 'file' => 'file', + 'arguments' => 'arguments', + 'properties' => 'properties', + 'configurator' => 'configurator', + 'calls' => 'calls', + 'tags' => 'tags', + 'decorates' => 'decorates', + 'decoration_inner_name' => 'decoration_inner_name', + 'decoration_priority' => 'decoration_priority', + 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', + 'bind' => 'bind', + ); + + private static $prototypeKeywords = array( + 'resource' => 'resource', + 'namespace' => 'namespace', + 'exclude' => 'exclude', + 'parent' => 'parent', + 'shared' => 'shared', + 'lazy' => 'lazy', + 'public' => 'public', + 'abstract' => 'abstract', + 'deprecated' => 'deprecated', + 'factory' => 'factory', + 'arguments' => 'arguments', + 'properties' => 'properties', + 'configurator' => 'configurator', + 'calls' => 'calls', + 'tags' => 'tags', + 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', + 'bind' => 'bind', + ); + + private static $instanceofKeywords = array( + 'shared' => 'shared', + 'lazy' => 'lazy', + 'public' => 'public', + 'properties' => 'properties', + 'configurator' => 'configurator', + 'calls' => 'calls', + 'tags' => 'tags', + 'autowire' => 'autowire', + ); + + private static $defaultsKeywords = array( + 'public' => 'public', + 'tags' => 'tags', + 'autowire' => 'autowire', + 'autoconfigure' => 'autoconfigure', + 'bind' => 'bind', + ); + + private $yamlParser; + + private $anonymousServicesCount; + private $anonymousServicesSuffix; + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $path = $this->locator->locate($resource); + + $content = $this->loadFile($path); + + $this->container->fileExists($path); + + // empty file + if (null === $content) { + return; + } + + // imports + $this->parseImports($content, $path); + + // parameters + if (isset($content['parameters'])) { + if (!\is_array($content['parameters'])) { + throw new InvalidArgumentException(sprintf('The "parameters" key should contain an array in %s. Check your YAML syntax.', $path)); + } + + foreach ($content['parameters'] as $key => $value) { + $this->container->setParameter($key, $this->resolveServices($value, $path, true)); + } + } + + // extensions + $this->loadFromExtensions($content); + + // services + $this->anonymousServicesCount = 0; + $this->anonymousServicesSuffix = ContainerBuilder::hash($path); + $this->setCurrentDir(\dirname($path)); + try { + $this->parseDefinitions($content, $path); + } finally { + $this->instanceof = array(); + } + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + if (!\is_string($resource)) { + return false; + } + + if (null === $type && \in_array(pathinfo($resource, PATHINFO_EXTENSION), array('yaml', 'yml'), true)) { + return true; + } + + return \in_array($type, array('yaml', 'yml'), true); + } + + private function parseImports(array $content, string $file) + { + if (!isset($content['imports'])) { + return; + } + + if (!\is_array($content['imports'])) { + throw new InvalidArgumentException(sprintf('The "imports" key should contain an array in %s. Check your YAML syntax.', $file)); + } + + $defaultDirectory = \dirname($file); + foreach ($content['imports'] as $import) { + if (!\is_array($import)) { + $import = array('resource' => $import); + } + if (!isset($import['resource'])) { + throw new InvalidArgumentException(sprintf('An import should provide a resource in %s. Check your YAML syntax.', $file)); + } + + $this->setCurrentDir($defaultDirectory); + $this->import($import['resource'], isset($import['type']) ? $import['type'] : null, isset($import['ignore_errors']) ? (bool) $import['ignore_errors'] : false, $file); + } + } + + private function parseDefinitions(array $content, string $file) + { + if (!isset($content['services'])) { + return; + } + + if (!\is_array($content['services'])) { + throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file)); + } + + if (array_key_exists('_instanceof', $content['services'])) { + $instanceof = $content['services']['_instanceof']; + unset($content['services']['_instanceof']); + + if (!\is_array($instanceof)) { + throw new InvalidArgumentException(sprintf('Service "_instanceof" key must be an array, "%s" given in "%s".', \gettype($instanceof), $file)); + } + $this->instanceof = array(); + $this->isLoadingInstanceof = true; + foreach ($instanceof as $id => $service) { + if (!$service || !\is_array($service)) { + throw new InvalidArgumentException(sprintf('Type definition "%s" must be a non-empty array within "_instanceof" in %s. Check your YAML syntax.', $id, $file)); + } + if (\is_string($service) && 0 === strpos($service, '@')) { + throw new InvalidArgumentException(sprintf('Type definition "%s" cannot be an alias within "_instanceof" in %s. Check your YAML syntax.', $id, $file)); + } + $this->parseDefinition($id, $service, $file, array()); + } + } + + $this->isLoadingInstanceof = false; + $defaults = $this->parseDefaults($content, $file); + foreach ($content['services'] as $id => $service) { + $this->parseDefinition($id, $service, $file, $defaults); + } + } + + /** + * @throws InvalidArgumentException + */ + private function parseDefaults(array &$content, string $file): array + { + if (!array_key_exists('_defaults', $content['services'])) { + return array(); + } + $defaults = $content['services']['_defaults']; + unset($content['services']['_defaults']); + + if (!\is_array($defaults)) { + throw new InvalidArgumentException(sprintf('Service "_defaults" key must be an array, "%s" given in "%s".', \gettype($defaults), $file)); + } + + foreach ($defaults as $key => $default) { + if (!isset(self::$defaultsKeywords[$key])) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', self::$defaultsKeywords))); + } + } + + if (isset($defaults['tags'])) { + if (!\is_array($tags = $defaults['tags'])) { + throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in %s. Check your YAML syntax.', $file)); + } + + foreach ($tags as $tag) { + if (!\is_array($tag)) { + $tag = array('name' => $tag); + } + + if (!isset($tag['name'])) { + throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in %s.', $file)); + } + $name = $tag['name']; + unset($tag['name']); + + if (!\is_string($name) || '' === $name) { + throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in %s.', $file)); + } + + foreach ($tag as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in %s. Check your YAML syntax.', $name, $attribute, $file)); + } + } + } + } + + if (isset($defaults['bind'])) { + if (!\is_array($defaults['bind'])) { + throw new InvalidArgumentException(sprintf('Parameter "bind" in "_defaults" must be an array in %s. Check your YAML syntax.', $file)); + } + + $defaults['bind'] = array_map(function ($v) { return new BoundArgument($v); }, $this->resolveServices($defaults['bind'], $file)); + } + + return $defaults; + } + + private function isUsingShortSyntax(array $service): bool + { + foreach ($service as $key => $value) { + if (\is_string($key) && ('' === $key || '$' !== $key[0])) { + return false; + } + } + + return true; + } + + /** + * Parses a definition. + * + * @param string $id + * @param array|string $service + * @param string $file + * @param array $defaults + * + * @throws InvalidArgumentException When tags are invalid + */ + private function parseDefinition($id, $service, $file, array $defaults) + { + if (preg_match('/^_[a-zA-Z0-9_]*$/', $id)) { + throw new InvalidArgumentException(sprintf('Service names that start with an underscore are reserved. Rename the "%s" service or define it in XML instead.', $id)); + } + + if (\is_string($service) && 0 === strpos($service, '@')) { + $this->container->setAlias($id, $alias = new Alias(substr($service, 1))); + if (isset($defaults['public'])) { + $alias->setPublic($defaults['public']); + } + + return; + } + + if (\is_array($service) && $this->isUsingShortSyntax($service)) { + $service = array('arguments' => $service); + } + + if (null === $service) { + $service = array(); + } + + if (!\is_array($service)) { + throw new InvalidArgumentException(sprintf('A service definition must be an array or a string starting with "@" but %s found for service "%s" in %s. Check your YAML syntax.', \gettype($service), $id, $file)); + } + + $this->checkDefinition($id, $service, $file); + + if (isset($service['alias'])) { + $this->container->setAlias($id, $alias = new Alias($service['alias'])); + if (array_key_exists('public', $service)) { + $alias->setPublic($service['public']); + } elseif (isset($defaults['public'])) { + $alias->setPublic($defaults['public']); + } + + foreach ($service as $key => $value) { + if (!\in_array($key, array('alias', 'public'))) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for the service "%s" which is defined as an alias in "%s". Allowed configuration keys for service aliases are "alias" and "public".', $key, $id, $file)); + } + } + + return; + } + + if ($this->isLoadingInstanceof) { + $definition = new ChildDefinition(''); + } elseif (isset($service['parent'])) { + if (!empty($this->instanceof)) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot use the "parent" option in the same file where "_instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file.', $id)); + } + + foreach ($defaults as $k => $v) { + if ('tags' === $k) { + // since tags are never inherited from parents, there is no confusion + // thus we can safely add them as defaults to ChildDefinition + continue; + } + if ('bind' === $k) { + throw new InvalidArgumentException(sprintf('Attribute "bind" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file.', $id)); + } + if (!isset($service[$k])) { + throw new InvalidArgumentException(sprintf('Attribute "%s" on service "%s" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly.', $k, $id)); + } + } + + $definition = new ChildDefinition($service['parent']); + } else { + $definition = new Definition(); + + if (isset($defaults['public'])) { + $definition->setPublic($defaults['public']); + } + if (isset($defaults['autowire'])) { + $definition->setAutowired($defaults['autowire']); + } + if (isset($defaults['autoconfigure'])) { + $definition->setAutoconfigured($defaults['autoconfigure']); + } + + $definition->setChanges(array()); + } + + if (isset($service['class'])) { + $definition->setClass($service['class']); + } + + if (isset($service['shared'])) { + $definition->setShared($service['shared']); + } + + if (isset($service['synthetic'])) { + $definition->setSynthetic($service['synthetic']); + } + + if (isset($service['lazy'])) { + $definition->setLazy($service['lazy']); + } + + if (isset($service['public'])) { + $definition->setPublic($service['public']); + } + + if (isset($service['abstract'])) { + $definition->setAbstract($service['abstract']); + } + + if (array_key_exists('deprecated', $service)) { + $definition->setDeprecated(true, $service['deprecated']); + } + + if (isset($service['factory'])) { + $definition->setFactory($this->parseCallable($service['factory'], 'factory', $id, $file)); + } + + if (isset($service['file'])) { + $definition->setFile($service['file']); + } + + if (isset($service['arguments'])) { + $definition->setArguments($this->resolveServices($service['arguments'], $file)); + } + + if (isset($service['properties'])) { + $definition->setProperties($this->resolveServices($service['properties'], $file)); + } + + if (isset($service['configurator'])) { + $definition->setConfigurator($this->parseCallable($service['configurator'], 'configurator', $id, $file)); + } + + if (isset($service['calls'])) { + if (!\is_array($service['calls'])) { + throw new InvalidArgumentException(sprintf('Parameter "calls" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + foreach ($service['calls'] as $call) { + if (isset($call['method'])) { + $method = $call['method']; + $args = isset($call['arguments']) ? $this->resolveServices($call['arguments'], $file) : array(); + } else { + $method = $call[0]; + $args = isset($call[1]) ? $this->resolveServices($call[1], $file) : array(); + } + + if (!\is_array($args)) { + throw new InvalidArgumentException(sprintf('The second parameter for function call "%s" must be an array of its arguments for service "%s" in %s. Check your YAML syntax.', $method, $id, $file)); + } + $definition->addMethodCall($method, $args); + } + } + + $tags = isset($service['tags']) ? $service['tags'] : array(); + if (!\is_array($tags)) { + throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + if (isset($defaults['tags'])) { + $tags = array_merge($tags, $defaults['tags']); + } + + foreach ($tags as $tag) { + if (!\is_array($tag)) { + $tag = array('name' => $tag); + } + + if (!isset($tag['name'])) { + throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file)); + } + $name = $tag['name']; + unset($tag['name']); + + if (!\is_string($name) || '' === $name) { + throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file)); + } + + foreach ($tag as $attribute => $value) { + if (!is_scalar($value) && null !== $value) { + throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file)); + } + } + + $definition->addTag($name, $tag); + } + + if (isset($service['decorates'])) { + if ('' !== $service['decorates'] && '@' === $service['decorates'][0]) { + throw new InvalidArgumentException(sprintf('The value of the "decorates" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $id, $service['decorates'], substr($service['decorates'], 1))); + } + + $renameId = isset($service['decoration_inner_name']) ? $service['decoration_inner_name'] : null; + $priority = isset($service['decoration_priority']) ? $service['decoration_priority'] : 0; + $definition->setDecoratedService($service['decorates'], $renameId, $priority); + } + + if (isset($service['autowire'])) { + $definition->setAutowired($service['autowire']); + } + + if (isset($defaults['bind']) || isset($service['bind'])) { + // deep clone, to avoid multiple process of the same instance in the passes + $bindings = isset($defaults['bind']) ? unserialize(serialize($defaults['bind'])) : array(); + + if (isset($service['bind'])) { + if (!\is_array($service['bind'])) { + throw new InvalidArgumentException(sprintf('Parameter "bind" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + $bindings = array_merge($bindings, $this->resolveServices($service['bind'], $file)); + } + + $definition->setBindings($bindings); + } + + if (isset($service['autoconfigure'])) { + if (!$definition instanceof ChildDefinition) { + $definition->setAutoconfigured($service['autoconfigure']); + } elseif ($service['autoconfigure']) { + throw new InvalidArgumentException(sprintf('The service "%s" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service.', $id)); + } + } + + if (array_key_exists('namespace', $service) && !array_key_exists('resource', $service)) { + throw new InvalidArgumentException(sprintf('A "resource" attribute must be set when the "namespace" attribute is set for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + + if (array_key_exists('resource', $service)) { + if (!\is_string($service['resource'])) { + throw new InvalidArgumentException(sprintf('A "resource" attribute must be of type string for service "%s" in %s. Check your YAML syntax.', $id, $file)); + } + $exclude = isset($service['exclude']) ? $service['exclude'] : null; + $namespace = isset($service['namespace']) ? $service['namespace'] : $id; + $this->registerClasses($definition, $namespace, $service['resource'], $exclude); + } else { + $this->setDefinition($id, $definition); + } + } + + /** + * Parses a callable. + * + * @param string|array $callable A callable + * @param string $parameter A parameter (e.g. 'factory' or 'configurator') + * @param string $id A service identifier + * @param string $file A parsed file + * + * @throws InvalidArgumentException When errors are occuried + * + * @return string|array A parsed callable + */ + private function parseCallable($callable, $parameter, $id, $file) + { + if (\is_string($callable)) { + if ('' !== $callable && '@' === $callable[0]) { + throw new InvalidArgumentException(sprintf('The value of the "%s" option for the "%s" service must be the id of the service without the "@" prefix (replace "%s" with "%s").', $parameter, $id, $callable, substr($callable, 1))); + } + + if (false !== strpos($callable, ':') && false === strpos($callable, '::')) { + $parts = explode(':', $callable); + + return array($this->resolveServices('@'.$parts[0], $file), $parts[1]); + } + + return $callable; + } + + if (\is_array($callable)) { + if (isset($callable[0]) && isset($callable[1])) { + return array($this->resolveServices($callable[0], $file), $callable[1]); + } + + if ('factory' === $parameter && isset($callable[1]) && null === $callable[0]) { + return $callable; + } + + throw new InvalidArgumentException(sprintf('Parameter "%s" must contain an array with two elements for service "%s" in %s. Check your YAML syntax.', $parameter, $id, $file)); + } + + throw new InvalidArgumentException(sprintf('Parameter "%s" must be a string or an array for service "%s" in %s. Check your YAML syntax.', $parameter, $id, $file)); + } + + /** + * Loads a YAML file. + * + * @param string $file + * + * @return array The file content + * + * @throws InvalidArgumentException when the given file is not a local file or when it does not exist + */ + protected function loadFile($file) + { + if (!class_exists('Symfony\Component\Yaml\Parser')) { + throw new RuntimeException('Unable to load YAML config files as the Symfony Yaml Component is not installed.'); + } + + if (!stream_is_local($file)) { + throw new InvalidArgumentException(sprintf('This is not a local file "%s".', $file)); + } + + if (!file_exists($file)) { + throw new InvalidArgumentException(sprintf('The file "%s" does not exist.', $file)); + } + + if (null === $this->yamlParser) { + $this->yamlParser = new YamlParser(); + } + + try { + $configuration = $this->yamlParser->parseFile($file, Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS); + } catch (ParseException $e) { + throw new InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $file), 0, $e); + } + + return $this->validate($configuration, $file); + } + + /** + * Validates a YAML file. + * + * @param mixed $content + * @param string $file + * + * @return array + * + * @throws InvalidArgumentException When service file is not valid + */ + private function validate($content, $file) + { + if (null === $content) { + return $content; + } + + if (!\is_array($content)) { + throw new InvalidArgumentException(sprintf('The service file "%s" is not valid. It should contain an array. Check your YAML syntax.', $file)); + } + + foreach ($content as $namespace => $data) { + if (\in_array($namespace, array('imports', 'parameters', 'services'))) { + continue; + } + + if (!$this->container->hasExtension($namespace)) { + $extensionNamespaces = array_filter(array_map(function ($ext) { return $ext->getAlias(); }, $this->container->getExtensions())); + throw new InvalidArgumentException(sprintf( + 'There is no extension able to load the configuration for "%s" (in %s). Looked for namespace "%s", found %s', + $namespace, + $file, + $namespace, + $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none' + )); + } + } + + return $content; + } + + /** + * Resolves services. + * + * @param mixed $value + * @param string $file + * @param bool $isParameter + * + * @return array|string|Reference|ArgumentInterface + */ + private function resolveServices($value, $file, $isParameter = false) + { + if ($value instanceof TaggedValue) { + $argument = $value->getValue(); + if ('iterator' === $value->getTag()) { + if (!\is_array($argument)) { + throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts sequences in "%s".', $file)); + } + $argument = $this->resolveServices($argument, $file, $isParameter); + try { + return new IteratorArgument($argument); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException(sprintf('"!iterator" tag only accepts arrays of "@service" references in "%s".', $file)); + } + } + if ('tagged' === $value->getTag()) { + if (!\is_string($argument) || !$argument) { + throw new InvalidArgumentException(sprintf('"!tagged" tag only accepts non empty string in "%s".', $file)); + } + + return new TaggedIteratorArgument($argument); + } + if ('service' === $value->getTag()) { + if ($isParameter) { + throw new InvalidArgumentException(sprintf('Using an anonymous service in a parameter is not allowed in "%s".', $file)); + } + + $isLoadingInstanceof = $this->isLoadingInstanceof; + $this->isLoadingInstanceof = false; + $instanceof = $this->instanceof; + $this->instanceof = array(); + + $id = sprintf('%d_%s', ++$this->anonymousServicesCount, preg_replace('/^.*\\\\/', '', isset($argument['class']) ? $argument['class'] : '').$this->anonymousServicesSuffix); + $this->parseDefinition($id, $argument, $file, array()); + + if (!$this->container->hasDefinition($id)) { + throw new InvalidArgumentException(sprintf('Creating an alias using the tag "!service" is not allowed in "%s".', $file)); + } + + $this->container->getDefinition($id)->setPublic(false); + + $this->isLoadingInstanceof = $isLoadingInstanceof; + $this->instanceof = $instanceof; + + return new Reference($id); + } + + throw new InvalidArgumentException(sprintf('Unsupported tag "!%s".', $value->getTag())); + } + + if (\is_array($value)) { + foreach ($value as $k => $v) { + $value[$k] = $this->resolveServices($v, $file, $isParameter); + } + } elseif (\is_string($value) && 0 === strpos($value, '@=')) { + if (!class_exists(Expression::class)) { + throw new \LogicException(sprintf('The "@=" expression syntax cannot be used without the ExpressionLanguage component. Try running "composer require symfony/expression-language".')); + } + + return new Expression(substr($value, 2)); + } elseif (\is_string($value) && 0 === strpos($value, '@')) { + if (0 === strpos($value, '@@')) { + $value = substr($value, 1); + $invalidBehavior = null; + } elseif (0 === strpos($value, '@!')) { + $value = substr($value, 2); + $invalidBehavior = ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE; + } elseif (0 === strpos($value, '@?')) { + $value = substr($value, 2); + $invalidBehavior = ContainerInterface::IGNORE_ON_INVALID_REFERENCE; + } else { + $value = substr($value, 1); + $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE; + } + + if (null !== $invalidBehavior) { + $value = new Reference($value, $invalidBehavior); + } + } + + return $value; + } + + /** + * Loads from Extensions. + */ + private function loadFromExtensions(array $content) + { + foreach ($content as $namespace => $values) { + if (\in_array($namespace, array('imports', 'parameters', 'services'))) { + continue; + } + + if (!\is_array($values) && null !== $values) { + $values = array(); + } + + $this->container->loadFromExtension($namespace, $values); + } + } + + /** + * Checks the keywords used to define a service. + * + * @param string $id The service name + * @param array $definition The service definition to check + * @param string $file The loaded YAML file + */ + private function checkDefinition($id, array $definition, $file) + { + if ($this->isLoadingInstanceof) { + $keywords = self::$instanceofKeywords; + } elseif ($throw = (isset($definition['resource']) || isset($definition['namespace']))) { + $keywords = self::$prototypeKeywords; + } else { + $keywords = self::$serviceKeywords; + } + + foreach ($definition as $key => $value) { + if (!isset($keywords[$key])) { + throw new InvalidArgumentException(sprintf('The configuration key "%s" is unsupported for definition "%s" in "%s". Allowed configuration keys are "%s".', $key, $id, $file, implode('", "', $keywords))); + } + } + } +} diff --git a/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd b/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd new file mode 100644 index 0000000..99ac005 --- /dev/null +++ b/vendor/symfony/dependency-injection/Loader/schema/dic/services/services-1.0.xsd @@ -0,0 +1,278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Parameter.php b/vendor/symfony/dependency-injection/Parameter.php new file mode 100644 index 0000000..d484ac0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Parameter.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\DependencyInjection; + +/** + * Parameter represents a parameter reference. + * + * @author Fabien Potencier + */ +class Parameter +{ + private $id; + + public function __construct(string $id) + { + $this->id = $id; + } + + /** + * @return string The parameter key + */ + public function __toString() + { + return $this->id; + } +} diff --git a/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php b/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php new file mode 100644 index 0000000..12bc761 --- /dev/null +++ b/vendor/symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.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\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * @author Nicolas Grekas + */ +class EnvPlaceholderParameterBag extends ParameterBag +{ + private $envPlaceholders = array(); + private $providedTypes = array(); + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (0 === strpos($name, 'env(') && ')' === substr($name, -1) && 'env()' !== $name) { + $env = substr($name, 4, -1); + + if (isset($this->envPlaceholders[$env])) { + foreach ($this->envPlaceholders[$env] as $placeholder) { + return $placeholder; // return first result + } + } + if (!preg_match('/^(?:\w++:)*+\w++$/', $env)) { + throw new InvalidArgumentException(sprintf('Invalid %s name: only "word" characters are allowed.', $name)); + } + + if ($this->has($name)) { + $defaultValue = parent::get($name); + + if (null !== $defaultValue && !is_scalar($defaultValue)) { + throw new RuntimeException(sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', \gettype($defaultValue), $name)); + } + } + + $uniqueName = md5($name.uniqid(mt_rand(), true)); + $placeholder = sprintf('env_%s_%s', str_replace(':', '_', $env), $uniqueName); + $this->envPlaceholders[$env][$placeholder] = $placeholder; + + return $placeholder; + } + + return parent::get($name); + } + + /** + * Returns the map of env vars used in the resolved parameter values to their placeholders. + * + * @return string[][] A map of env var names to their placeholders + */ + public function getEnvPlaceholders() + { + return $this->envPlaceholders; + } + + /** + * Merges the env placeholders of another EnvPlaceholderParameterBag. + */ + public function mergeEnvPlaceholders(self $bag) + { + if ($newPlaceholders = $bag->getEnvPlaceholders()) { + $this->envPlaceholders += $newPlaceholders; + + foreach ($newPlaceholders as $env => $placeholders) { + $this->envPlaceholders[$env] += $placeholders; + } + } + } + + /** + * Maps env prefixes to their corresponding PHP types. + */ + public function setProvidedTypes(array $providedTypes) + { + $this->providedTypes = $providedTypes; + } + + /** + * Gets the PHP types corresponding to env() parameter prefixes. + * + * @return string[][] + */ + public function getProvidedTypes() + { + return $this->providedTypes; + } + + /** + * {@inheritdoc} + */ + public function resolve() + { + if ($this->resolved) { + return; + } + parent::resolve(); + + foreach ($this->envPlaceholders as $env => $placeholders) { + if (!$this->has($name = "env($env)")) { + continue; + } + if (is_numeric($default = $this->parameters[$name])) { + $this->parameters[$name] = (string) $default; + } elseif (null !== $default && !is_scalar($default)) { + throw new RuntimeException(sprintf('The default value of env parameter "%s" must be scalar or null, %s given.', $env, \gettype($default))); + } + } + } +} diff --git a/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php b/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.php new file mode 100644 index 0000000..ad65ad9 --- /dev/null +++ b/vendor/symfony/dependency-injection/ParameterBag/FrozenParameterBag.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\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * Holds read-only parameters. + * + * @author Fabien Potencier + */ +class FrozenParameterBag extends ParameterBag +{ + /** + * For performance reasons, the constructor assumes that + * all keys are already lowercased. + * + * This is always the case when used internally. + * + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + $this->resolved = true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + throw new LogicException('Impossible to call clear() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function add(array $parameters) + { + throw new LogicException('Impossible to call add() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + throw new LogicException('Impossible to call remove() on a frozen ParameterBag.'); + } +} diff --git a/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php b/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.php new file mode 100644 index 0000000..91f61b6 --- /dev/null +++ b/vendor/symfony/dependency-injection/ParameterBag/ParameterBag.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\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +/** + * Holds parameters. + * + * @author Fabien Potencier + */ +class ParameterBag implements ParameterBagInterface +{ + protected $parameters = array(); + protected $resolved = false; + + /** + * @param array $parameters An array of parameters + */ + public function __construct(array $parameters = array()) + { + $this->add($parameters); + } + + /** + * Clears all parameters. + */ + public function clear() + { + $this->parameters = array(); + } + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + */ + public function add(array $parameters) + { + foreach ($parameters as $key => $value) { + $this->set($key, $value); + } + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->parameters; + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + $name = (string) $name; + + if (!array_key_exists($name, $this->parameters)) { + if (!$name) { + throw new ParameterNotFoundException($name); + } + + $alternatives = array(); + foreach ($this->parameters as $key => $parameterValue) { + $lev = levenshtein($name, $key); + if ($lev <= \strlen($name) / 3 || false !== strpos($key, $name)) { + $alternatives[] = $key; + } + } + + $nonNestedAlternative = null; + if (!\count($alternatives) && false !== strpos($name, '.')) { + $namePartsLength = array_map('strlen', explode('.', $name)); + $key = substr($name, 0, -1 * (1 + array_pop($namePartsLength))); + while (\count($namePartsLength)) { + if ($this->has($key)) { + if (\is_array($this->get($key))) { + $nonNestedAlternative = $key; + } + break; + } + + $key = substr($key, 0, -1 * (1 + array_pop($namePartsLength))); + } + } + + throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative); + } + + return $this->parameters[$name]; + } + + /** + * Sets a service container parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + */ + public function set($name, $value) + { + $this->parameters[(string) $name] = $value; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return array_key_exists((string) $name, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $name The parameter name + */ + public function remove($name) + { + unset($this->parameters[(string) $name]); + } + + /** + * {@inheritdoc} + */ + public function resolve() + { + if ($this->resolved) { + return; + } + + $parameters = array(); + foreach ($this->parameters as $key => $value) { + try { + $value = $this->resolveValue($value); + $parameters[$key] = $this->unescapeValue($value); + } catch (ParameterNotFoundException $e) { + $e->setSourceKey($key); + + throw $e; + } + } + + $this->parameters = $parameters; + $this->resolved = true; + } + + /** + * Replaces parameter placeholders (%name%) by their values. + * + * @param mixed $value A value + * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) + * + * @return mixed The resolved value + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + * @throws ParameterCircularReferenceException if a circular reference if detected + * @throws RuntimeException when a given parameter has a type problem + */ + public function resolveValue($value, array $resolving = array()) + { + if (\is_array($value)) { + $args = array(); + foreach ($value as $k => $v) { + $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving); + } + + return $args; + } + + if (!\is_string($value) || 2 > \strlen($value)) { + return $value; + } + + return $this->resolveString($value, $resolving); + } + + /** + * Resolves parameters inside a string. + * + * @param string $value The string to resolve + * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) + * + * @return string The resolved string + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + * @throws ParameterCircularReferenceException if a circular reference if detected + * @throws RuntimeException when a given parameter has a type problem + */ + public function resolveString($value, array $resolving = array()) + { + // we do this to deal with non string values (Boolean, integer, ...) + // as the preg_replace_callback throw an exception when trying + // a non-string in a parameter value + if (preg_match('/^%([^%\s]+)%$/', $value, $match)) { + $key = $match[1]; + + if (isset($resolving[$key])) { + throw new ParameterCircularReferenceException(array_keys($resolving)); + } + + $resolving[$key] = true; + + return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving); + } + + return preg_replace_callback('/%%|%([^%\s]+)%/', function ($match) use ($resolving, $value) { + // skip %% + if (!isset($match[1])) { + return '%%'; + } + + $key = $match[1]; + if (isset($resolving[$key])) { + throw new ParameterCircularReferenceException(array_keys($resolving)); + } + + $resolved = $this->get($key); + + if (!\is_string($resolved) && !is_numeric($resolved)) { + throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type %s inside string value "%s".', $key, \gettype($resolved), $value)); + } + + $resolved = (string) $resolved; + $resolving[$key] = true; + + return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); + }, $value); + } + + public function isResolved() + { + return $this->resolved; + } + + /** + * {@inheritdoc} + */ + public function escapeValue($value) + { + if (\is_string($value)) { + return str_replace('%', '%%', $value); + } + + if (\is_array($value)) { + $result = array(); + foreach ($value as $k => $v) { + $result[$k] = $this->escapeValue($v); + } + + return $result; + } + + return $value; + } + + /** + * {@inheritdoc} + */ + public function unescapeValue($value) + { + if (\is_string($value)) { + return str_replace('%%', '%', $value); + } + + if (\is_array($value)) { + $result = array(); + foreach ($value as $k => $v) { + $result[$k] = $this->unescapeValue($v); + } + + return $result; + } + + return $value; + } +} diff --git a/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php b/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.php new file mode 100644 index 0000000..7386df0 --- /dev/null +++ b/vendor/symfony/dependency-injection/ParameterBag/ParameterBagInterface.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\DependencyInjection\ParameterBag; + +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; + +/** + * ParameterBagInterface. + * + * @author Fabien Potencier + */ +interface ParameterBagInterface +{ + /** + * Clears all parameters. + * + * @throws LogicException if the ParameterBagInterface can not be cleared + */ + public function clear(); + + /** + * Adds parameters to the service container parameters. + * + * @param array $parameters An array of parameters + * + * @throws LogicException if the parameter can not be added + */ + public function add(array $parameters); + + /** + * Gets the service container parameters. + * + * @return array An array of parameters + */ + public function all(); + + /** + * Gets a service container parameter. + * + * @param string $name The parameter name + * + * @return mixed The parameter value + * + * @throws ParameterNotFoundException if the parameter is not defined + */ + public function get($name); + + /** + * Removes a parameter. + * + * @param string $name The parameter name + */ + public function remove($name); + + /** + * Sets a service container parameter. + * + * @param string $name The parameter name + * @param mixed $value The parameter value + * + * @throws LogicException if the parameter can not be set + */ + public function set($name, $value); + + /** + * Returns true if a parameter name is defined. + * + * @param string $name The parameter name + * + * @return bool true if the parameter name is defined, false otherwise + */ + public function has($name); + + /** + * Replaces parameter placeholders (%name%) by their values for all parameters. + */ + public function resolve(); + + /** + * Replaces parameter placeholders (%name%) by their values. + * + * @param mixed $value A value + * + * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist + */ + public function resolveValue($value); + + /** + * Escape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function escapeValue($value); + + /** + * Unescape parameter placeholders %. + * + * @param mixed $value + * + * @return mixed + */ + public function unescapeValue($value); +} diff --git a/vendor/symfony/dependency-injection/README.md b/vendor/symfony/dependency-injection/README.md new file mode 100644 index 0000000..932647f --- /dev/null +++ b/vendor/symfony/dependency-injection/README.md @@ -0,0 +1,14 @@ +DependencyInjection Component +============================= + +The DependencyInjection component allows you to standardize and centralize the +way objects are constructed in your application. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/dependency_injection/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/vendor/symfony/dependency-injection/Reference.php b/vendor/symfony/dependency-injection/Reference.php new file mode 100644 index 0000000..c13cf6f --- /dev/null +++ b/vendor/symfony/dependency-injection/Reference.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\DependencyInjection; + +/** + * Reference represents a service reference. + * + * @author Fabien Potencier + */ +class Reference +{ + private $id; + private $invalidBehavior; + + public function __construct(string $id, int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + { + $this->id = $id; + $this->invalidBehavior = $invalidBehavior; + } + + /** + * @return string The service identifier + */ + public function __toString() + { + return $this->id; + } + + /** + * Returns the behavior to be used when the service does not exist. + * + * @return int + */ + public function getInvalidBehavior() + { + return $this->invalidBehavior; + } +} diff --git a/vendor/symfony/dependency-injection/ResettableContainerInterface.php b/vendor/symfony/dependency-injection/ResettableContainerInterface.php new file mode 100644 index 0000000..b74e676 --- /dev/null +++ b/vendor/symfony/dependency-injection/ResettableContainerInterface.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\DependencyInjection; + +/** + * ResettableContainerInterface defines additional resetting functionality + * for containers, allowing to release shared services when the container is + * not needed anymore. + * + * @author Christophe Coevoet + */ +interface ResettableContainerInterface extends ContainerInterface +{ + /** + * Resets shared services from the container. + * + * The container is not intended to be used again after being reset in a normal workflow. This method is + * meant as a way to release references for ref-counting. + * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service. + */ + public function reset(); +} diff --git a/vendor/symfony/dependency-injection/ServiceLocator.php b/vendor/symfony/dependency-injection/ServiceLocator.php new file mode 100644 index 0000000..bdedc88 --- /dev/null +++ b/vendor/symfony/dependency-injection/ServiceLocator.php @@ -0,0 +1,145 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; + +/** + * @author Robin Chalas + * @author Nicolas Grekas + */ +class ServiceLocator implements PsrContainerInterface +{ + private $factories; + private $loading = array(); + private $externalId; + private $container; + + /** + * @param callable[] $factories + */ + public function __construct(array $factories) + { + $this->factories = $factories; + } + + /** + * {@inheritdoc} + */ + public function has($id) + { + return isset($this->factories[$id]); + } + + /** + * {@inheritdoc} + */ + public function get($id) + { + if (!isset($this->factories[$id])) { + throw new ServiceNotFoundException($id, end($this->loading) ?: null, null, array(), $this->createServiceNotFoundMessage($id)); + } + + if (isset($this->loading[$id])) { + $ids = array_values($this->loading); + $ids = \array_slice($this->loading, array_search($id, $ids)); + $ids[] = $id; + + throw new ServiceCircularReferenceException($id, $ids); + } + + $this->loading[$id] = $id; + try { + return $this->factories[$id](); + } finally { + unset($this->loading[$id]); + } + } + + public function __invoke($id) + { + return isset($this->factories[$id]) ? $this->get($id) : null; + } + + /** + * @internal + */ + public function withContext($externalId, Container $container) + { + $locator = clone $this; + $locator->externalId = $externalId; + $locator->container = $container; + + return $locator; + } + + private function createServiceNotFoundMessage($id) + { + if ($this->loading) { + return sprintf('The service "%s" has a dependency on a non-existent service "%s". This locator %s', end($this->loading), $id, $this->formatAlternatives()); + } + + $class = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 3); + $class = isset($class[2]['object']) ? \get_class($class[2]['object']) : null; + $externalId = $this->externalId ?: $class; + + $msg = sprintf('Service "%s" not found: ', $id); + + if (!$this->container) { + $class = null; + } elseif ($this->container->has($id) || isset($this->container->getRemovedIds()[$id])) { + $msg .= 'even though it exists in the app\'s container, '; + } else { + try { + $this->container->get($id); + $class = null; + } catch (ServiceNotFoundException $e) { + if ($e->getAlternatives()) { + $msg .= sprintf(' did you mean %s? Anyway, ', $this->formatAlternatives($e->getAlternatives(), 'or')); + } else { + $class = null; + } + } + } + if ($externalId) { + $msg .= sprintf('the container inside "%s" is a smaller service locator that %s', $externalId, $this->formatAlternatives()); + } else { + $msg .= sprintf('the current service locator %s', $this->formatAlternatives()); + } + + if (!$class) { + // no-op + } elseif (is_subclass_of($class, ServiceSubscriberInterface::class)) { + $msg .= sprintf(' Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "%s::getSubscribedServices()".', preg_replace('/([^\\\\]++\\\\)++/', '', $class)); + } else { + $msg .= 'Try using dependency injection instead.'; + } + + return $msg; + } + + private function formatAlternatives(array $alternatives = null, $separator = 'and') + { + $format = '"%s"%s'; + if (null === $alternatives) { + if (!$alternatives = array_keys($this->factories)) { + return 'is empty...'; + } + $format = sprintf('only knows about the %s service%s.', $format, 1 < \count($alternatives) ? 's' : ''); + } + $last = array_pop($alternatives); + + return sprintf($format, $alternatives ? implode('", "', $alternatives) : $last, $alternatives ? sprintf(' %s "%s"', $separator, $last) : ''); + } +} diff --git a/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php b/vendor/symfony/dependency-injection/ServiceSubscriberInterface.php new file mode 100644 index 0000000..7024484 --- /dev/null +++ b/vendor/symfony/dependency-injection/ServiceSubscriberInterface.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\DependencyInjection; + +/** + * A ServiceSubscriber exposes its dependencies via the static {@link getSubscribedServices} method. + * + * The getSubscribedServices method returns an array of service types required by such instances, + * optionally keyed by the service names used internally. Service types that start with an interrogation + * mark "?" are optional, while the other ones are mandatory service dependencies. + * + * The injected service locators SHOULD NOT allow access to any other services not specified by the method. + * + * It is expected that ServiceSubscriber instances consume PSR-11-based service locators internally. + * This interface does not dictate any injection method for these service locators, although constructor + * injection is recommended. + * + * @author Nicolas Grekas + */ +interface ServiceSubscriberInterface +{ + /** + * Returns an array of service types required by such instances, optionally keyed by the service names used internally. + * + * For mandatory dependencies: + * + * * array('logger' => 'Psr\Log\LoggerInterface') means the objects use the "logger" name + * internally to fetch a service which must implement Psr\Log\LoggerInterface. + * * array('Psr\Log\LoggerInterface') is a shortcut for + * * array('Psr\Log\LoggerInterface' => 'Psr\Log\LoggerInterface') + * + * otherwise: + * + * * array('logger' => '?Psr\Log\LoggerInterface') denotes an optional dependency + * * array('?Psr\Log\LoggerInterface') is a shortcut for + * * array('Psr\Log\LoggerInterface' => '?Psr\Log\LoggerInterface') + * + * @return array The required service types, optionally keyed by service names + */ + public static function getSubscribedServices(); +} diff --git a/vendor/symfony/dependency-injection/TaggedContainerInterface.php b/vendor/symfony/dependency-injection/TaggedContainerInterface.php new file mode 100644 index 0000000..90b297f --- /dev/null +++ b/vendor/symfony/dependency-injection/TaggedContainerInterface.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\DependencyInjection; + +/** + * TaggedContainerInterface is the interface implemented when a container knows how to deals with tags. + * + * @author Fabien Potencier + */ +interface TaggedContainerInterface extends ContainerInterface +{ + /** + * Returns service ids for a given tag. + * + * @param string $name The tag name + * + * @return array An array of tags + */ + public function findTaggedServiceIds($name); +} diff --git a/vendor/symfony/dependency-injection/Tests/Argument/RewindableGeneratorTest.php b/vendor/symfony/dependency-injection/Tests/Argument/RewindableGeneratorTest.php new file mode 100644 index 0000000..31e3f11 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Argument/RewindableGeneratorTest.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\DependencyInjection\Tests\Argument; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; + +class RewindableGeneratorTest extends TestCase +{ + public function testImplementsCountable() + { + $this->assertInstanceOf(\Countable::class, new RewindableGenerator(function () { + yield 1; + }, 1)); + } + + public function testCountUsesProvidedValue() + { + $generator = new RewindableGenerator(function () { + yield 1; + }, 3); + + $this->assertCount(3, $generator); + } + + public function testCountUsesProvidedValueAsCallback() + { + $called = 0; + $generator = new RewindableGenerator(function () { + yield 1; + }, function () use (&$called) { + ++$called; + + return 3; + }); + + $this->assertSame(0, $called, 'Count callback is called lazily'); + $this->assertCount(3, $generator); + + \count($generator); + + $this->assertSame(1, $called, 'Count callback is called only once'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ChildDefinitionTest.php b/vendor/symfony/dependency-injection/Tests/ChildDefinitionTest.php new file mode 100644 index 0000000..7f359b9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ChildDefinitionTest.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\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ChildDefinition; + +class ChildDefinitionTest extends TestCase +{ + public function testConstructor() + { + $def = new ChildDefinition('foo'); + + $this->assertSame('foo', $def->getParent()); + $this->assertSame(array(), $def->getChanges()); + } + + /** + * @dataProvider getPropertyTests + */ + public function testSetProperty($property, $changeKey) + { + $def = new ChildDefinition('foo'); + + $getter = 'get'.ucfirst($property); + $setter = 'set'.ucfirst($property); + + $this->assertNull($def->$getter()); + $this->assertSame($def, $def->$setter('foo')); + $this->assertSame('foo', $def->$getter()); + $this->assertSame(array($changeKey => true), $def->getChanges()); + } + + public function getPropertyTests() + { + return array( + array('class', 'class'), + array('factory', 'factory'), + array('configurator', 'configurator'), + array('file', 'file'), + ); + } + + public function testSetPublic() + { + $def = new ChildDefinition('foo'); + + $this->assertTrue($def->isPublic()); + $this->assertSame($def, $def->setPublic(false)); + $this->assertFalse($def->isPublic()); + $this->assertSame(array('public' => true), $def->getChanges()); + } + + public function testSetLazy() + { + $def = new ChildDefinition('foo'); + + $this->assertFalse($def->isLazy()); + $this->assertSame($def, $def->setLazy(false)); + $this->assertFalse($def->isLazy()); + $this->assertSame(array('lazy' => true), $def->getChanges()); + } + + public function testSetAutowired() + { + $def = new ChildDefinition('foo'); + + $this->assertFalse($def->isAutowired()); + $this->assertSame($def, $def->setAutowired(true)); + $this->assertTrue($def->isAutowired()); + $this->assertSame(array('autowired' => true), $def->getChanges()); + } + + public function testSetArgument() + { + $def = new ChildDefinition('foo'); + + $this->assertSame(array(), $def->getArguments()); + $this->assertSame($def, $def->replaceArgument(0, 'foo')); + $this->assertSame(array('index_0' => 'foo'), $def->getArguments()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testReplaceArgumentShouldRequireIntegerIndex() + { + $def = new ChildDefinition('foo'); + + $def->replaceArgument('0', 'foo'); + } + + public function testReplaceArgument() + { + $def = new ChildDefinition('foo'); + + $def->setArguments(array(0 => 'foo', 1 => 'bar')); + $this->assertSame('foo', $def->getArgument(0)); + $this->assertSame('bar', $def->getArgument(1)); + + $this->assertSame($def, $def->replaceArgument(1, 'baz')); + $this->assertSame('foo', $def->getArgument(0)); + $this->assertSame('baz', $def->getArgument(1)); + + $this->assertSame(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz'), $def->getArguments()); + + $this->assertSame($def, $def->replaceArgument('$bar', 'val')); + $this->assertSame('val', $def->getArgument('$bar')); + $this->assertSame(array(0 => 'foo', 1 => 'bar', 'index_1' => 'baz', '$bar' => 'val'), $def->getArguments()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetArgumentShouldCheckBounds() + { + $def = new ChildDefinition('foo'); + + $def->setArguments(array(0 => 'foo')); + $def->replaceArgument(0, 'foo'); + + $def->getArgument(1); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\BadMethodCallException + */ + public function testCannotCallSetAutoconfigured() + { + $def = new ChildDefinition('foo'); + $def->setAutoconfigured(true); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\BadMethodCallException + */ + public function testCannotCallSetInstanceofConditionals() + { + $def = new ChildDefinition('foo'); + $def->setInstanceofConditionals(array('Foo' => new ChildDefinition(''))); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.php new file mode 100644 index 0000000..8d44fad --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/AnalyzeServiceReferencesPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class AnalyzeServiceReferencesPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $a = $container + ->register('a') + ->addArgument($ref1 = new Reference('b')) + ; + + $b = $container + ->register('b') + ->addMethodCall('setA', array($ref2 = new Reference('a'))) + ; + + $c = $container + ->register('c') + ->addArgument($ref3 = new Reference('a')) + ->addArgument($ref4 = new Reference('b')) + ; + + $d = $container + ->register('d') + ->setProperty('foo', $ref5 = new Reference('b')) + ; + + $e = $container + ->register('e') + ->setConfigurator(array($ref6 = new Reference('b'), 'methodName')) + ; + + $graph = $this->process($container); + + $this->assertCount(4, $edges = $graph->getNode('b')->getInEdges()); + + $this->assertSame($ref1, $edges[0]->getValue()); + $this->assertSame($ref4, $edges[1]->getValue()); + $this->assertSame($ref5, $edges[2]->getValue()); + $this->assertSame($ref6, $edges[3]->getValue()); + } + + public function testProcessMarksEdgesLazyWhenReferencedServiceIsLazy() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ->setLazy(true) + ->addArgument($ref1 = new Reference('b')) + ; + + $container + ->register('b') + ->addArgument($ref2 = new Reference('a')) + ; + + $graph = $this->process($container); + + $this->assertCount(1, $graph->getNode('b')->getInEdges()); + $this->assertCount(1, $edges = $graph->getNode('a')->getInEdges()); + + $this->assertSame($ref2, $edges[0]->getValue()); + $this->assertTrue($edges[0]->isLazy()); + } + + public function testProcessMarksEdgesLazyWhenReferencedFromIteratorArgument() + { + $container = new ContainerBuilder(); + $container->register('a'); + $container->register('b'); + + $container + ->register('c') + ->addArgument($ref1 = new Reference('a')) + ->addArgument(new IteratorArgument(array($ref2 = new Reference('b')))) + ; + + $graph = $this->process($container); + + $this->assertCount(1, $graph->getNode('a')->getInEdges()); + $this->assertCount(1, $graph->getNode('b')->getInEdges()); + $this->assertCount(2, $edges = $graph->getNode('c')->getOutEdges()); + + $this->assertSame($ref1, $edges[0]->getValue()); + $this->assertFalse($edges[0]->isLazy()); + $this->assertSame($ref2, $edges[1]->getValue()); + $this->assertTrue($edges[1]->isLazy()); + } + + public function testProcessDetectsReferencesFromInlinedDefinitions() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + + $container + ->register('b') + ->addArgument(new Definition(null, array($ref = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); + $this->assertSame($ref, $refs[0]->getValue()); + } + + public function testProcessDetectsReferencesFromIteratorArguments() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + + $container + ->register('b') + ->addArgument(new IteratorArgument(array($ref = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); + $this->assertSame($ref, $refs[0]->getValue()); + } + + public function testProcessDetectsReferencesFromInlinedFactoryDefinitions() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + + $factory = new Definition(); + $factory->setFactory(array(new Reference('a'), 'a')); + + $container + ->register('b') + ->addArgument($factory) + ; + + $graph = $this->process($container); + + $this->assertTrue($graph->hasNode('a')); + $this->assertCount(1, $refs = $graph->getNode('a')->getInEdges()); + } + + public function testProcessDoesNotSaveDuplicateReferences() + { + $container = new ContainerBuilder(); + + $container + ->register('a') + ; + $container + ->register('b') + ->addArgument(new Definition(null, array($ref1 = new Reference('a')))) + ->addArgument(new Definition(null, array($ref2 = new Reference('a')))) + ; + + $graph = $this->process($container); + + $this->assertCount(2, $graph->getNode('a')->getInEdges()); + } + + public function testProcessDetectsFactoryReferences() + { + $container = new ContainerBuilder(); + + $container + ->register('foo', 'stdClass') + ->setFactory(array('stdClass', 'getInstance')); + + $container + ->register('bar', 'stdClass') + ->setFactory(array(new Reference('foo'), 'getInstance')); + + $graph = $this->process($container); + + $this->assertTrue($graph->hasNode('foo')); + $this->assertCount(1, $graph->getNode('foo')->getInEdges()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new RepeatedPass(array(new AnalyzeServiceReferencesPass())); + $pass->process($container); + + return $container->getCompiler()->getServiceReferenceGraph(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.php new file mode 100644 index 0000000..f76001a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/AutoAliasServicePassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\AutoAliasServicePass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class AutoAliasServicePassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException + */ + public function testProcessWithMissingParameter() + { + $container = new ContainerBuilder(); + + $container->register('example') + ->addTag('auto_alias', array('format' => '%non_existing%.example')); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testProcessWithMissingFormat() + { + $container = new ContainerBuilder(); + + $container->register('example') + ->addTag('auto_alias', array()); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + } + + public function testProcessWithNonExistingAlias() + { + $container = new ContainerBuilder(); + + $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') + ->addTag('auto_alias', array('format' => '%existing%.example')); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + + $this->assertEquals('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault', $container->getDefinition('example')->getClass()); + } + + public function testProcessWithExistingAlias() + { + $container = new ContainerBuilder(); + + $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') + ->addTag('auto_alias', array('format' => '%existing%.example')); + + $container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql'); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + + $this->assertTrue($container->hasAlias('example')); + $this->assertEquals('mysql.example', $container->getAlias('example')); + $this->assertSame('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql', $container->getDefinition('mysql.example')->getClass()); + } + + public function testProcessWithManualAlias() + { + $container = new ContainerBuilder(); + + $container->register('example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassDefault') + ->addTag('auto_alias', array('format' => '%existing%.example')); + + $container->register('mysql.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMysql'); + $container->register('mariadb.example', 'Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb'); + $container->setAlias('example', 'mariadb.example'); + $container->setParameter('existing', 'mysql'); + + $pass = new AutoAliasServicePass(); + $pass->process($container); + + $this->assertTrue($container->hasAlias('example')); + $this->assertEquals('mariadb.example', $container->getAlias('example')); + $this->assertSame('Symfony\Component\DependencyInjection\Tests\Compiler\ServiceClassMariaDb', $container->getDefinition('mariadb.example')->getClass()); + } +} + +class ServiceClassDefault +{ +} + +class ServiceClassMysql extends ServiceClassDefault +{ +} + +class ServiceClassMariaDb extends ServiceClassMysql +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php new file mode 100644 index 0000000..4598824 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/AutowirePassTest.php @@ -0,0 +1,814 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Compiler\AutowirePass; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\includes\FooVariadic; +use Symfony\Component\DependencyInjection\TypedReference; + +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + +/** + * @author Kévin Dunglas + */ +class AutowirePassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container->register(Foo::class); + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(1, $container->getDefinition('bar')->getArguments()); + $this->assertEquals(Foo::class, (string) $container->getDefinition('bar')->getArgument(0)); + } + + public function testProcessVariadic() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $definition = $container->register('fooVariadic', FooVariadic::class); + $definition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(1, $container->getDefinition('fooVariadic')->getArguments()); + $this->assertEquals(Foo::class, (string) $container->getDefinition('fooVariadic')->getArgument(0)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot autowire service "c": argument "$a" of method "Symfony\Component\DependencyInjection\Tests\Compiler\C::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\B" service. + */ + public function testProcessAutowireParent() + { + $container = new ContainerBuilder(); + + $container->register(B::class); + $cDefinition = $container->register('c', __NAMESPACE__.'\C'); + $cDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(1, $container->getDefinition('c')->getArguments()); + $this->assertEquals(B::class, (string) $container->getDefinition('c')->getArgument(0)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot autowire service "g": argument "$d" of method "Symfony\Component\DependencyInjection\Tests\Compiler\G::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DInterface" but no such service exists. You should maybe alias this interface to the existing "Symfony\Component\DependencyInjection\Tests\Compiler\F" service. + */ + public function testProcessAutowireInterface() + { + $container = new ContainerBuilder(); + + $container->register(F::class); + $gDefinition = $container->register('g', __NAMESPACE__.'\G'); + $gDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(3, $container->getDefinition('g')->getArguments()); + $this->assertEquals(F::class, (string) $container->getDefinition('g')->getArgument(0)); + $this->assertEquals(F::class, (string) $container->getDefinition('g')->getArgument(1)); + $this->assertEquals(F::class, (string) $container->getDefinition('g')->getArgument(2)); + } + + public function testCompleteExistingDefinition() + { + $container = new ContainerBuilder(); + + $container->register('b', __NAMESPACE__.'\B'); + $container->register(DInterface::class, F::class); + $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument(new Reference('b')); + $hDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(2, $container->getDefinition('h')->getArguments()); + $this->assertEquals('b', (string) $container->getDefinition('h')->getArgument(0)); + $this->assertEquals(DInterface::class, (string) $container->getDefinition('h')->getArgument(1)); + } + + public function testCompleteExistingDefinitionWithNotDefinedArguments() + { + $container = new ContainerBuilder(); + + $container->register(B::class); + $container->register(DInterface::class, F::class); + $hDefinition = $container->register('h', __NAMESPACE__.'\H')->addArgument('')->addArgument(''); + $hDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(2, $container->getDefinition('h')->getArguments()); + $this->assertEquals(B::class, (string) $container->getDefinition('h')->getArgument(0)); + $this->assertEquals(DInterface::class, (string) $container->getDefinition('h')->getArgument(1)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Invalid service "private_service": constructor of class "Symfony\Component\DependencyInjection\Tests\Compiler\PrivateConstructor" must be public. + */ + public function testPrivateConstructorThrowsAutowireException() + { + $container = new ContainerBuilder(); + + $container->autowire('private_service', __NAMESPACE__.'\PrivateConstructor'); + + $pass = new AutowirePass(true); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\CannotBeAutowired::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "c1", "c2", "c3". + */ + public function testTypeCollision() + { + $container = new ContainerBuilder(); + + $container->register('c1', __NAMESPACE__.'\CollisionA'); + $container->register('c2', __NAMESPACE__.'\CollisionB'); + $container->register('c3', __NAMESPACE__.'\CollisionB'); + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "a": argument "$k" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotGuessableArgument::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but no such service exists. You should maybe alias this class to one of these existing services: "a1", "a2". + */ + public function testTypeNotGuessable() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "a": argument "$k" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotGuessableArgumentForSubclass::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\A" but no such service exists. You should maybe alias this class to one of these existing services: "a1", "a2". + */ + public function testTypeNotGuessableWithSubclass() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\B'); + $container->register('a2', __NAMESPACE__.'\B'); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgumentForSubclass'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "a": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\CannotBeAutowired::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists. + */ + public function testTypeNotGuessableNoServicesFound() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testTypeNotGuessableWithTypeSet() + { + $container = new ContainerBuilder(); + + $container->register('a1', __NAMESPACE__.'\Foo'); + $container->register('a2', __NAMESPACE__.'\Foo'); + $container->register(Foo::class, Foo::class); + $aDefinition = $container->register('a', __NAMESPACE__.'\NotGuessableArgument'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('a')->getArguments()); + $this->assertEquals(Foo::class, (string) $container->getDefinition('a')->getArgument(0)); + } + + public function testWithTypeSet() + { + $container = new ContainerBuilder(); + + $container->register('c1', __NAMESPACE__.'\CollisionA'); + $container->register('c2', __NAMESPACE__.'\CollisionB'); + $container->setAlias(CollisionInterface::class, 'c2'); + $aDefinition = $container->register('a', __NAMESPACE__.'\CannotBeAutowired'); + $aDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertCount(1, $container->getDefinition('a')->getArguments()); + $this->assertEquals(CollisionInterface::class, (string) $container->getDefinition('a')->getArgument(0)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "coop_tilleuls": argument "$j" of method "Symfony\Component\DependencyInjection\Tests\Compiler\LesTilleuls::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Dunglas" but no such service exists. + */ + public function testServicesAreNotAutoCreated() + { + $container = new ContainerBuilder(); + + $coopTilleulsDefinition = $container->register('coop_tilleuls', __NAMESPACE__.'\LesTilleuls'); + $coopTilleulsDefinition->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testResolveParameter() + { + $container = new ContainerBuilder(); + + $container->setParameter('class_name', Bar::class); + $container->register(Foo::class); + $barDefinition = $container->register('bar', '%class_name%'); + $barDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertEquals(Foo::class, $container->getDefinition('bar')->getArgument(0)); + } + + public function testOptionalParameter() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Foo::class); + $optDefinition = $container->register('opt', __NAMESPACE__.'\OptionalParameter'); + $optDefinition->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $definition = $container->getDefinition('opt'); + $this->assertNull($definition->getArgument(0)); + $this->assertEquals(A::class, $definition->getArgument(1)); + $this->assertEquals(Foo::class, $definition->getArgument(2)); + } + + public function testDontTriggerAutowiring() + { + $container = new ContainerBuilder(); + + $container->register(Foo::class); + $container->register('bar', __NAMESPACE__.'\Bar'); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertCount(0, $container->getDefinition('bar')->getArguments()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class was not found. + */ + public function testClassNotFoundThrowsException() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\BadTypeHintedArgument'); + $aDefinition->setAutowired(true); + + $container->register(Dunglas::class, Dunglas::class); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass" but this class is missing a parent class (Class Symfony\Bug\NotExistClass not found). + */ + public function testParentClassNotFoundThrowsException() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', __NAMESPACE__.'\BadParentTypeHintedArgument'); + $aDefinition->setAutowired(true); + + $container->register(Dunglas::class, Dunglas::class); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but this service is abstract. You should maybe alias this class to the existing "foo" service. + */ + public function testDontUseAbstractServices() + { + $container = new ContainerBuilder(); + + $container->register(Foo::class)->setAbstract(true); + $container->register('foo', __NAMESPACE__.'\Foo'); + $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + } + + public function testSomeSpecificArgumentsAreSet() + { + $container = new ContainerBuilder(); + + $container->register('foo', Foo::class); + $container->register(A::class); + $container->register(Dunglas::class); + $container->register('multiple', __NAMESPACE__.'\MultipleArguments') + ->setAutowired(true) + // set the 2nd (index 1) argument only: autowire the first and third + // args are: A, Foo, Dunglas + ->setArguments(array( + 1 => new Reference('foo'), + 3 => array('bar'), + )); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $definition = $container->getDefinition('multiple'); + $this->assertEquals( + array( + new TypedReference(A::class, A::class, MultipleArguments::class), + new Reference('foo'), + new TypedReference(Dunglas::class, Dunglas::class, MultipleArguments::class), + array('bar'), + ), + $definition->getArguments() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$bar" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" is type-hinted "array", you should configure its value explicitly. + */ + public function testScalarArgsCannotBeAutowired() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Dunglas::class); + $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments') + ->setArguments(array(1 => 'foo')) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "arg_no_type_hint": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\MultipleArguments::__construct()" has no type-hint, you should configure its value explicitly. + */ + public function testNoTypeArgsCannotBeAutowired() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Dunglas::class); + $container->register('arg_no_type_hint', __NAMESPACE__.'\MultipleArguments') + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + } + + public function testOptionalScalarNotReallyOptionalUsesDefaultValue() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Lille::class); + $definition = $container->register('not_really_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalarNotReallyOptional') + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertSame('default_val', $definition->getArgument(1)); + } + + public function testOptionalScalarArgsDontMessUpOrder() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Lille::class); + $container->register('with_optional_scalar', __NAMESPACE__.'\MultipleArgumentsOptionalScalar') + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $definition = $container->getDefinition('with_optional_scalar'); + $this->assertEquals( + array( + new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class), + // use the default value + 'default_val', + new TypedReference(Lille::class, Lille::class), + ), + $definition->getArguments() + ); + } + + public function testOptionalScalarArgsNotPassedIfLast() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Lille::class); + $container->register('with_optional_scalar_last', __NAMESPACE__.'\MultipleArgumentsOptionalScalarLast') + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $definition = $container->getDefinition('with_optional_scalar_last'); + $this->assertEquals( + array( + new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalarLast::class), + new TypedReference(Lille::class, Lille::class, MultipleArgumentsOptionalScalarLast::class), + ), + $definition->getArguments() + ); + } + + public function testOptionalArgsNoRequiredForCoreClasses() + { + $container = new ContainerBuilder(); + + $container->register('foo', \SplFileObject::class) + ->addArgument('foo.txt') + ->setAutowired(true); + + (new AutowirePass())->process($container); + + $definition = $container->getDefinition('foo'); + $this->assertEquals( + array('foo.txt'), + $definition->getArguments() + ); + } + + public function testSetterInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $container->register(A::class); + $container->register(CollisionA::class); + $container->register(CollisionB::class); + + // manually configure *one* call, to override autowiring + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowired(true) + ->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2')) + ; + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $this->assertEquals( + array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'), + array_column($methodCalls, 0) + ); + + // test setWithCallsConfigured args + $this->assertEquals( + array('manual_arg1', 'manual_arg2'), + $methodCalls[0][1] + ); + // test setFoo args + $this->assertEquals( + array(new TypedReference(Foo::class, Foo::class, SetterInjection::class)), + $methodCalls[1][1] + ); + } + + public function testExplicitMethodInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $container->register(A::class); + $container->register(CollisionA::class); + $container->register(CollisionB::class); + + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowired(true) + ->addMethodCall('notASetter', array()) + ; + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $this->assertEquals( + array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'), + array_column($methodCalls, 0) + ); + $this->assertEquals( + array(new TypedReference(A::class, A::class, SetterInjection::class)), + $methodCalls[0][1] + ); + } + + public function getCreateResourceTests() + { + return array( + array('IdenticalClassResource', true), + array('ClassChangedConstructorArgs', false), + ); + } + + public function testIgnoreServiceWithClassNotExisting() + { + $container = new ContainerBuilder(); + + $container->register('class_not_exist', __NAMESPACE__.'\OptionalServiceClass'); + + $barDefinition = $container->register('bar', __NAMESPACE__.'\Bar'); + $barDefinition->setAutowired(true); + + $container->register(Foo::class, Foo::class); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertTrue($container->hasDefinition('bar')); + } + + public function testSetterInjectionCollisionThrowsException() + { + $container = new ContainerBuilder(); + + $container->register('c1', CollisionA::class); + $container->register('c2', CollisionB::class); + $aDefinition = $container->register('setter_injection_collision', SetterInjectionCollision::class); + $aDefinition->setAutowired(true); + + (new AutowireRequiredMethodsPass())->process($container); + + $pass = new AutowirePass(); + + try { + $pass->process($container); + } catch (AutowiringFailedException $e) { + } + + $this->assertNotNull($e); + $this->assertSame('Cannot autowire service "setter_injection_collision": argument "$collision" of method "Symfony\Component\DependencyInjection\Tests\Compiler\SetterInjectionCollision::setMultipleInstancesForOneArg()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\CollisionInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "c1", "c2".', $e->getMessage()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "my_service": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\K::__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" but no such service exists. Did you create a class that implements this interface? + */ + public function testInterfaceWithNoImplementationSuggestToWriteOne() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('my_service', K::class); + $aDefinition->setAutowired(true); + + (new AutowireRequiredMethodsPass())->process($container); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "bar": argument "$foo" of method "Symfony\Component\DependencyInjection\Tests\Compiler\Bar::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\Foo" but no such service exists. You should maybe alias this class to the existing "foo" service. + */ + public function testProcessDoesNotTriggerDeprecations() + { + $container = new ContainerBuilder(); + $container->register('deprecated', 'Symfony\Component\DependencyInjection\Tests\Fixtures\DeprecatedClass')->setDeprecated(true); + $container->register('foo', __NAMESPACE__.'\Foo'); + $container->register('bar', __NAMESPACE__.'\Bar')->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertTrue($container->hasDefinition('deprecated')); + $this->assertTrue($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + } + + public function testEmptyStringIsKept() + { + $container = new ContainerBuilder(); + + $container->register(A::class); + $container->register(Lille::class); + $container->register('foo', __NAMESPACE__.'\MultipleArgumentsOptionalScalar') + ->setAutowired(true) + ->setArguments(array('', '')); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertEquals(array(new TypedReference(A::class, A::class, MultipleArgumentsOptionalScalar::class), '', new TypedReference(Lille::class, Lille::class)), $container->getDefinition('foo')->getArguments()); + } + + public function testWithFactory() + { + $container = new ContainerBuilder(); + + $container->register(Foo::class); + $definition = $container->register('a', A::class) + ->setFactory(array(A::class, 'create')) + ->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowirePass())->process($container); + + $this->assertEquals(array(new TypedReference(Foo::class, Foo::class, A::class)), $definition->getArguments()); + } + + /** + * @dataProvider provideNotWireableCalls + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + */ + public function testNotWireableCalls($method, $expectedMsg) + { + $container = new ContainerBuilder(); + + $foo = $container->register('foo', NotWireable::class)->setAutowired(true) + ->addMethodCall('setBar', array()) + ->addMethodCall('setOptionalNotAutowireable', array()) + ->addMethodCall('setOptionalNoTypeHint', array()) + ->addMethodCall('setOptionalArgNoAutowireable', array()) + ; + + if ($method) { + $foo->addMethodCall($method, array()); + } + + if (method_exists($this, 'expectException')) { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage($expectedMsg); + } else { + $this->setExpectedException(RuntimeException::class, $expectedMsg); + } + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + } + + public function provideNotWireableCalls() + { + return array( + array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class was not found.'), + array('setDifferentNamespace', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setDifferentNamespace()" references class "stdClass" but no such service exists.'), + array(null, 'Invalid service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "foo": argument "$sam" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireableBecauseOfATypo()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\lesTilleuls" but no such service exists. Did you mean "Symfony\Component\DependencyInjection\Tests\Compiler\LesTilleuls"? + */ + public function testSuggestRegisteredServicesWithSimilarCase() + { + $container = new ContainerBuilder(); + + $container->register(LesTilleuls::class, LesTilleuls::class); + $container->register('foo', NotWireable::class)->setAutowired(true) + ->addMethodCall('setNotAutowireableBecauseOfATypo', array()) + ; + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + (new AutowirePass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead. + */ + public function testByIdAlternative() + { + $container = new ContainerBuilder(); + + $container->setAlias(IInterface::class, 'i'); + $container->register('i', I::class); + $container->register('j', J::class) + ->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. Try changing the type-hint to "Symfony\Component\DependencyInjection\Tests\Compiler\IInterface" instead. + */ + public function testExceptionWhenAliasExists() + { + $container = new ContainerBuilder(); + + // multiple I services... but there *is* IInterface available + $container->setAlias(IInterface::class, 'i'); + $container->register('i', I::class); + $container->register('i2', I::class); + // J type-hints against I concretely + $container->register('j', J::class) + ->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException + * @expectedExceptionMessage Cannot autowire service "j": argument "$i" of method "Symfony\Component\DependencyInjection\Tests\Compiler\J::__construct()" references class "Symfony\Component\DependencyInjection\Tests\Compiler\I" but no such service exists. You should maybe alias this class to one of these existing services: "i", "i2". + */ + public function testExceptionWhenAliasDoesNotExist() + { + $container = new ContainerBuilder(); + + // multiple I instances... but no IInterface alias + $container->register('i', I::class); + $container->register('i2', I::class); + // J type-hints against I concretely + $container->register('j', J::class) + ->setAutowired(true); + + $pass = new AutowirePass(); + $pass->process($container); + } + + public function testInlineServicesAreNotCandidates() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(realpath(__DIR__.'/../Fixtures/xml'))); + $loader->load('services_inline_not_candidate.xml'); + + $pass = new AutowirePass(); + $pass->process($container); + + $this->assertSame(array(), $container->getDefinition('autowired')->getArguments()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/AutowireRequiredMethodsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/AutowireRequiredMethodsPassTest.php new file mode 100644 index 0000000..07c9f9d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/AutowireRequiredMethodsPassTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + +class AutowireRequiredMethodsPassTest extends TestCase +{ + public function testSetterInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $container->register(A::class); + $container->register(CollisionA::class); + $container->register(CollisionB::class); + + // manually configure *one* call, to override autowiring + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowired(true) + ->addMethodCall('setWithCallsConfigured', array('manual_arg1', 'manual_arg2')); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $this->assertEquals( + array('setWithCallsConfigured', 'setFoo', 'setDependencies', 'setChildMethodWithoutDocBlock'), + array_column($methodCalls, 0) + ); + + // test setWithCallsConfigured args + $this->assertEquals( + array('manual_arg1', 'manual_arg2'), + $methodCalls[0][1] + ); + // test setFoo args + $this->assertEquals(array(), $methodCalls[1][1]); + } + + public function testExplicitMethodInjection() + { + $container = new ContainerBuilder(); + $container->register(Foo::class); + $container->register(A::class); + $container->register(CollisionA::class); + $container->register(CollisionB::class); + + $container + ->register('setter_injection', SetterInjection::class) + ->setAutowired(true) + ->addMethodCall('notASetter', array()); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredMethodsPass())->process($container); + + $methodCalls = $container->getDefinition('setter_injection')->getMethodCalls(); + + $this->assertEquals( + array('notASetter', 'setFoo', 'setDependencies', 'setWithCallsConfigured', 'setChildMethodWithoutDocBlock'), + array_column($methodCalls, 0) + ); + $this->assertEquals(array(), $methodCalls[0][1]); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/CheckArgumentsValidityPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/CheckArgumentsValidityPassTest.php new file mode 100644 index 0000000..d121689 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/CheckArgumentsValidityPassTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\CheckArgumentsValidityPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Kévin Dunglas + */ +class CheckArgumentsValidityPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $definition = $container->register('foo'); + $definition->setArguments(array(null, 1, 'a')); + $definition->setMethodCalls(array( + array('bar', array('a', 'b')), + array('baz', array('c', 'd')), + )); + + $pass = new CheckArgumentsValidityPass(); + $pass->process($container); + + $this->assertEquals(array(null, 1, 'a'), $container->getDefinition('foo')->getArguments()); + $this->assertEquals(array( + array('bar', array('a', 'b')), + array('baz', array('c', 'd')), + ), $container->getDefinition('foo')->getMethodCalls()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @dataProvider definitionProvider + */ + public function testException(array $arguments, array $methodCalls) + { + $container = new ContainerBuilder(); + $definition = $container->register('foo'); + $definition->setArguments($arguments); + $definition->setMethodCalls($methodCalls); + + $pass = new CheckArgumentsValidityPass(); + $pass->process($container); + } + + public function definitionProvider() + { + return array( + array(array(null, 'a' => 'a'), array()), + array(array(1 => 1), array()), + array(array(), array(array('baz', array(null, 'a' => 'a')))), + array(array(), array(array('baz', array(1 => 1)))), + ); + } + + public function testNoException() + { + $container = new ContainerBuilder(); + $definition = $container->register('foo'); + $definition->setArguments(array(null, 'a' => 'a')); + + $pass = new CheckArgumentsValidityPass(false); + $pass->process($container); + $this->assertCount(1, $definition->getErrors()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php new file mode 100644 index 0000000..a87dc44 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/CheckCircularReferencesPassTest.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\Compiler; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class CheckCircularReferencesPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('a')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessWithAliases() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->setAlias('b', 'c'); + $container->setAlias('c', 'a'); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessWithFactory() + { + $container = new ContainerBuilder(); + + $container + ->register('a', 'stdClass') + ->setFactory(array(new Reference('b'), 'getInstance')); + + $container + ->register('b', 'stdClass') + ->setFactory(array(new Reference('a'), 'getInstance')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessDetectsIndirectCircularReference() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('c')); + $container->register('c')->addArgument(new Reference('a')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testProcessDetectsIndirectCircularReferenceWithFactory() + { + $container = new ContainerBuilder(); + + $container->register('a')->addArgument(new Reference('b')); + + $container + ->register('b', 'stdClass') + ->setFactory(array(new Reference('c'), 'getInstance')); + + $container->register('c')->addArgument(new Reference('a')); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testDeepCircularReference() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('c')); + $container->register('c')->addArgument(new Reference('b')); + + $this->process($container); + } + + public function testProcessIgnoresMethodCalls() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addMethodCall('setA', array(new Reference('a'))); + + $this->process($container); + + $this->addToAssertionCount(1); + } + + public function testProcessIgnoresLazyServices() + { + $container = new ContainerBuilder(); + $container->register('a')->setLazy(true)->addArgument(new Reference('b')); + $container->register('b')->addArgument(new Reference('a')); + + $this->process($container); + + // just make sure that a lazily loaded service does not trigger a CircularReferenceException + $this->addToAssertionCount(1); + } + + public function testProcessIgnoresIteratorArguments() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b')->addArgument(new IteratorArgument(array(new Reference('a')))); + + $this->process($container); + + // just make sure that an IteratorArgument does not trigger a CircularReferenceException + $this->addToAssertionCount(1); + } + + protected function process(ContainerBuilder $container) + { + $compiler = new Compiler(); + $passConfig = $compiler->getPassConfig(); + $passConfig->setOptimizationPasses(array( + new AnalyzeServiceReferencesPass(true), + new CheckCircularReferencesPass(), + )); + $passConfig->setRemovingPasses(array()); + + $compiler->compile($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.php new file mode 100644 index 0000000..f698ed0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/CheckDefinitionValidityPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\CheckDefinitionValidityPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CheckDefinitionValidityPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testProcessDetectsSyntheticNonPublicDefinitions() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(true)->setPublic(false); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testProcessDetectsNonSyntheticNonAbstractDefinitionWithoutClass() + { + $container = new ContainerBuilder(); + $container->register('a')->setSynthetic(false)->setAbstract(false); + + $this->process($container); + } + + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a', 'class'); + $container->register('b', 'class')->setSynthetic(true)->setPublic(true); + $container->register('c', 'class')->setAbstract(true); + $container->register('d', 'class')->setSynthetic(true); + + $this->process($container); + + $this->addToAssertionCount(1); + } + + public function testValidTags() + { + $container = new ContainerBuilder(); + $container->register('a', 'class')->addTag('foo', array('bar' => 'baz')); + $container->register('b', 'class')->addTag('foo', array('bar' => null)); + $container->register('c', 'class')->addTag('foo', array('bar' => 1)); + $container->register('d', 'class')->addTag('foo', array('bar' => 1.1)); + + $this->process($container); + + $this->addToAssertionCount(1); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testInvalidTags() + { + $container = new ContainerBuilder(); + $container->register('a', 'class')->addTag('foo', array('bar' => array('baz' => 'baz'))); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException + */ + public function testDynamicPublicServiceName() + { + $container = new ContainerBuilder(); + $env = $container->getParameterBag()->get('env(BAR)'); + $container->register("foo.$env", 'class')->setPublic(true); + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException + */ + public function testDynamicPublicAliasName() + { + $container = new ContainerBuilder(); + $env = $container->getParameterBag()->get('env(BAR)'); + $container->setAlias("foo.$env", 'class')->setPublic(true); + + $this->process($container); + } + + public function testDynamicPrivateName() + { + $container = new ContainerBuilder(); + $env = $container->getParameterBag()->get('env(BAR)'); + $container->register("foo.$env", 'class'); + $container->setAlias("bar.$env", 'class'); + + $this->process($container); + + $this->addToAssertionCount(1); + } + + protected function process(ContainerBuilder $container) + { + $pass = new CheckDefinitionValidityPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.php new file mode 100644 index 0000000..631c344 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/CheckExceptionOnInvalidReferenceBehaviorPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class CheckExceptionOnInvalidReferenceBehaviorPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ; + $container->register('b', '\stdClass'); + + $this->process($container); + + $this->addToAssertionCount(1); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + */ + public function testProcessThrowsExceptionOnInvalidReference() + { + $container = new ContainerBuilder(); + + $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ; + + $this->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + */ + public function testProcessThrowsExceptionOnInvalidReferenceFromInlinedDefinition() + { + $container = new ContainerBuilder(); + + $def = new Definition(); + $def->addArgument(new Reference('b')); + + $container + ->register('a', '\stdClass') + ->addArgument($def) + ; + + $this->process($container); + } + + public function testProcessDefinitionWithBindings() + { + $container = new ContainerBuilder(); + + $container + ->register('b') + ->setBindings(array(new BoundArgument(new Reference('a')))) + ; + + $this->process($container); + + $this->addToAssertionCount(1); + } + + private function process(ContainerBuilder $container) + { + $pass = new CheckExceptionOnInvalidReferenceBehaviorPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.php new file mode 100644 index 0000000..22b6fd1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/CheckReferenceValidityPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\CheckReferenceValidityPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class CheckReferenceValidityPassTest extends TestCase +{ + /** + * @expectedException \RuntimeException + */ + public function testProcessDetectsReferenceToAbstractDefinition() + { + $container = new ContainerBuilder(); + + $container->register('a')->setAbstract(true); + $container->register('b')->addArgument(new Reference('a')); + + $this->process($container); + } + + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a')->addArgument(new Reference('b')); + $container->register('b'); + + $this->process($container); + + $this->addToAssertionCount(1); + } + + protected function process(ContainerBuilder $container) + { + $pass = new CheckReferenceValidityPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.php new file mode 100644 index 0000000..24c2695 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/DecoratorServicePassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class DecoratorServicePassTest extends TestCase +{ + public function testProcessWithoutAlias() + { + $container = new ContainerBuilder(); + $fooDefinition = $container + ->register('foo') + ->setPublic(false) + ; + $fooExtendedDefinition = $container + ->register('foo.extended') + ->setPublic(true) + ->setDecoratedService('foo') + ; + $barDefinition = $container + ->register('bar') + ->setPublic(true) + ; + $barExtendedDefinition = $container + ->register('bar.extended') + ->setPublic(true) + ->setDecoratedService('bar', 'bar.yoo') + ; + + $this->process($container); + + $this->assertEquals('foo.extended', $container->getAlias('foo')); + $this->assertFalse($container->getAlias('foo')->isPublic()); + + $this->assertEquals('bar.extended', $container->getAlias('bar')); + $this->assertTrue($container->getAlias('bar')->isPublic()); + + $this->assertSame($fooDefinition, $container->getDefinition('foo.extended.inner')); + $this->assertFalse($container->getDefinition('foo.extended.inner')->isPublic()); + + $this->assertSame($barDefinition, $container->getDefinition('bar.yoo')); + $this->assertFalse($container->getDefinition('bar.yoo')->isPublic()); + + $this->assertNull($fooExtendedDefinition->getDecoratedService()); + $this->assertNull($barExtendedDefinition->getDecoratedService()); + } + + public function testProcessWithAlias() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(true) + ; + $container->setAlias('foo.alias', new Alias('foo', false)); + $fooExtendedDefinition = $container + ->register('foo.extended') + ->setPublic(true) + ->setDecoratedService('foo.alias') + ; + + $this->process($container); + + $this->assertEquals('foo.extended', $container->getAlias('foo.alias')); + $this->assertFalse($container->getAlias('foo.alias')->isPublic()); + + $this->assertEquals('foo', $container->getAlias('foo.extended.inner')); + $this->assertFalse($container->getAlias('foo.extended.inner')->isPublic()); + + $this->assertNull($fooExtendedDefinition->getDecoratedService()); + } + + public function testProcessWithPriority() + { + $container = new ContainerBuilder(); + $fooDefinition = $container + ->register('foo') + ->setPublic(false) + ; + $barDefinition = $container + ->register('bar') + ->setPublic(true) + ->setDecoratedService('foo') + ; + $bazDefinition = $container + ->register('baz') + ->setPublic(true) + ->setDecoratedService('foo', null, 5) + ; + $quxDefinition = $container + ->register('qux') + ->setPublic(true) + ->setDecoratedService('foo', null, 3) + ; + + $this->process($container); + + $this->assertEquals('bar', $container->getAlias('foo')); + $this->assertFalse($container->getAlias('foo')->isPublic()); + + $this->assertSame($fooDefinition, $container->getDefinition('baz.inner')); + $this->assertFalse($container->getDefinition('baz.inner')->isPublic()); + + $this->assertEquals('qux', $container->getAlias('bar.inner')); + $this->assertFalse($container->getAlias('bar.inner')->isPublic()); + + $this->assertEquals('baz', $container->getAlias('qux.inner')); + $this->assertFalse($container->getAlias('qux.inner')->isPublic()); + + $this->assertNull($barDefinition->getDecoratedService()); + $this->assertNull($bazDefinition->getDecoratedService()); + $this->assertNull($quxDefinition->getDecoratedService()); + } + + public function testProcessMovesTagsFromDecoratedDefinitionToDecoratingDefinition() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setTags(array('bar' => array('attr' => 'baz'))) + ; + $container + ->register('baz') + ->setTags(array('foobar' => array('attr' => 'bar'))) + ->setDecoratedService('foo') + ; + + $this->process($container); + + $this->assertEmpty($container->getDefinition('baz.inner')->getTags()); + $this->assertEquals(array('bar' => array('attr' => 'baz'), 'foobar' => array('attr' => 'bar')), $container->getDefinition('baz')->getTags()); + } + + protected function process(ContainerBuilder $container) + { + $repeatedPass = new DecoratorServicePass(); + $repeatedPass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/DefinitionErrorExceptionPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/DefinitionErrorExceptionPassTest.php new file mode 100644 index 0000000..e0585e2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/DefinitionErrorExceptionPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\DefinitionErrorExceptionPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; + +class DefinitionErrorExceptionPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Things went wrong! + */ + public function testThrowsException() + { + $container = new ContainerBuilder(); + $def = new Definition(); + $def->addError('Things went wrong!'); + $def->addError('Now something else!'); + $container->register('foo_service_id') + ->setArguments(array( + $def, + )); + + $pass = new DefinitionErrorExceptionPass(); + $pass->process($container); + } + + public function testNoExceptionThrown() + { + $container = new ContainerBuilder(); + $def = new Definition(); + $container->register('foo_service_id') + ->setArguments(array( + $def, + )); + + $pass = new DefinitionErrorExceptionPass(); + $pass->process($container); + $this->assertSame($def, $container->getDefinition('foo_service_id')->getArgument(0)); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.php new file mode 100644 index 0000000..810fbe4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ExtensionCompilerPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ExtensionCompilerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; + +/** + * @author Wouter J + */ +class ExtensionCompilerPassTest extends TestCase +{ + private $container; + private $pass; + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->pass = new ExtensionCompilerPass(); + } + + public function testProcess() + { + $extension1 = new CompilerPassExtension('extension1'); + $extension2 = new DummyExtension('extension2'); + $extension3 = new DummyExtension('extension3'); + $extension4 = new CompilerPassExtension('extension4'); + + $this->container->registerExtension($extension1); + $this->container->registerExtension($extension2); + $this->container->registerExtension($extension3); + $this->container->registerExtension($extension4); + + $this->pass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('extension1')); + $this->assertFalse($this->container->hasDefinition('extension2')); + $this->assertFalse($this->container->hasDefinition('extension3')); + $this->assertTrue($this->container->hasDefinition('extension4')); + } +} + +class DummyExtension extends Extension +{ + private $alias; + + public function __construct($alias) + { + $this->alias = $alias; + } + + public function getAlias() + { + return $this->alias; + } + + public function load(array $configs, ContainerBuilder $container) + { + } + + public function process(ContainerBuilder $container) + { + $container->register($this->alias); + } +} + +class CompilerPassExtension extends DummyExtension implements CompilerPassInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.php new file mode 100644 index 0000000..2db0aaa --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/InlineServiceDefinitionsPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class InlineServiceDefinitionsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container + ->register('inlinable.service') + ->setPublic(false) + ; + + $container + ->register('service') + ->setArguments(array(new Reference('inlinable.service'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $arguments[0]); + $this->assertSame($container->getDefinition('inlinable.service'), $arguments[0]); + } + + public function testProcessDoesNotInlinesWhenAliasedServiceIsShared() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container->setAlias('moo', 'foo'); + + $container + ->register('service') + ->setArguments(array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertSame($ref, $arguments[0]); + } + + public function testProcessDoesInlineNonSharedService() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setShared(false) + ; + $container + ->register('bar') + ->setPublic(false) + ->setShared(false) + ; + $container->setAlias('moo', 'bar'); + + $container + ->register('service') + ->setArguments(array(new Reference('foo'), $ref = new Reference('moo'), new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertEquals($container->getDefinition('foo'), $arguments[0]); + $this->assertNotSame($container->getDefinition('foo'), $arguments[0]); + $this->assertSame($ref, $arguments[1]); + $this->assertEquals($container->getDefinition('bar'), $arguments[2]); + $this->assertNotSame($container->getDefinition('bar'), $arguments[2]); + } + + public function testProcessDoesNotInlineMixedServicesLoop() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar')) + ->setShared(false) + ; + $container + ->register('bar') + ->setPublic(false) + ->addMethodCall('setFoo', array(new Reference('foo'))) + ; + + $this->process($container); + + $this->assertEquals(new Reference('bar'), $container->getDefinition('foo')->getArgument(0)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + * @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> foo -> bar". + */ + public function testProcessThrowsOnNonSharedLoops() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar')) + ->setShared(false) + ; + $container + ->register('bar') + ->setShared(false) + ->addMethodCall('setFoo', array(new Reference('foo'))) + ; + + $this->process($container); + } + + public function testProcessNestedNonSharedServices() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->addArgument(new Reference('bar1')) + ->addArgument(new Reference('bar2')) + ; + $container + ->register('bar1') + ->setShared(false) + ->addArgument(new Reference('baz')) + ; + $container + ->register('bar2') + ->setShared(false) + ->addArgument(new Reference('baz')) + ; + $container + ->register('baz') + ->setShared(false) + ; + + $this->process($container); + + $baz1 = $container->getDefinition('foo')->getArgument(0)->getArgument(0); + $baz2 = $container->getDefinition('foo')->getArgument(1)->getArgument(0); + + $this->assertEquals($container->getDefinition('baz'), $baz1); + $this->assertEquals($container->getDefinition('baz'), $baz2); + $this->assertNotSame($baz1, $baz2); + } + + public function testProcessInlinesIfMultipleReferencesButAllFromTheSameDefinition() + { + $container = new ContainerBuilder(); + + $a = $container->register('a')->setPublic(false); + $b = $container + ->register('b') + ->addArgument(new Reference('a')) + ->addArgument(new Definition(null, array(new Reference('a')))) + ; + + $this->process($container); + + $arguments = $b->getArguments(); + $this->assertSame($a, $arguments[0]); + + $inlinedArguments = $arguments[1]->getArguments(); + $this->assertSame($a, $inlinedArguments[0]); + } + + public function testProcessInlinesPrivateFactoryReference() + { + $container = new ContainerBuilder(); + + $container->register('a')->setPublic(false); + $b = $container + ->register('b') + ->setPublic(false) + ->setFactory(array(new Reference('a'), 'a')) + ; + + $container + ->register('foo') + ->setArguments(array( + $ref = new Reference('b'), + )); + + $this->process($container); + + $inlinedArguments = $container->getDefinition('foo')->getArguments(); + $this->assertSame($b, $inlinedArguments[0]); + } + + public function testProcessDoesNotInlinePrivateFactoryIfReferencedMultipleTimesWithinTheSameDefinition() + { + $container = new ContainerBuilder(); + $container + ->register('a') + ; + $container + ->register('b') + ->setPublic(false) + ->setFactory(array(new Reference('a'), 'a')) + ; + + $container + ->register('foo') + ->setArguments(array( + $ref1 = new Reference('b'), + $ref2 = new Reference('b'), + )) + ; + $this->process($container); + + $args = $container->getDefinition('foo')->getArguments(); + $this->assertSame($ref1, $args[0]); + $this->assertSame($ref2, $args[1]); + } + + public function testProcessDoesNotInlineReferenceWhenUsedByInlineFactory() + { + $container = new ContainerBuilder(); + $container + ->register('a') + ; + $container + ->register('b') + ->setPublic(false) + ->setFactory(array(new Reference('a'), 'a')) + ; + + $inlineFactory = new Definition(); + $inlineFactory->setPublic(false); + $inlineFactory->setFactory(array(new Reference('b'), 'b')); + + $container + ->register('foo') + ->setArguments(array( + $ref = new Reference('b'), + $inlineFactory, + )) + ; + $this->process($container); + + $args = $container->getDefinition('foo')->getArguments(); + $this->assertSame($ref, $args[0]); + } + + public function testProcessDoesNotInlineWhenServiceIsPrivateButLazy() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ->setLazy(true) + ; + + $container + ->register('service') + ->setArguments(array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $arguments = $container->getDefinition('service')->getArguments(); + $this->assertSame($ref, $arguments[0]); + } + + public function testProcessDoesNotInlineWhenServiceReferencesItself() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ->addMethodCall('foo', array($ref = new Reference('foo'))) + ; + + $this->process($container); + + $calls = $container->getDefinition('foo')->getMethodCalls(); + $this->assertSame($ref, $calls[0][1][0]); + } + + public function testProcessDoesNotSetLazyArgumentValuesAfterInlining() + { + $container = new ContainerBuilder(); + $container + ->register('inline') + ->setShared(false) + ; + $container + ->register('service-closure') + ->setArguments(array(new ServiceClosureArgument(new Reference('inline')))) + ; + $container + ->register('iterator') + ->setArguments(array(new IteratorArgument(array(new Reference('inline'))))) + ; + + $this->process($container); + + $values = $container->getDefinition('service-closure')->getArgument(0)->getValues(); + $this->assertInstanceOf(Reference::class, $values[0]); + $this->assertSame('inline', (string) $values[0]); + + $values = $container->getDefinition('iterator')->getArgument(0)->getValues(); + $this->assertInstanceOf(Reference::class, $values[0]); + $this->assertSame('inline', (string) $values[0]); + } + + protected function process(ContainerBuilder $container) + { + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass())); + $repeatedPass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php new file mode 100644 index 0000000..6695abf --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/IntegrationTest.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This class tests the integration of the different compiler passes. + */ +class IntegrationTest extends TestCase +{ + /** + * This tests that dependencies are correctly processed. + * + * We're checking that: + * + * * A is public, B/C are private + * * A -> C + * * B -> C + */ + public function testProcessRemovesAndInlinesRecursively() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $a = $container + ->register('a', '\stdClass') + ->addArgument(new Reference('c')) + ->setPublic(true) + ; + + $b = $container + ->register('b', '\stdClass') + ->addArgument(new Reference('c')) + ->setPublic(false) + ; + + $c = $container + ->register('c', '\stdClass') + ->setPublic(false) + ; + + $container->compile(); + + $this->assertTrue($container->hasDefinition('a')); + $arguments = $a->getArguments(); + $this->assertSame($c, $arguments[0]); + $this->assertFalse($container->hasDefinition('b')); + $this->assertFalse($container->hasDefinition('c')); + } + + public function testProcessInlinesReferencesToAliases() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $a = $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ->setPublic(true) + ; + + $container->setAlias('b', new Alias('c', false)); + + $c = $container + ->register('c', '\stdClass') + ->setPublic(false) + ; + + $container->compile(); + + $this->assertTrue($container->hasDefinition('a')); + $arguments = $a->getArguments(); + $this->assertSame($c, $arguments[0]); + $this->assertFalse($container->hasAlias('b')); + $this->assertFalse($container->hasDefinition('c')); + } + + public function testProcessInlinesWhenThereAreMultipleReferencesButFromTheSameDefinition() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $container + ->register('a', '\stdClass') + ->addArgument(new Reference('b')) + ->addMethodCall('setC', array(new Reference('c'))) + ->setPublic(true) + ; + + $container + ->register('b', '\stdClass') + ->addArgument(new Reference('c')) + ->setPublic(false) + ; + + $container + ->register('c', '\stdClass') + ->setPublic(false) + ; + + $container->compile(); + + $this->assertTrue($container->hasDefinition('a')); + $this->assertFalse($container->hasDefinition('b')); + $this->assertFalse($container->hasDefinition('c'), 'Service C was not inlined.'); + } + + /** + * @dataProvider getYamlCompileTests + */ + public function testYamlContainerCompiles($directory, $actualServiceId, $expectedServiceId, ContainerBuilder $mainContainer = null) + { + // allow a container to be passed in, which might have autoconfigure settings + $container = $mainContainer ?: new ContainerBuilder(); + $container->setResourceTracking(false); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Fixtures/yaml/integration/'.$directory)); + $loader->load('main.yml'); + $container->compile(); + $actualService = $container->getDefinition($actualServiceId); + + // create a fresh ContainerBuilder, to avoid autoconfigure stuff + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Fixtures/yaml/integration/'.$directory)); + $loader->load('expected.yml'); + $container->compile(); + $expectedService = $container->getDefinition($expectedServiceId); + + // reset changes, we don't care if these differ + $actualService->setChanges(array()); + $expectedService->setChanges(array()); + + $this->assertEquals($expectedService, $actualService); + } + + public function getYamlCompileTests() + { + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(IntegrationTestStub::class); + yield array( + 'autoconfigure_child_not_applied', + 'child_service', + 'child_service_expected', + $container, + ); + + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(IntegrationTestStub::class); + yield array( + 'autoconfigure_parent_child', + 'child_service', + 'child_service_expected', + $container, + ); + + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(IntegrationTestStub::class) + ->addTag('from_autoconfigure'); + yield array( + 'autoconfigure_parent_child_tags', + 'child_service', + 'child_service_expected', + $container, + ); + + yield array( + 'child_parent', + 'child_service', + 'child_service_expected', + ); + + yield array( + 'defaults_child_tags', + 'child_service', + 'child_service_expected', + ); + + yield array( + 'defaults_instanceof_importance', + 'main_service', + 'main_service_expected', + ); + + yield array( + 'defaults_parent_child', + 'child_service', + 'child_service_expected', + ); + + yield array( + 'instanceof_parent_child', + 'child_service', + 'child_service_expected', + ); + } +} + +class IntegrationTestStub extends IntegrationTestStubParent +{ +} + +class IntegrationTestStubParent +{ + public function enableSummer($enable) + { + // methods used in calls - added here to prevent errors for not existing + } + + public function setSunshine($type) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php new file mode 100644 index 0000000..3ebd3f5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/MergeExtensionConfigurationPassTest.php @@ -0,0 +1,199 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationContainerBuilder; +use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class MergeExtensionConfigurationPassTest extends TestCase +{ + public function testExpressionLanguageProviderForwarding() + { + $tmpProviders = array(); + + $extension = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface')->getMock(); + $extension->expects($this->any()) + ->method('getXsdValidationBasePath') + ->will($this->returnValue(false)); + $extension->expects($this->any()) + ->method('getNamespace') + ->will($this->returnValue('http://example.org/schema/dic/foo')); + $extension->expects($this->any()) + ->method('getAlias') + ->will($this->returnValue('foo')); + $extension->expects($this->once()) + ->method('load') + ->will($this->returnCallback(function (array $config, ContainerBuilder $container) use (&$tmpProviders) { + $tmpProviders = $container->getExpressionLanguageProviders(); + })); + + $provider = $this->getMockBuilder('Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface')->getMock(); + $container = new ContainerBuilder(new ParameterBag()); + $container->registerExtension($extension); + $container->prependExtensionConfig('foo', array('bar' => true)); + $container->addExpressionLanguageProvider($provider); + + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + + $this->assertEquals(array($provider), $tmpProviders); + } + + public function testExtensionLoadGetAMergeExtensionConfigurationContainerBuilderInstance() + { + $extension = $this->getMockBuilder(FooExtension::class)->setMethods(array('load'))->getMock(); + $extension->expects($this->once()) + ->method('load') + ->with($this->isType('array'), $this->isInstanceOf(MergeExtensionConfigurationContainerBuilder::class)) + ; + + $container = new ContainerBuilder(new ParameterBag()); + $container->registerExtension($extension); + $container->prependExtensionConfig('foo', array()); + + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + } + + public function testExtensionConfigurationIsTrackedByDefault() + { + $extension = $this->getMockBuilder(FooExtension::class)->setMethods(array('getConfiguration'))->getMock(); + $extension->expects($this->exactly(2)) + ->method('getConfiguration') + ->will($this->returnValue(new FooConfiguration())); + + $container = new ContainerBuilder(new ParameterBag()); + $container->registerExtension($extension); + $container->prependExtensionConfig('foo', array('bar' => true)); + + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + + $this->assertContains(new FileResource(__FILE__), $container->getResources(), '', false, false); + } + + public function testOverriddenEnvsAreMerged() + { + $container = new ContainerBuilder(); + $container->registerExtension(new FooExtension()); + $container->prependExtensionConfig('foo', array('bar' => '%env(FOO)%')); + $container->prependExtensionConfig('foo', array('bar' => '%env(BAR)%', 'baz' => '%env(BAZ)%')); + + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + + $this->assertSame(array('BAZ', 'FOO'), array_keys($container->getParameterBag()->getEnvPlaceholders())); + $this->assertSame(array('BAZ' => 1, 'FOO' => 0), $container->getEnvCounters()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Using a cast in "env(int:FOO)" is incompatible with resolution at compile time in "Symfony\Component\DependencyInjection\Tests\Compiler\BarExtension". The logic in the extension should be moved to a compiler pass, or an env parameter with no cast should be used instead. + */ + public function testProcessedEnvsAreIncompatibleWithResolve() + { + $container = new ContainerBuilder(); + $container->registerExtension(new BarExtension()); + $container->prependExtensionConfig('bar', array()); + + (new MergeExtensionConfigurationPass())->process($container); + } + + public function testThrowingExtensionsGetMergedBag() + { + $container = new ContainerBuilder(); + $container->registerExtension(new ThrowingExtension()); + $container->prependExtensionConfig('throwing', array('bar' => '%env(FOO)%')); + + try { + $pass = new MergeExtensionConfigurationPass(); + $pass->process($container); + $this->fail('An exception should have been thrown.'); + } catch (\Exception $e) { + } + + $this->assertSame(array('FOO'), array_keys($container->getParameterBag()->getEnvPlaceholders())); + } +} + +class FooConfiguration implements ConfigurationInterface +{ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('foo'); + $rootNode + ->children() + ->scalarNode('bar')->end() + ->scalarNode('baz')->end() + ->end(); + + return $treeBuilder; + } +} + +class FooExtension extends Extension +{ + public function getAlias() + { + return 'foo'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new FooConfiguration(); + } + + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if (isset($config['baz'])) { + $container->getParameterBag()->get('env(BOZ)'); + $container->resolveEnvPlaceholders($config['baz']); + } + } +} + +class BarExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $container->resolveEnvPlaceholders('%env(int:FOO)%', true); + } +} + +class ThrowingExtension extends Extension +{ + public function getAlias() + { + return 'throwing'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new FooConfiguration(); + } + + public function load(array $configs, ContainerBuilder $container) + { + throw new \Exception(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.php b/vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.php new file mode 100644 index 0000000..7e9238f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/OptionalServiceClass.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\DependencyInjection\Tests\Compiler; + +use Symfony\Bug\NotExistClass; + +class OptionalServiceClass extends NotExistClass +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.php new file mode 100644 index 0000000..1fd772e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/PassConfigTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; + +/** + * @author Guilhem N + */ +class PassConfigTest extends TestCase +{ + public function testPassOrdering() + { + $config = new PassConfig(); + $config->setBeforeOptimizationPasses(array()); + + $pass1 = $this->getMockBuilder(CompilerPassInterface::class)->getMock(); + $config->addPass($pass1, PassConfig::TYPE_BEFORE_OPTIMIZATION, 10); + + $pass2 = $this->getMockBuilder(CompilerPassInterface::class)->getMock(); + $config->addPass($pass2, PassConfig::TYPE_BEFORE_OPTIMIZATION, 30); + + $passes = $config->getBeforeOptimizationPasses(); + $this->assertSame($pass2, $passes[0]); + $this->assertSame($pass1, $passes[1]); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.php new file mode 100644 index 0000000..61e3fa9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/PriorityTaggedServiceTraitTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class PriorityTaggedServiceTraitTest extends TestCase +{ + public function testThatCacheWarmersAreProcessedInPriorityOrder() + { + $services = array( + 'my_service1' => array('my_custom_tag' => array('priority' => 100)), + 'my_service2' => array('my_custom_tag' => array('priority' => 200)), + 'my_service3' => array('my_custom_tag' => array('priority' => -501)), + 'my_service4' => array('my_custom_tag' => array()), + 'my_service5' => array('my_custom_tag' => array('priority' => -1)), + 'my_service6' => array('my_custom_tag' => array('priority' => -500)), + 'my_service7' => array('my_custom_tag' => array('priority' => -499)), + 'my_service8' => array('my_custom_tag' => array('priority' => 1)), + 'my_service9' => array('my_custom_tag' => array('priority' => -2)), + 'my_service10' => array('my_custom_tag' => array('priority' => -1000)), + 'my_service11' => array('my_custom_tag' => array('priority' => -1001)), + 'my_service12' => array('my_custom_tag' => array('priority' => -1002)), + 'my_service13' => array('my_custom_tag' => array('priority' => -1003)), + 'my_service14' => array('my_custom_tag' => array('priority' => -1000)), + 'my_service15' => array('my_custom_tag' => array('priority' => 1)), + 'my_service16' => array('my_custom_tag' => array('priority' => -1)), + 'my_service17' => array('my_custom_tag' => array('priority' => 200)), + 'my_service18' => array('my_custom_tag' => array('priority' => 100)), + 'my_service19' => array('my_custom_tag' => array()), + ); + + $container = new ContainerBuilder(); + + foreach ($services as $id => $tags) { + $definition = $container->register($id); + + foreach ($tags as $name => $attributes) { + $definition->addTag($name, $attributes); + } + } + + $expected = array( + new Reference('my_service2'), + new Reference('my_service17'), + new Reference('my_service1'), + new Reference('my_service18'), + new Reference('my_service8'), + new Reference('my_service15'), + new Reference('my_service4'), + new Reference('my_service19'), + new Reference('my_service5'), + new Reference('my_service16'), + new Reference('my_service9'), + new Reference('my_service7'), + new Reference('my_service6'), + new Reference('my_service3'), + new Reference('my_service10'), + new Reference('my_service14'), + new Reference('my_service11'), + new Reference('my_service12'), + new Reference('my_service13'), + ); + + $priorityTaggedServiceTraitImplementation = new PriorityTaggedServiceTraitImplementation(); + + $this->assertEquals($expected, $priorityTaggedServiceTraitImplementation->test('my_custom_tag', $container)); + } +} + +class PriorityTaggedServiceTraitImplementation +{ + use PriorityTaggedServiceTrait; + + public function test($tagName, ContainerBuilder $container) + { + return $this->findAndSortTaggedServices($tagName, $container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.php new file mode 100644 index 0000000..cddb62d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/RegisterEnvVarProcessorsPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\RegisterEnvVarProcessorsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; + +class RegisterEnvVarProcessorsPassTest extends TestCase +{ + public function testSimpleProcessor() + { + $container = new ContainerBuilder(); + $container->register('foo', SimpleProcessor::class)->addTag('container.env_var_processor'); + + (new RegisterEnvVarProcessorsPass())->process($container); + + $this->assertTrue($container->has('container.env_var_processors_locator')); + $this->assertInstanceOf(SimpleProcessor::class, $container->get('container.env_var_processors_locator')->get('foo')); + + $expected = array( + 'foo' => array('string'), + 'base64' => array('string'), + 'bool' => array('bool'), + 'const' => array('bool', 'int', 'float', 'string', 'array'), + 'file' => array('string'), + 'float' => array('float'), + 'int' => array('int'), + 'json' => array('array'), + 'resolve' => array('string'), + 'string' => array('string'), + ); + + $this->assertSame($expected, $container->getParameterBag()->getProvidedTypes()); + } + + public function testNoProcessor() + { + $container = new ContainerBuilder(); + + (new RegisterEnvVarProcessorsPass())->process($container); + + $this->assertFalse($container->has('container.env_var_processors_locator')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid type "foo" returned by "Symfony\Component\DependencyInjection\Tests\Compiler\BadProcessor::getProvidedTypes()", expected one of "array", "bool", "float", "int", "string". + */ + public function testBadProcessor() + { + $container = new ContainerBuilder(); + $container->register('foo', BadProcessor::class)->addTag('container.env_var_processor'); + + (new RegisterEnvVarProcessorsPass())->process($container); + } +} + +class SimpleProcessor implements EnvVarProcessorInterface +{ + public function getEnv($prefix, $name, \Closure $getEnv) + { + return $getEnv($name); + } + + public static function getProvidedTypes() + { + return array('foo' => 'string'); + } +} + +class BadProcessor extends SimpleProcessor +{ + public static function getProvidedTypes() + { + return array('foo' => 'string|foo'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/RegisterServiceSubscribersPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/RegisterServiceSubscribersPassTest.php new file mode 100644 index 0000000..42f994e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/RegisterServiceSubscribersPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\RegisterServiceSubscribersPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveServiceSubscribersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; +use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber; +use Symfony\Component\DependencyInjection\TypedReference; + +require_once __DIR__.'/../Fixtures/includes/classes.php'; + +class RegisterServiceSubscribersPassTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Service "foo" must implement interface "Symfony\Component\DependencyInjection\ServiceSubscriberInterface". + */ + public function testInvalidClass() + { + $container = new ContainerBuilder(); + + $container->register('foo', CustomDefinition::class) + ->addTag('container.service_subscriber') + ; + + (new RegisterServiceSubscribersPass())->process($container); + (new ResolveServiceSubscribersPass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "container.service_subscriber" tag accepts only the "key" and "id" attributes, "bar" given for service "foo". + */ + public function testInvalidAttributes() + { + $container = new ContainerBuilder(); + + $container->register('foo', TestServiceSubscriber::class) + ->addTag('container.service_subscriber', array('bar' => '123')) + ; + + (new RegisterServiceSubscribersPass())->process($container); + (new ResolveServiceSubscribersPass())->process($container); + } + + public function testNoAttributes() + { + $container = new ContainerBuilder(); + + $container->register('foo', TestServiceSubscriber::class) + ->addArgument(new Reference(PsrContainerInterface::class)) + ->addTag('container.service_subscriber') + ; + + (new RegisterServiceSubscribersPass())->process($container); + (new ResolveServiceSubscribersPass())->process($container); + + $foo = $container->getDefinition('foo'); + $locator = $container->getDefinition((string) $foo->getArgument(0)); + + $this->assertFalse($locator->isPublic()); + $this->assertSame(ServiceLocator::class, $locator->getClass()); + + $expected = array( + TestServiceSubscriber::class => new ServiceClosureArgument(new TypedReference(TestServiceSubscriber::class, TestServiceSubscriber::class, TestServiceSubscriber::class)), + CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + 'bar' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class)), + 'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + ); + + $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); + } + + public function testWithAttributes() + { + $container = new ContainerBuilder(); + + $container->register('foo', TestServiceSubscriber::class) + ->setAutowired(true) + ->addArgument(new Reference(PsrContainerInterface::class)) + ->addTag('container.service_subscriber', array('key' => 'bar', 'id' => 'bar')) + ->addTag('container.service_subscriber', array('key' => 'bar', 'id' => 'baz')) // should be ignored: the first wins + ; + + (new RegisterServiceSubscribersPass())->process($container); + (new ResolveServiceSubscribersPass())->process($container); + + $foo = $container->getDefinition('foo'); + $locator = $container->getDefinition((string) $foo->getArgument(0)); + + $this->assertFalse($locator->isPublic()); + $this->assertSame(ServiceLocator::class, $locator->getClass()); + + $expected = array( + TestServiceSubscriber::class => new ServiceClosureArgument(new TypedReference(TestServiceSubscriber::class, TestServiceSubscriber::class, TestServiceSubscriber::class)), + CustomDefinition::class => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + 'bar' => new ServiceClosureArgument(new TypedReference('bar', CustomDefinition::class, TestServiceSubscriber::class)), + 'baz' => new ServiceClosureArgument(new TypedReference(CustomDefinition::class, CustomDefinition::class, TestServiceSubscriber::class, ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + ); + + $this->assertEquals($expected, $container->getDefinition((string) $locator->getFactory()[0])->getArgument(0)); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Service key "test" does not exist in the map returned by "Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber::getSubscribedServices()" for service "foo_service". + */ + public function testExtraServiceSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo_service', TestServiceSubscriber::class) + ->setAutowired(true) + ->addArgument(new Reference(PsrContainerInterface::class)) + ->addTag('container.service_subscriber', array( + 'key' => 'test', + 'id' => TestServiceSubscriber::class, + )) + ; + $container->register(TestServiceSubscriber::class, TestServiceSubscriber::class); + $container->compile(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.php new file mode 100644 index 0000000..fd26561 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/RemoveUnusedDefinitionsPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class RemoveUnusedDefinitionsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container + ->register('bar') + ->setPublic(false) + ; + $container + ->register('moo') + ->setArguments(array(new Reference('bar'))) + ; + + $this->process($container); + + $this->assertFalse($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + $this->assertTrue($container->hasDefinition('moo')); + } + + public function testProcessRemovesUnusedDefinitionsRecursively() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container + ->register('bar') + ->setArguments(array(new Reference('foo'))) + ->setPublic(false) + ; + + $this->process($container); + + $this->assertFalse($container->hasDefinition('foo')); + $this->assertFalse($container->hasDefinition('bar')); + } + + public function testProcessWorksWithInlinedDefinitions() + { + $container = new ContainerBuilder(); + $container + ->register('foo') + ->setPublic(false) + ; + $container + ->register('bar') + ->setArguments(array(new Definition(null, array(new Reference('foo'))))) + ; + + $this->process($container); + + $this->assertTrue($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + } + + public function testProcessWontRemovePrivateFactory() + { + $container = new ContainerBuilder(); + + $container + ->register('foo', 'stdClass') + ->setFactory(array('stdClass', 'getInstance')) + ->setPublic(false); + + $container + ->register('bar', 'stdClass') + ->setFactory(array(new Reference('foo'), 'getInstance')) + ->setPublic(false); + + $container + ->register('foobar') + ->addArgument(new Reference('bar')); + + $this->process($container); + + $this->assertTrue($container->hasDefinition('foo')); + $this->assertTrue($container->hasDefinition('bar')); + $this->assertTrue($container->hasDefinition('foobar')); + } + + public function testProcessConsiderEnvVariablesAsUsedEvenInPrivateServices() + { + $container = new ContainerBuilder(); + $container->setParameter('env(FOOBAR)', 'test'); + $container + ->register('foo') + ->setArguments(array('%env(FOOBAR)%')) + ->setPublic(false) + ; + + $resolvePass = new ResolveParameterPlaceHoldersPass(); + $resolvePass->process($container); + + $this->process($container); + + $this->assertFalse($container->hasDefinition('foo')); + + $envCounters = $container->getEnvCounters(); + $this->assertArrayHasKey('FOOBAR', $envCounters); + $this->assertSame(1, $envCounters['FOOBAR']); + } + + protected function process(ContainerBuilder $container) + { + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass())); + $repeatedPass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.php new file mode 100644 index 0000000..7574e79 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ReplaceAliasByActualDefinitionPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +require_once __DIR__.'/../Fixtures/includes/foo.php'; + +class ReplaceAliasByActualDefinitionPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $aDefinition = $container->register('a', '\stdClass'); + $aDefinition->setFactory(array(new Reference('b'), 'createA')); + + $bDefinition = new Definition('\stdClass'); + $bDefinition->setPublic(false); + $container->setDefinition('b', $bDefinition); + + $container->setAlias('a_alias', 'a'); + $container->setAlias('b_alias', 'b'); + + $container->setAlias('container', 'service_container'); + + $this->process($container); + + $this->assertTrue($container->has('a'), '->process() does nothing to public definitions.'); + $this->assertTrue($container->hasAlias('a_alias')); + $this->assertFalse($container->has('b'), '->process() removes non-public definitions.'); + $this->assertTrue( + $container->has('b_alias') && !$container->hasAlias('b_alias'), + '->process() replaces alias to actual.' + ); + + $this->assertTrue($container->has('container')); + + $resolvedFactory = $aDefinition->getFactory(); + $this->assertSame('b_alias', (string) $resolvedFactory[0]); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testProcessWithInvalidAlias() + { + $container = new ContainerBuilder(); + $container->setAlias('a_alias', 'a'); + $this->process($container); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ReplaceAliasByActualDefinitionPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveBindingsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveBindingsPassTest.php new file mode 100644 index 0000000..d59b95a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveBindingsPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredMethodsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists; +use Symfony\Component\DependencyInjection\TypedReference; + +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + +class ResolveBindingsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $bindings = array(CaseSensitiveClass::class => new BoundArgument(new Reference('foo'))); + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setArguments(array(1 => '123')); + $definition->addMethodCall('setSensitiveClass'); + $definition->setBindings($bindings); + + $container->register('foo', CaseSensitiveClass::class) + ->setBindings($bindings); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $this->assertEquals(array(new Reference('foo'), '123'), $definition->getArguments()); + $this->assertEquals(array(array('setSensitiveClass', array(new Reference('foo')))), $definition->getMethodCalls()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Unused binding "$quz" in service "Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy". + */ + public function testUnusedBinding() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setBindings(array('$quz' => '123')); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegexp Unused binding "$quz" in service [\s\S]+ Invalid service ".*\\ParentNotExists": class NotExists not found\. + */ + public function testMissingParent() + { + $container = new ContainerBuilder(); + + $definition = $container->register(ParentNotExists::class, ParentNotExists::class); + $definition->setBindings(array('$quz' => '123')); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + } + + public function testTypedReferenceSupport() + { + $container = new ContainerBuilder(); + + $bindings = array(CaseSensitiveClass::class => new BoundArgument(new Reference('foo'))); + + // Explicit service id + $definition1 = $container->register('def1', NamedArgumentsDummy::class); + $definition1->addArgument($typedRef = new TypedReference('bar', CaseSensitiveClass::class)); + $definition1->setBindings($bindings); + + $definition2 = $container->register('def2', NamedArgumentsDummy::class); + $definition2->addArgument(new TypedReference(CaseSensitiveClass::class, CaseSensitiveClass::class)); + $definition2->setBindings($bindings); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $this->assertEquals(array($typedRef), $container->getDefinition('def1')->getArguments()); + $this->assertEquals(array(new Reference('foo')), $container->getDefinition('def2')->getArguments()); + } + + public function testScalarSetter() + { + $container = new ContainerBuilder(); + + $definition = $container->autowire('foo', ScalarSetter::class); + $definition->setBindings(array('$defaultLocale' => 'fr')); + + (new AutowireRequiredMethodsPass())->process($container); + (new ResolveBindingsPass())->process($container); + + $this->assertEquals(array(array('setDefaultLocale', array('fr'))), $definition->getMethodCalls()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveChildDefinitionsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveChildDefinitionsPassTest.php new file mode 100644 index 0000000..cf011b4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveChildDefinitionsPassTest.php @@ -0,0 +1,399 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveChildDefinitionsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('parent', 'foo')->setArguments(array('moo', 'b'))->setProperty('foo', 'moo'); + $container->setDefinition('child', new ChildDefinition('parent')) + ->replaceArgument(0, 'a') + ->setProperty('foo', 'bar') + ->setClass('bar') + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertNotInstanceOf(ChildDefinition::class, $def); + $this->assertEquals('bar', $def->getClass()); + $this->assertEquals(array('a', 'b'), $def->getArguments()); + $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); + } + + public function testProcessAppendsMethodCallsAlways() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->addMethodCall('foo', array('bar')) + ; + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ->addMethodCall('bar', array('foo')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertEquals(array( + array('foo', array('bar')), + array('bar', array('foo')), + ), $def->getMethodCalls()); + } + + public function testProcessDoesNotCopyAbstract() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setAbstract(true) + ; + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertFalse($def->isAbstract()); + } + + public function testProcessDoesNotCopyShared() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setShared(false) + ; + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertTrue($def->isShared()); + } + + public function testProcessDoesNotCopyTags() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->addTag('foo') + ; + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertEquals(array(), $def->getTags()); + } + + public function testProcessDoesNotCopyDecoratedService() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ->setDecoratedService('foo') + ; + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertNull($def->getDecoratedService()); + } + + public function testProcessDoesNotDropShared() + { + $container = new ContainerBuilder(); + + $container + ->register('parent') + ; + + $container + ->setDefinition('child', new ChildDefinition('parent')) + ->setShared(false) + ; + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertFalse($def->isShared()); + } + + public function testProcessHandlesMultipleInheritance() + { + $container = new ContainerBuilder(); + + $container + ->register('parent', 'foo') + ->setArguments(array('foo', 'bar', 'c')) + ; + + $container + ->setDefinition('child2', new ChildDefinition('child1')) + ->replaceArgument(1, 'b') + ; + + $container + ->setDefinition('child1', new ChildDefinition('parent')) + ->replaceArgument(0, 'a') + ; + + $this->process($container); + + $def = $container->getDefinition('child2'); + $this->assertEquals(array('a', 'b', 'c'), $def->getArguments()); + $this->assertEquals('foo', $def->getClass()); + } + + public function testSetLazyOnServiceHasParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass'); + + $container->setDefinition('child1', new ChildDefinition('parent')) + ->setLazy(true) + ; + + $this->process($container); + + $this->assertTrue($container->getDefinition('child1')->isLazy()); + } + + public function testSetLazyOnServiceIsParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setLazy(true) + ; + + $container->setDefinition('child1', new ChildDefinition('parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('child1')->isLazy()); + } + + public function testSetAutowiredOnServiceHasParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setAutowired(true) + ; + + $container->setDefinition('child1', new ChildDefinition('parent')) + ->setAutowired(false) + ; + + $this->process($container); + + $this->assertFalse($container->getDefinition('child1')->isAutowired()); + } + + public function testSetAutowiredOnServiceIsParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setAutowired(true) + ; + + $container->setDefinition('child1', new ChildDefinition('parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('child1')->isAutowired()); + } + + public function testDeepDefinitionsResolving() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'parentClass'); + $container->register('sibling', 'siblingClass') + ->setConfigurator(new ChildDefinition('parent'), 'foo') + ->setFactory(array(new ChildDefinition('parent'), 'foo')) + ->addArgument(new ChildDefinition('parent')) + ->setProperty('prop', new ChildDefinition('parent')) + ->addMethodCall('meth', array(new ChildDefinition('parent'))) + ; + + $this->process($container); + + $configurator = $container->getDefinition('sibling')->getConfigurator(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($configurator)); + $this->assertSame('parentClass', $configurator->getClass()); + + $factory = $container->getDefinition('sibling')->getFactory(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($factory[0])); + $this->assertSame('parentClass', $factory[0]->getClass()); + + $argument = $container->getDefinition('sibling')->getArgument(0); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($argument)); + $this->assertSame('parentClass', $argument->getClass()); + + $properties = $container->getDefinition('sibling')->getProperties(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($properties['prop'])); + $this->assertSame('parentClass', $properties['prop']->getClass()); + + $methodCalls = $container->getDefinition('sibling')->getMethodCalls(); + $this->assertSame('Symfony\Component\DependencyInjection\Definition', \get_class($methodCalls[0][1][0])); + $this->assertSame('parentClass', $methodCalls[0][1][0]->getClass()); + } + + public function testSetDecoratedServiceOnServiceHasParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass'); + + $container->setDefinition('child1', new ChildDefinition('parent')) + ->setDecoratedService('foo', 'foo_inner', 5) + ; + + $this->process($container); + + $this->assertEquals(array('foo', 'foo_inner', 5), $container->getDefinition('child1')->getDecoratedService()); + } + + public function testDecoratedServiceCopiesDeprecatedStatusFromParent() + { + $container = new ContainerBuilder(); + $container->register('deprecated_parent') + ->setDeprecated(true) + ; + + $container->setDefinition('decorated_deprecated_parent', new ChildDefinition('deprecated_parent')); + + $this->process($container); + + $this->assertTrue($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); + } + + public function testDecoratedServiceCanOverwriteDeprecatedParentStatus() + { + $container = new ContainerBuilder(); + $container->register('deprecated_parent') + ->setDeprecated(true) + ; + + $container->setDefinition('decorated_deprecated_parent', new ChildDefinition('deprecated_parent')) + ->setDeprecated(false) + ; + + $this->process($container); + + $this->assertFalse($container->getDefinition('decorated_deprecated_parent')->isDeprecated()); + } + + public function testProcessResolvesAliases() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'ParentClass'); + $container->setAlias('parent_alias', 'parent'); + $container->setDefinition('child', new ChildDefinition('parent_alias')); + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertSame('ParentClass', $def->getClass()); + } + + public function testProcessSetsArguments() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'ParentClass')->setArguments(array(0)); + $container->setDefinition('child', (new ChildDefinition('parent'))->setArguments(array( + 1, + 'index_0' => 2, + 'foo' => 3, + ))); + + $this->process($container); + + $def = $container->getDefinition('child'); + $this->assertSame(array(2, 1, 'foo' => 3), $def->getArguments()); + } + + public function testBindings() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setBindings(array('a' => '1', 'b' => '2')) + ; + + $child = $container->setDefinition('child', new ChildDefinition('parent')) + ->setBindings(array('b' => 'B', 'c' => 'C')) + ; + + $this->process($container); + + $bindings = array(); + foreach ($container->getDefinition('child')->getBindings() as $k => $v) { + $bindings[$k] = $v->getValues()[0]; + } + $this->assertEquals(array('b' => 'B', 'c' => 'C', 'a' => '1'), $bindings); + } + + public function testSetAutoconfiguredOnServiceIsParent() + { + $container = new ContainerBuilder(); + + $container->register('parent', 'stdClass') + ->setAutoconfigured(true) + ; + + $container->setDefinition('child1', new ChildDefinition('parent')); + + $this->process($container); + + $this->assertFalse($container->getDefinition('child1')->isAutoconfigured()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveChildDefinitionsPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveClassPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveClassPassTest.php new file mode 100644 index 0000000..acbcf1d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveClassPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; + +class ResolveClassPassTest extends TestCase +{ + /** + * @dataProvider provideValidClassId + */ + public function testResolveClassFromId($serviceId) + { + $container = new ContainerBuilder(); + $def = $container->register($serviceId); + + (new ResolveClassPass())->process($container); + + $this->assertSame($serviceId, $def->getClass()); + } + + public function provideValidClassId() + { + yield array('Acme\UnknownClass'); + yield array(CaseSensitiveClass::class); + } + + /** + * @dataProvider provideInvalidClassId + */ + public function testWontResolveClassFromId($serviceId) + { + $container = new ContainerBuilder(); + $def = $container->register($serviceId); + + (new ResolveClassPass())->process($container); + + $this->assertNull($def->getClass()); + } + + public function provideInvalidClassId() + { + yield array(\stdClass::class); + yield array('bar'); + yield array('\DateTime'); + } + + public function testNonFqcnChildDefinition() + { + $container = new ContainerBuilder(); + $parent = $container->register('App\Foo', null); + $child = $container->setDefinition('App\Foo.child', new ChildDefinition('App\Foo')); + + (new ResolveClassPass())->process($container); + + $this->assertSame('App\Foo', $parent->getClass()); + $this->assertNull($child->getClass()); + } + + public function testClassFoundChildDefinition() + { + $container = new ContainerBuilder(); + $parent = $container->register('App\Foo', null); + $child = $container->setDefinition(self::class, new ChildDefinition('App\Foo')); + + (new ResolveClassPass())->process($container); + + $this->assertSame('App\Foo', $parent->getClass()); + $this->assertSame(self::class, $child->getClass()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Service definition "App\Foo\Child" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class. + */ + public function testAmbiguousChildDefinition() + { + $container = new ContainerBuilder(); + $parent = $container->register('App\Foo', null); + $child = $container->setDefinition('App\Foo\Child', new ChildDefinition('App\Foo')); + + (new ResolveClassPass())->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveFactoryClassPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveFactoryClassPassTest.php new file mode 100644 index 0000000..96453c3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveFactoryClassPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ResolveFactoryClassPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class ResolveFactoryClassPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $factory = $container->register('factory', 'Foo\Bar'); + $factory->setFactory(array(null, 'create')); + + $pass = new ResolveFactoryClassPass(); + $pass->process($container); + + $this->assertSame(array('Foo\Bar', 'create'), $factory->getFactory()); + } + + public function testInlinedDefinitionFactoryIsProcessed() + { + $container = new ContainerBuilder(); + + $factory = $container->register('factory'); + $factory->setFactory(array((new Definition('Baz\Qux'))->setFactory(array(null, 'getInstance')), 'create')); + + $pass = new ResolveFactoryClassPass(); + $pass->process($container); + + $this->assertSame(array('Baz\Qux', 'getInstance'), $factory->getFactory()[0]->getFactory()); + } + + public function provideFulfilledFactories() + { + return array( + array(array('Foo\Bar', 'create')), + array(array(new Reference('foo'), 'create')), + array(array(new Definition('Baz'), 'create')), + ); + } + + /** + * @dataProvider provideFulfilledFactories + */ + public function testIgnoresFulfilledFactories($factory) + { + $container = new ContainerBuilder(); + $definition = new Definition(); + $definition->setFactory($factory); + + $container->setDefinition('factory', $definition); + + $pass = new ResolveFactoryClassPass(); + $pass->process($container); + + $this->assertSame($factory, $container->getDefinition('factory')->getFactory()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The "factory" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class? + */ + public function testNotAnyClassThrowsException() + { + $container = new ContainerBuilder(); + + $factory = $container->register('factory'); + $factory->setFactory(array(null, 'create')); + + $pass = new ResolveFactoryClassPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveHotPathPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveHotPathPassTest.php new file mode 100644 index 0000000..d2ba590 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveHotPathPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\ResolveHotPathPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class ResolveHotPathPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $container->register('foo') + ->addTag('container.hot_path') + ->addArgument(new IteratorArgument(array(new Reference('lazy')))) + ->addArgument(new Reference('service_container')) + ->addArgument(new Definition('', array(new Reference('bar')))) + ->addArgument(new Reference('baz', ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE)) + ->addArgument(new Reference('missing')) + ; + + $container->register('lazy'); + $container->register('bar') + ->addArgument(new Reference('buz')) + ->addArgument(new Reference('deprec_ref_notag')); + $container->register('baz') + ->addArgument(new Reference('lazy')) + ->addArgument(new Reference('lazy')); + $container->register('buz'); + $container->register('deprec_with_tag')->setDeprecated()->addTag('container.hot_path'); + $container->register('deprec_ref_notag')->setDeprecated(); + + (new ResolveHotPathPass())->process($container); + + $this->assertFalse($container->getDefinition('lazy')->hasTag('container.hot_path')); + $this->assertTrue($container->getDefinition('bar')->hasTag('container.hot_path')); + $this->assertTrue($container->getDefinition('buz')->hasTag('container.hot_path')); + $this->assertFalse($container->getDefinition('baz')->hasTag('container.hot_path')); + $this->assertFalse($container->getDefinition('service_container')->hasTag('container.hot_path')); + $this->assertFalse($container->getDefinition('deprec_with_tag')->hasTag('container.hot_path')); + $this->assertFalse($container->getDefinition('deprec_ref_notag')->hasTag('container.hot_path')); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php new file mode 100644 index 0000000..ad6f79d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInstanceofConditionalsPassTest.php @@ -0,0 +1,268 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\BoundArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveInstanceofConditionalsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $def = $container->register('foo', self::class)->addTag('tag')->setAutowired(true)->setChanges(array()); + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition(''))->setProperty('foo', 'bar')->addTag('baz', array('attr' => 123)), + )); + + (new ResolveInstanceofConditionalsPass())->process($container); + + $parent = 'instanceof.'.parent::class.'.0.foo'; + $def = $container->getDefinition('foo'); + $this->assertEmpty($def->getInstanceofConditionals()); + $this->assertInstanceOf(ChildDefinition::class, $def); + $this->assertTrue($def->isAutowired()); + $this->assertSame($parent, $def->getParent()); + $this->assertSame(array('tag' => array(array()), 'baz' => array(array('attr' => 123))), $def->getTags()); + + $parent = $container->getDefinition($parent); + $this->assertSame(array('foo' => 'bar'), $parent->getProperties()); + $this->assertSame(array(), $parent->getTags()); + } + + public function testProcessInheritance() + { + $container = new ContainerBuilder(); + + $def = $container + ->register('parent', parent::class) + ->addMethodCall('foo', array('foo')); + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition(''))->addMethodCall('foo', array('bar')), + )); + + $def = (new ChildDefinition('parent'))->setClass(self::class); + $container->setDefinition('child', $def); + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveChildDefinitionsPass())->process($container); + + $expected = array( + array('foo', array('bar')), + array('foo', array('foo')), + ); + + $this->assertSame($expected, $container->getDefinition('parent')->getMethodCalls()); + $this->assertSame($expected, $container->getDefinition('child')->getMethodCalls()); + } + + public function testProcessDoesReplaceShared() + { + $container = new ContainerBuilder(); + + $def = $container->register('foo', 'stdClass'); + $def->setInstanceofConditionals(array( + 'stdClass' => (new ChildDefinition(''))->setShared(false), + )); + + (new ResolveInstanceofConditionalsPass())->process($container); + + $def = $container->getDefinition('foo'); + $this->assertFalse($def->isShared()); + } + + public function testProcessHandlesMultipleInheritance() + { + $container = new ContainerBuilder(); + + $def = $container->register('foo', self::class)->setShared(true); + + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition(''))->setLazy(true)->setShared(false), + self::class => (new ChildDefinition(''))->setAutowired(true), + )); + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveChildDefinitionsPass())->process($container); + + $def = $container->getDefinition('foo'); + $this->assertTrue($def->isAutowired()); + $this->assertTrue($def->isLazy()); + $this->assertTrue($def->isShared()); + } + + public function testProcessUsesAutoconfiguredInstanceof() + { + $container = new ContainerBuilder(); + $def = $container->register('normal_service', self::class); + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition('')) + ->addTag('local_instanceof_tag') + ->setFactory('locally_set_factory'), + )); + $def->setAutoconfigured(true); + $container->registerForAutoconfiguration(parent::class) + ->addTag('autoconfigured_tag') + ->setAutowired(true) + ->setFactory('autoconfigured_factory'); + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveChildDefinitionsPass())->process($container); + + $def = $container->getDefinition('normal_service'); + // autowired thanks to the autoconfigured instanceof + $this->assertTrue($def->isAutowired()); + // factory from the specific instanceof overrides global one + $this->assertEquals('locally_set_factory', $def->getFactory()); + // tags are merged, the locally set one is first + $this->assertSame(array('local_instanceof_tag' => array(array()), 'autoconfigured_tag' => array(array())), $def->getTags()); + } + + public function testAutoconfigureInstanceofDoesNotDuplicateTags() + { + $container = new ContainerBuilder(); + $def = $container->register('normal_service', self::class); + $def + ->addTag('duplicated_tag') + ->addTag('duplicated_tag', array('and_attributes' => 1)) + ; + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition(''))->addTag('duplicated_tag'), + )); + $def->setAutoconfigured(true); + $container->registerForAutoconfiguration(parent::class) + ->addTag('duplicated_tag', array('and_attributes' => 1)) + ; + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveChildDefinitionsPass())->process($container); + + $def = $container->getDefinition('normal_service'); + $this->assertSame(array('duplicated_tag' => array(array(), array('and_attributes' => 1))), $def->getTags()); + } + + public function testProcessDoesNotUseAutoconfiguredInstanceofIfNotEnabled() + { + $container = new ContainerBuilder(); + $def = $container->register('normal_service', self::class); + $def->setInstanceofConditionals(array( + parent::class => (new ChildDefinition('')) + ->addTag('foo_tag'), + )); + $container->registerForAutoconfiguration(parent::class) + ->setAutowired(true); + + (new ResolveInstanceofConditionalsPass())->process($container); + (new ResolveChildDefinitionsPass())->process($container); + + $def = $container->getDefinition('normal_service'); + $this->assertFalse($def->isAutowired()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage "App\FakeInterface" is set as an "instanceof" conditional, but it does not exist. + */ + public function testBadInterfaceThrowsException() + { + $container = new ContainerBuilder(); + $def = $container->register('normal_service', self::class); + $def->setInstanceofConditionals(array( + 'App\\FakeInterface' => (new ChildDefinition('')) + ->addTag('foo_tag'), + )); + + (new ResolveInstanceofConditionalsPass())->process($container); + } + + public function testBadInterfaceForAutomaticInstanceofIsOk() + { + $container = new ContainerBuilder(); + $container->register('normal_service', self::class) + ->setAutoconfigured(true); + $container->registerForAutoconfiguration('App\\FakeInterface') + ->setAutowired(true); + + (new ResolveInstanceofConditionalsPass())->process($container); + $this->assertTrue($container->hasDefinition('normal_service')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Autoconfigured instanceof for type "PHPUnit\Framework\TestCase" defines method calls but these are not supported and should be removed. + */ + public function testProcessThrowsExceptionForAutoconfiguredCalls() + { + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(parent::class) + ->addMethodCall('setFoo'); + + (new ResolveInstanceofConditionalsPass())->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Autoconfigured instanceof for type "PHPUnit\Framework\TestCase" defines arguments but these are not supported and should be removed. + */ + public function testProcessThrowsExceptionForArguments() + { + $container = new ContainerBuilder(); + $container->registerForAutoconfiguration(parent::class) + ->addArgument('bar'); + + (new ResolveInstanceofConditionalsPass())->process($container); + } + + public function testMergeReset() + { + $container = new ContainerBuilder(); + + $container + ->register('bar', self::class) + ->addArgument('a') + ->addMethodCall('setB') + ->setDecoratedService('foo') + ->addTag('t') + ->setInstanceofConditionals(array( + parent::class => (new ChildDefinition(''))->addTag('bar'), + )) + ; + + (new ResolveInstanceofConditionalsPass())->process($container); + + $abstract = $container->getDefinition('abstract.instanceof.bar'); + + $this->assertEmpty($abstract->getArguments()); + $this->assertEmpty($abstract->getMethodCalls()); + $this->assertNull($abstract->getDecoratedService()); + $this->assertEmpty($abstract->getTags()); + $this->assertTrue($abstract->isAbstract()); + } + + public function testBindings() + { + $container = new ContainerBuilder(); + $def = $container->register('foo', self::class)->setBindings(array('$toto' => 123)); + $def->setInstanceofConditionals(array(parent::class => new ChildDefinition(''))); + + (new ResolveInstanceofConditionalsPass())->process($container); + + $bindings = $container->getDefinition('foo')->getBindings(); + $this->assertSame(array('$toto'), array_keys($bindings)); + $this->assertInstanceOf(BoundArgument::class, $bindings['$toto']); + $this->assertSame(123, $bindings['$toto']->getValues()[0]); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php new file mode 100644 index 0000000..61bad48 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveInvalidReferencesPassTest.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\ResolveInvalidReferencesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; + +class ResolveInvalidReferencesPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->setArguments(array( + new Reference('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE), + new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + )) + ->addMethodCall('foo', array(new Reference('moo', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertSame(array(null, null), $arguments); + $this->assertCount(0, $def->getMethodCalls()); + } + + public function testProcessIgnoreInvalidArgumentInCollectionArgument() + { + $container = new ContainerBuilder(); + $container->register('baz'); + $def = $container + ->register('foo') + ->setArguments(array( + array( + new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + $baz = new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + new Reference('moo', ContainerInterface::NULL_ON_INVALID_REFERENCE), + ), + )) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertSame(array($baz, null), $arguments[0]); + } + + public function testProcessKeepMethodCallOnInvalidArgumentInCollectionArgument() + { + $container = new ContainerBuilder(); + $container->register('baz'); + $def = $container + ->register('foo') + ->addMethodCall('foo', array( + array( + new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + $baz = new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + new Reference('moo', ContainerInterface::NULL_ON_INVALID_REFERENCE), + ), + )) + ; + + $this->process($container); + + $calls = $def->getMethodCalls(); + $this->assertCount(1, $def->getMethodCalls()); + $this->assertSame(array($baz, null), $calls[0][1][0]); + } + + public function testProcessIgnoreNonExistentServices() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->setArguments(array(new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertEquals('bar', (string) $arguments[0]); + } + + public function testProcessRemovesPropertiesOnInvalid() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->setProperty('foo', new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)) + ; + + $this->process($container); + + $this->assertEquals(array(), $def->getProperties()); + } + + public function testProcessRemovesArgumentsOnInvalid() + { + $container = new ContainerBuilder(); + $def = $container + ->register('foo') + ->addArgument(array( + array( + new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE), + new ServiceClosureArgument(new Reference('baz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)), + ), + )) + ; + + $this->process($container); + + $this->assertSame(array(array(array())), $def->getArguments()); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveInvalidReferencesPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveNamedArgumentsPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveNamedArgumentsPassTest.php new file mode 100644 index 0000000..fe681b4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveNamedArgumentsPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ResolveNamedArgumentsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy; + +/** + * @author Kévin Dunglas + */ +class ResolveNamedArgumentsPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setArguments(array( + 2 => 'http://api.example.com', + '$apiKey' => '123', + 0 => new Reference('foo'), + )); + $definition->addMethodCall('setApiKey', array('$apiKey' => '123')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + + $this->assertEquals(array(0 => new Reference('foo'), 1 => '123', 2 => 'http://api.example.com'), $definition->getArguments()); + $this->assertEquals(array(array('setApiKey', array('123'))), $definition->getMethodCalls()); + } + + public function testWithFactory() + { + $container = new ContainerBuilder(); + + $container->register('factory', NoConstructor::class); + $definition = $container->register('foo', NoConstructor::class) + ->setFactory(array(new Reference('factory'), 'create')) + ->setArguments(array('$apiKey' => '123')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + + $this->assertSame(array(0 => '123'), $definition->getArguments()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testClassNull() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NamedArgumentsDummy::class); + $definition->setArguments(array('$apiKey' => '123')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testClassNotExist() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NotExist::class, NotExist::class); + $definition->setArguments(array('$apiKey' => '123')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + */ + public function testClassNoConstructor() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NoConstructor::class, NoConstructor::class); + $definition->setArguments(array('$apiKey' => '123')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testArgumentNotFound() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setArguments(array('$notFound' => '123')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + } + + public function testTypedArgument() + { + $container = new ContainerBuilder(); + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setArguments(array('$apiKey' => '123', CaseSensitiveClass::class => new Reference('foo'))); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + + $this->assertEquals(array(new Reference('foo'), '123'), $definition->getArguments()); + } + + public function testResolvesMultipleArgumentsOfTheSameType() + { + $container = new ContainerBuilder(); + + $definition = $container->register(SimilarArgumentsDummy::class, SimilarArgumentsDummy::class); + $definition->setArguments(array(CaseSensitiveClass::class => new Reference('foo'), '$token' => 'qwerty')); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + + $this->assertEquals(array(new Reference('foo'), 'qwerty', new Reference('foo')), $definition->getArguments()); + } + + public function testResolvePrioritizeNamedOverType() + { + $container = new ContainerBuilder(); + + $definition = $container->register(SimilarArgumentsDummy::class, SimilarArgumentsDummy::class); + $definition->setArguments(array(CaseSensitiveClass::class => new Reference('foo'), '$token' => 'qwerty', '$class1' => new Reference('bar'))); + + $pass = new ResolveNamedArgumentsPass(); + $pass->process($container); + + $this->assertEquals(array(new Reference('bar'), 'qwerty', new Reference('foo')), $definition->getArguments()); + } +} + +class NoConstructor +{ + public static function create($apiKey) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php new file mode 100644 index 0000000..a34e007 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveParameterPlaceHoldersPassTest.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ResolveParameterPlaceHoldersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolveParameterPlaceHoldersPassTest extends TestCase +{ + private $compilerPass; + private $container; + private $fooDefinition; + + protected function setUp() + { + $this->compilerPass = new ResolveParameterPlaceHoldersPass(); + $this->container = $this->createContainerBuilder(); + $this->compilerPass->process($this->container); + $this->fooDefinition = $this->container->getDefinition('foo'); + } + + public function testClassParametersShouldBeResolved() + { + $this->assertSame('Foo', $this->fooDefinition->getClass()); + } + + public function testFactoryParametersShouldBeResolved() + { + $this->assertSame(array('FooFactory', 'getFoo'), $this->fooDefinition->getFactory()); + } + + public function testArgumentParametersShouldBeResolved() + { + $this->assertSame(array('bar', array('bar' => 'baz')), $this->fooDefinition->getArguments()); + } + + public function testMethodCallParametersShouldBeResolved() + { + $this->assertSame(array(array('foobar', array('bar', array('bar' => 'baz')))), $this->fooDefinition->getMethodCalls()); + } + + public function testPropertyParametersShouldBeResolved() + { + $this->assertSame(array('bar' => 'baz'), $this->fooDefinition->getProperties()); + } + + public function testFileParametersShouldBeResolved() + { + $this->assertSame('foo.php', $this->fooDefinition->getFile()); + } + + public function testAliasParametersShouldBeResolved() + { + $this->assertSame('foo', $this->container->getAlias('bar')->__toString()); + } + + public function testBindingsShouldBeResolved() + { + list($boundValue) = $this->container->getDefinition('foo')->getBindings()['$baz']->getValues(); + + $this->assertSame($this->container->getParameterBag()->resolveValue('%env(BAZ)%'), $boundValue); + } + + private function createContainerBuilder() + { + $containerBuilder = new ContainerBuilder(); + + $containerBuilder->setParameter('foo.class', 'Foo'); + $containerBuilder->setParameter('foo.factory.class', 'FooFactory'); + $containerBuilder->setParameter('foo.arg1', 'bar'); + $containerBuilder->setParameter('foo.arg2', array('%foo.arg1%' => 'baz')); + $containerBuilder->setParameter('foo.method', 'foobar'); + $containerBuilder->setParameter('foo.property.name', 'bar'); + $containerBuilder->setParameter('foo.property.value', 'baz'); + $containerBuilder->setParameter('foo.file', 'foo.php'); + $containerBuilder->setParameter('alias.id', 'bar'); + + $fooDefinition = $containerBuilder->register('foo', '%foo.class%'); + $fooDefinition->setFactory(array('%foo.factory.class%', 'getFoo')); + $fooDefinition->setArguments(array('%foo.arg1%', array('%foo.arg1%' => 'baz'))); + $fooDefinition->addMethodCall('%foo.method%', array('%foo.arg1%', '%foo.arg2%')); + $fooDefinition->setProperty('%foo.property.name%', '%foo.property.value%'); + $fooDefinition->setFile('%foo.file%'); + $fooDefinition->setBindings(array('$baz' => '%env(BAZ)%')); + + $containerBuilder->setAlias('%alias.id%', 'foo'); + + return $containerBuilder; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolvePrivatesPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolvePrivatesPassTest.php new file mode 100644 index 0000000..89af24f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolvePrivatesPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\ResolvePrivatesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ResolvePrivatesPassTest extends TestCase +{ + public function testPrivateHasHigherPrecedenceThanPublic() + { + $container = new ContainerBuilder(); + + $container->register('foo', 'stdClass') + ->setPublic(true) + ->setPrivate(true) + ; + + $container->setAlias('bar', 'foo') + ->setPublic(false) + ->setPrivate(false) + ; + + (new ResolvePrivatesPass())->process($container); + + $this->assertFalse($container->getDefinition('foo')->isPublic()); + $this->assertFalse($container->getAlias('bar')->isPublic()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.php new file mode 100644 index 0000000..6fb6197 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveReferencesToAliasesPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Compiler\ResolveReferencesToAliasesPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class ResolveReferencesToAliasesPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->setAlias('bar', 'foo'); + $def = $container + ->register('moo') + ->setArguments(array(new Reference('bar'))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertEquals('foo', (string) $arguments[0]); + } + + public function testProcessRecursively() + { + $container = new ContainerBuilder(); + $container->setAlias('bar', 'foo'); + $container->setAlias('moo', 'bar'); + $def = $container + ->register('foobar') + ->setArguments(array(new Reference('moo'))) + ; + + $this->process($container); + + $arguments = $def->getArguments(); + $this->assertEquals('foo', (string) $arguments[0]); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testAliasCircularReference() + { + $container = new ContainerBuilder(); + $container->setAlias('bar', 'foo'); + $container->setAlias('foo', 'bar'); + $this->process($container); + } + + public function testResolveFactory() + { + $container = new ContainerBuilder(); + $container->register('factory', 'Factory'); + $container->setAlias('factory_alias', new Alias('factory')); + $foo = new Definition(); + $foo->setFactory(array(new Reference('factory_alias'), 'createFoo')); + $container->setDefinition('foo', $foo); + $bar = new Definition(); + $bar->setFactory(array('Factory', 'createFoo')); + $container->setDefinition('bar', $bar); + + $this->process($container); + + $resolvedFooFactory = $container->getDefinition('foo')->getFactory(); + $resolvedBarFactory = $container->getDefinition('bar')->getFactory(); + + $this->assertSame('factory', (string) $resolvedFooFactory[0]); + $this->assertSame('Factory', (string) $resolvedBarFactory[0]); + } + + protected function process(ContainerBuilder $container) + { + $pass = new ResolveReferencesToAliasesPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.php b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.php new file mode 100644 index 0000000..a219f86 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Compiler/ResolveTaggedIteratorArgumentPassTest.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\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Roland Franssen + */ +class ResolveTaggedIteratorArgumentPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->register('a', 'stdClass')->addTag('foo'); + $container->register('b', 'stdClass')->addTag('foo', array('priority' => 20)); + $container->register('c', 'stdClass')->addTag('foo', array('priority' => 10)); + $container->register('d', 'stdClass')->setProperty('foos', new TaggedIteratorArgument('foo')); + + (new ResolveTaggedIteratorArgumentPass())->process($container); + + $properties = $container->getDefinition('d')->getProperties(); + $expected = new TaggedIteratorArgument('foo'); + $expected->setValues(array(new Reference('b'), new Reference('c'), new Reference('a'))); + $this->assertEquals($expected, $properties['foos']); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceCheckerTest.php b/vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceCheckerTest.php new file mode 100644 index 0000000..a91934b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceCheckerTest.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\DependencyInjection\Tests\Config; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\ResourceCheckerInterface; +use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; +use Symfony\Component\DependencyInjection\Config\ContainerParametersResourceChecker; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class ContainerParametersResourceCheckerTest extends TestCase +{ + /** @var ContainerParametersResource */ + private $resource; + + /** @var ResourceCheckerInterface */ + private $resourceChecker; + + /** @var ContainerInterface */ + private $container; + + protected function setUp() + { + $this->resource = new ContainerParametersResource(array('locales' => array('fr', 'en'), 'default_locale' => 'fr')); + $this->container = $this->getMockBuilder(ContainerInterface::class)->getMock(); + $this->resourceChecker = new ContainerParametersResourceChecker($this->container); + } + + public function testSupports() + { + $this->assertTrue($this->resourceChecker->supports($this->resource)); + } + + /** + * @dataProvider isFreshProvider + */ + public function testIsFresh(callable $mockContainer, $expected) + { + $mockContainer($this->container); + + $this->assertSame($expected, $this->resourceChecker->isFresh($this->resource, time())); + } + + public function isFreshProvider() + { + yield 'not fresh on missing parameter' => array(function (\PHPUnit_Framework_MockObject_MockObject $container) { + $container->method('hasParameter')->with('locales')->willReturn(false); + }, false); + + yield 'not fresh on different value' => array(function (\PHPUnit_Framework_MockObject_MockObject $container) { + $container->method('getParameter')->with('locales')->willReturn(array('nl', 'es')); + }, false); + + yield 'fresh on every identical parameters' => array(function (\PHPUnit_Framework_MockObject_MockObject $container) { + $container->expects($this->exactly(2))->method('hasParameter')->willReturn(true); + $container->expects($this->exactly(2))->method('getParameter') + ->withConsecutive( + array($this->equalTo('locales')), + array($this->equalTo('default_locale')) + ) + ->will($this->returnValueMap(array( + array('locales', array('fr', 'en')), + array('default_locale', 'fr'), + ))) + ; + }, true); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceTest.php b/vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceTest.php new file mode 100644 index 0000000..4da4766 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Config/ContainerParametersResourceTest.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\DependencyInjection\Tests\Config; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; + +class ContainerParametersResourceTest extends TestCase +{ + /** @var ContainerParametersResource */ + private $resource; + + protected function setUp() + { + $this->resource = new ContainerParametersResource(array('locales' => array('fr', 'en'), 'default_locale' => 'fr')); + } + + public function testToString() + { + $this->assertSame('container_parameters_9893d3133814ab03cac3490f36dece77', (string) $this->resource); + } + + public function testSerializeUnserialize() + { + $unserialized = unserialize(serialize($this->resource)); + + $this->assertEquals($this->resource, $unserialized); + } + + public function testGetParameters() + { + $this->assertSame(array('locales' => array('fr', 'en'), 'default_locale' => 'fr'), $this->resource->getParameters()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php b/vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php new file mode 100644 index 0000000..ccc5c6c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ContainerBuilderTest.php @@ -0,0 +1,1473 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +require_once __DIR__.'/Fixtures/includes/classes.php'; +require_once __DIR__.'/Fixtures/includes/ProjectExtension.php'; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\Config\Resource\ComposerResource; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; +use Symfony\Component\DependencyInjection\Tests\Fixtures\SimilarArgumentsDummy; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\ExpressionLanguage\Expression; + +class ContainerBuilderTest extends TestCase +{ + public function testDefaultRegisteredDefinitions() + { + $builder = new ContainerBuilder(); + + $this->assertCount(1, $builder->getDefinitions()); + $this->assertTrue($builder->hasDefinition('service_container')); + + $definition = $builder->getDefinition('service_container'); + $this->assertInstanceOf(Definition::class, $definition); + $this->assertTrue($definition->isSynthetic()); + $this->assertSame(ContainerInterface::class, $definition->getClass()); + $this->assertTrue($builder->hasAlias(PsrContainerInterface::class)); + $this->assertTrue($builder->hasAlias(ContainerInterface::class)); + } + + public function testDefinitions() + { + $builder = new ContainerBuilder(); + $definitions = array( + 'foo' => new Definition('Bar\FooClass'), + 'bar' => new Definition('BarClass'), + ); + $builder->setDefinitions($definitions); + $this->assertEquals($definitions, $builder->getDefinitions(), '->setDefinitions() sets the service definitions'); + $this->assertTrue($builder->hasDefinition('foo'), '->hasDefinition() returns true if a service definition exists'); + $this->assertFalse($builder->hasDefinition('foobar'), '->hasDefinition() returns false if a service definition does not exist'); + + $builder->setDefinition('foobar', $foo = new Definition('FooBarClass')); + $this->assertEquals($foo, $builder->getDefinition('foobar'), '->getDefinition() returns a service definition if defined'); + $this->assertSame($builder->setDefinition('foobar', $foo = new Definition('FooBarClass')), $foo, '->setDefinition() implements a fluid interface by returning the service reference'); + + $builder->addDefinitions($defs = array('foobar' => new Definition('FooBarClass'))); + $this->assertEquals(array_merge($definitions, $defs), $builder->getDefinitions(), '->addDefinitions() adds the service definitions'); + + try { + $builder->getDefinition('baz'); + $this->fail('->getDefinition() throws a ServiceNotFoundException if the service definition does not exist'); + } catch (ServiceNotFoundException $e) { + $this->assertEquals('You have requested a non-existent service "baz".', $e->getMessage(), '->getDefinition() throws a ServiceNotFoundException if the service definition does not exist'); + } + } + + /** + * @group legacy + * @expectedDeprecation The "deprecated_foo" service is deprecated. You should stop using it, as it will soon be removed. + */ + public function testCreateDeprecatedService() + { + $definition = new Definition('stdClass'); + $definition->setDeprecated(true); + + $builder = new ContainerBuilder(); + $builder->setDefinition('deprecated_foo', $definition); + $builder->get('deprecated_foo'); + } + + public function testRegister() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass'); + $this->assertTrue($builder->hasDefinition('foo'), '->register() registers a new service definition'); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Definition', $builder->getDefinition('foo'), '->register() returns the newly created Definition instance'); + } + + public function testAutowire() + { + $builder = new ContainerBuilder(); + $builder->autowire('foo', 'Bar\FooClass'); + + $this->assertTrue($builder->hasDefinition('foo'), '->autowire() registers a new service definition'); + $this->assertTrue($builder->getDefinition('foo')->isAutowired(), '->autowire() creates autowired definitions'); + } + + public function testHas() + { + $builder = new ContainerBuilder(); + $this->assertFalse($builder->has('foo'), '->has() returns false if the service does not exist'); + $builder->register('foo', 'Bar\FooClass'); + $this->assertTrue($builder->has('foo'), '->has() returns true if a service definition exists'); + $builder->set('bar', new \stdClass()); + $this->assertTrue($builder->has('bar'), '->has() returns true if a service exists'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @expectedExceptionMessage You have requested a non-existent service "foo". + */ + public function testGetThrowsExceptionIfServiceDoesNotExist() + { + $builder = new ContainerBuilder(); + $builder->get('foo'); + } + + public function testGetReturnsNullIfServiceDoesNotExistAndInvalidReferenceIsUsed() + { + $builder = new ContainerBuilder(); + + $this->assertNull($builder->get('foo', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service does not exist and NULL_ON_INVALID_REFERENCE is passed as a second argument'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + */ + public function testGetThrowsCircularReferenceExceptionIfServiceHasReferenceToItself() + { + $builder = new ContainerBuilder(); + $builder->register('baz', 'stdClass')->setArguments(array(new Reference('baz'))); + $builder->get('baz'); + } + + public function testGetReturnsSameInstanceWhenServiceIsShared() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + + $this->assertTrue($builder->get('bar') === $builder->get('bar'), '->get() always returns the same instance if the service is shared'); + } + + public function testGetCreatesServiceBasedOnDefinition() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass'); + + $this->assertInternalType('object', $builder->get('foo'), '->get() returns the service definition associated with the id'); + } + + public function testGetReturnsRegisteredService() + { + $builder = new ContainerBuilder(); + $builder->set('bar', $bar = new \stdClass()); + + $this->assertSame($bar, $builder->get('bar'), '->get() returns the service associated with the id'); + } + + public function testRegisterDoesNotOverrideExistingService() + { + $builder = new ContainerBuilder(); + $builder->set('bar', $bar = new \stdClass()); + $builder->register('bar', 'stdClass'); + + $this->assertSame($bar, $builder->get('bar'), '->get() returns the service associated with the id even if a definition has been defined'); + } + + public function testNonSharedServicesReturnsDifferentInstances() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass')->setShared(false); + + $this->assertNotSame($builder->get('bar'), $builder->get('bar')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage You have requested a synthetic service ("foo"). The DIC does not know how to construct this service. + */ + public function testGetUnsetLoadingServiceWhenCreateServiceThrowsAnException() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass')->setSynthetic(true); + + // we expect a RuntimeException here as foo is synthetic + try { + $builder->get('foo'); + } catch (RuntimeException $e) { + } + + // we must also have the same RuntimeException here + $builder->get('foo'); + } + + public function testGetServiceIds() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass'); + $builder->bar = $bar = new \stdClass(); + $builder->register('bar', 'stdClass'); + $this->assertEquals( + array( + 'service_container', + 'foo', + 'bar', + 'Psr\Container\ContainerInterface', + 'Symfony\Component\DependencyInjection\ContainerInterface', + ), + $builder->getServiceIds(), + '->getServiceIds() returns all defined service ids' + ); + } + + public function testAliases() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'stdClass'); + $builder->setAlias('bar', 'foo'); + $this->assertTrue($builder->hasAlias('bar'), '->hasAlias() returns true if the alias exists'); + $this->assertFalse($builder->hasAlias('foobar'), '->hasAlias() returns false if the alias does not exist'); + $this->assertEquals('foo', (string) $builder->getAlias('bar'), '->getAlias() returns the aliased service'); + $this->assertTrue($builder->has('bar'), '->setAlias() defines a new service'); + $this->assertSame($builder->get('bar'), $builder->get('foo'), '->setAlias() creates a service that is an alias to another one'); + + try { + $builder->setAlias('foobar', 'foobar'); + $this->fail('->setAlias() throws an InvalidArgumentException if the alias references itself'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('An alias can not reference itself, got a circular reference on "foobar".', $e->getMessage(), '->setAlias() throws an InvalidArgumentException if the alias references itself'); + } + + try { + $builder->getAlias('foobar'); + $this->fail('->getAlias() throws an InvalidArgumentException if the alias does not exist'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('The service alias "foobar" does not exist.', $e->getMessage(), '->getAlias() throws an InvalidArgumentException if the alias does not exist'); + } + } + + public function testGetAliases() + { + $builder = new ContainerBuilder(); + $builder->setAlias('bar', 'foo'); + $builder->setAlias('foobar', 'foo'); + $builder->setAlias('moo', new Alias('foo', false)); + + $aliases = $builder->getAliases(); + $this->assertEquals('foo', (string) $aliases['bar']); + $this->assertTrue($aliases['bar']->isPublic()); + $this->assertEquals('foo', (string) $aliases['foobar']); + $this->assertEquals('foo', (string) $aliases['moo']); + $this->assertFalse($aliases['moo']->isPublic()); + + $builder->register('bar', 'stdClass'); + $this->assertFalse($builder->hasAlias('bar')); + + $builder->set('foobar', 'stdClass'); + $builder->set('moo', 'stdClass'); + $this->assertCount(2, $builder->getAliases(), '->getAliases() does not return aliased services that have been overridden'); + } + + public function testSetAliases() + { + $builder = new ContainerBuilder(); + $builder->setAliases(array('bar' => 'foo', 'foobar' => 'foo')); + + $aliases = $builder->getAliases(); + $this->assertArrayHasKey('bar', $aliases); + $this->assertArrayHasKey('foobar', $aliases); + } + + public function testAddAliases() + { + $builder = new ContainerBuilder(); + $builder->setAliases(array('bar' => 'foo')); + $builder->addAliases(array('foobar' => 'foo')); + + $aliases = $builder->getAliases(); + $this->assertArrayHasKey('bar', $aliases); + $this->assertArrayHasKey('foobar', $aliases); + } + + public function testSetReplacesAlias() + { + $builder = new ContainerBuilder(); + $builder->setAlias('alias', 'aliased'); + $builder->set('aliased', new \stdClass()); + + $builder->set('alias', $foo = new \stdClass()); + $this->assertSame($foo, $builder->get('alias'), '->set() replaces an existing alias'); + } + + public function testAliasesKeepInvalidBehavior() + { + $builder = new ContainerBuilder(); + + $aliased = new Definition('stdClass'); + $aliased->addMethodCall('setBar', array(new Reference('bar', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $builder->setDefinition('aliased', $aliased); + $builder->setAlias('alias', 'aliased'); + + $this->assertEquals(new \stdClass(), $builder->get('alias')); + } + + public function testAddGetCompilerPass() + { + $builder = new ContainerBuilder(); + $builder->setResourceTracking(false); + $defaultPasses = $builder->getCompiler()->getPassConfig()->getPasses(); + $builder->addCompilerPass($pass1 = $this->getMockBuilder('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface')->getMock(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -5); + $builder->addCompilerPass($pass2 = $this->getMockBuilder('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface')->getMock(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 10); + + $passes = $builder->getCompiler()->getPassConfig()->getPasses(); + $this->assertCount(\count($passes) - 2, $defaultPasses); + // Pass 1 is executed later + $this->assertTrue(array_search($pass1, $passes, true) > array_search($pass2, $passes, true)); + } + + public function testCreateService() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $builder->register('foo2', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/%file%.php'); + $builder->setParameter('file', 'foo'); + $this->assertInstanceOf('\Bar\FooClass', $builder->get('foo1'), '->createService() requires the file defined by the service definition'); + $this->assertInstanceOf('\Bar\FooClass', $builder->get('foo2'), '->createService() replaces parameters in the file provided by the service definition'); + } + + public function testCreateProxyWithRealServiceInstantiator() + { + $builder = new ContainerBuilder(); + + $builder->register('foo1', 'Bar\FooClass')->setFile(__DIR__.'/Fixtures/includes/foo.php'); + $builder->getDefinition('foo1')->setLazy(true); + + $foo1 = $builder->get('foo1'); + + $this->assertSame($foo1, $builder->get('foo1'), 'The same proxy is retrieved on multiple subsequent calls'); + $this->assertSame('Bar\FooClass', \get_class($foo1)); + } + + public function testCreateServiceClass() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', '%class%'); + $builder->setParameter('class', 'stdClass'); + $this->assertInstanceOf('\stdClass', $builder->get('foo1'), '->createService() replaces parameters in the class provided by the service definition'); + } + + public function testCreateServiceArguments() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->addArgument(array('foo' => '%value%', '%value%' => 'foo', new Reference('bar'), '%%unescape_it%%')); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('foo' => 'bar', 'bar' => 'foo', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->arguments, '->createService() replaces parameters and service references in the arguments provided by the service definition'); + } + + public function testCreateServiceFactory() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass')->setFactory('Bar\FooClass::getInstance'); + $builder->register('qux', 'Bar\FooClass')->setFactory(array('Bar\FooClass', 'getInstance')); + $builder->register('bar', 'Bar\FooClass')->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')); + $builder->register('baz', 'Bar\FooClass')->setFactory(array(new Reference('bar'), 'getInstance')); + + $this->assertTrue($builder->get('foo')->called, '->createService() calls the factory method to create the service instance'); + $this->assertTrue($builder->get('qux')->called, '->createService() calls the factory method to create the service instance'); + $this->assertTrue($builder->get('bar')->called, '->createService() uses anonymous service as factory'); + $this->assertTrue($builder->get('baz')->called, '->createService() uses another service as factory'); + } + + public function testCreateServiceMethodCalls() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%value%', new Reference('bar')))); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('bar', $builder->get('bar')), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); + } + + public function testCreateServiceMethodCallsWithEscapedParam() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->addMethodCall('setBar', array(array('%%unescape_it%%'))); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the method calls arguments'); + } + + public function testCreateServiceProperties() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder->register('foo1', 'Bar\FooClass')->setProperty('bar', array('%value%', new Reference('bar'), '%%unescape_it%%')); + $builder->setParameter('value', 'bar'); + $this->assertEquals(array('bar', $builder->get('bar'), '%unescape_it%'), $builder->get('foo1')->bar, '->createService() replaces the values in the properties'); + } + + public function testCreateServiceConfigurator() + { + $builder = new ContainerBuilder(); + $builder->register('foo1', 'Bar\FooClass')->setConfigurator('sc_configure'); + $builder->register('foo2', 'Bar\FooClass')->setConfigurator(array('%class%', 'configureStatic')); + $builder->setParameter('class', 'BazClass'); + $builder->register('baz', 'BazClass'); + $builder->register('foo3', 'Bar\FooClass')->setConfigurator(array(new Reference('baz'), 'configure')); + $builder->register('foo4', 'Bar\FooClass')->setConfigurator(array($builder->getDefinition('baz'), 'configure')); + $builder->register('foo5', 'Bar\FooClass')->setConfigurator('foo'); + + $this->assertTrue($builder->get('foo1')->configured, '->createService() calls the configurator'); + $this->assertTrue($builder->get('foo2')->configured, '->createService() calls the configurator'); + $this->assertTrue($builder->get('foo3')->configured, '->createService() calls the configurator'); + $this->assertTrue($builder->get('foo4')->configured, '->createService() calls the configurator'); + + try { + $builder->get('foo5'); + $this->fail('->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); + } catch (\InvalidArgumentException $e) { + $this->assertEquals('The configure callable for class "Bar\FooClass" is not a callable.', $e->getMessage(), '->createService() throws an InvalidArgumentException if the configure callable is not a valid callable'); + } + } + + public function testCreateServiceWithIteratorArgument() + { + $builder = new ContainerBuilder(); + $builder->register('bar', 'stdClass'); + $builder + ->register('lazy_context', 'LazyContext') + ->setArguments(array( + new IteratorArgument(array('k1' => new Reference('bar'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))), + new IteratorArgument(array()), + )) + ; + + $lazyContext = $builder->get('lazy_context'); + $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues); + $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyEmptyValues); + $this->assertCount(1, $lazyContext->lazyValues); + $this->assertCount(0, $lazyContext->lazyEmptyValues); + + $i = 0; + foreach ($lazyContext->lazyValues as $k => $v) { + ++$i; + $this->assertEquals('k1', $k); + $this->assertInstanceOf('\stdClass', $v); + } + + // The second argument should have been ignored. + $this->assertEquals(1, $i); + + $i = 0; + foreach ($lazyContext->lazyEmptyValues as $k => $v) { + ++$i; + } + + $this->assertEquals(0, $i); + } + + /** + * @expectedException \RuntimeException + */ + public function testCreateSyntheticService() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass')->setSynthetic(true); + $builder->get('foo'); + } + + public function testCreateServiceWithExpression() + { + $builder = new ContainerBuilder(); + $builder->setParameter('bar', 'bar'); + $builder->register('bar', 'BarClass'); + $builder->register('foo', 'Bar\FooClass')->addArgument(array('foo' => new Expression('service("bar").foo ~ parameter("bar")'))); + $this->assertEquals('foobar', $builder->get('foo')->arguments['foo']); + } + + public function testResolveServices() + { + $builder = new ContainerBuilder(); + $builder->register('foo', 'Bar\FooClass'); + $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Reference('foo')), '->resolveServices() resolves service references to service instances'); + $this->assertEquals(array('foo' => array('foo', $builder->get('foo'))), $builder->resolveServices(array('foo' => array('foo', new Reference('foo')))), '->resolveServices() resolves service references to service instances in nested arrays'); + $this->assertEquals($builder->get('foo'), $builder->resolveServices(new Expression('service("foo")')), '->resolveServices() resolves expressions'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Constructing service "foo" from a parent definition is not supported at build time. + */ + public function testResolveServicesWithDecoratedDefinition() + { + $builder = new ContainerBuilder(); + $builder->setDefinition('grandpa', new Definition('stdClass')); + $builder->setDefinition('parent', new ChildDefinition('grandpa')); + $builder->setDefinition('foo', new ChildDefinition('parent')); + + $builder->get('foo'); + } + + public function testResolveServicesWithCustomDefinitionClass() + { + $builder = new ContainerBuilder(); + $builder->setDefinition('foo', new CustomDefinition('stdClass')); + + $this->assertInstanceOf('stdClass', $builder->get('foo')); + } + + public function testMerge() + { + $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); + $container->setResourceTracking(false); + $config = new ContainerBuilder(new ParameterBag(array('foo' => 'bar'))); + $container->merge($config); + $this->assertEquals(array('bar' => 'foo', 'foo' => 'bar'), $container->getParameterBag()->all(), '->merge() merges current parameters with the loaded ones'); + + $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); + $container->setResourceTracking(false); + $config = new ContainerBuilder(new ParameterBag(array('foo' => '%bar%'))); + $container->merge($config); + $container->compile(); + $this->assertEquals(array('bar' => 'foo', 'foo' => 'foo'), $container->getParameterBag()->all(), '->merge() evaluates the values of the parameters towards already defined ones'); + + $container = new ContainerBuilder(new ParameterBag(array('bar' => 'foo'))); + $container->setResourceTracking(false); + $config = new ContainerBuilder(new ParameterBag(array('foo' => '%bar%', 'baz' => '%foo%'))); + $container->merge($config); + $container->compile(); + $this->assertEquals(array('bar' => 'foo', 'foo' => 'foo', 'baz' => 'foo'), $container->getParameterBag()->all(), '->merge() evaluates the values of the parameters towards already defined ones'); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->register('foo', 'Bar\FooClass'); + $container->register('bar', 'BarClass'); + $config = new ContainerBuilder(); + $config->setDefinition('baz', new Definition('BazClass')); + $config->setAlias('alias_for_foo', 'foo'); + $container->merge($config); + $this->assertEquals(array('service_container', 'foo', 'bar', 'baz'), array_keys($container->getDefinitions()), '->merge() merges definitions already defined ones'); + + $aliases = $container->getAliases(); + $this->assertArrayHasKey('alias_for_foo', $aliases); + $this->assertEquals('foo', (string) $aliases['alias_for_foo']); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->register('foo', 'Bar\FooClass'); + $config->setDefinition('foo', new Definition('BazClass')); + $container->merge($config); + $this->assertEquals('BazClass', $container->getDefinition('foo')->getClass(), '->merge() overrides already defined services'); + + $container = new ContainerBuilder(); + $bag = new EnvPlaceholderParameterBag(); + $bag->get('env(Foo)'); + $config = new ContainerBuilder($bag); + $this->assertSame(array('%env(Bar)%'), $config->resolveEnvPlaceholders(array($bag->get('env(Bar)')))); + $container->merge($config); + $this->assertEquals(array('Foo' => 0, 'Bar' => 1), $container->getEnvCounters()); + + $container = new ContainerBuilder(); + $config = new ContainerBuilder(); + $childDefA = $container->registerForAutoconfiguration('AInterface'); + $childDefB = $config->registerForAutoconfiguration('BInterface'); + $container->merge($config); + $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutoconfiguredInstanceof()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage "AInterface" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface. + */ + public function testMergeThrowsExceptionForDuplicateAutomaticInstanceofDefinitions() + { + $container = new ContainerBuilder(); + $config = new ContainerBuilder(); + $container->registerForAutoconfiguration('AInterface'); + $config->registerForAutoconfiguration('AInterface'); + $container->merge($config); + } + + public function testResolveEnvValues() + { + $_ENV['DUMMY_ENV_VAR'] = 'du%%y'; + $_SERVER['DUMMY_SERVER_VAR'] = 'ABC'; + $_SERVER['HTTP_DUMMY_VAR'] = 'DEF'; + + $container = new ContainerBuilder(); + $container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)% %env(DUMMY_SERVER_VAR)% %env(HTTP_DUMMY_VAR)%'); + $container->setParameter('env(HTTP_DUMMY_VAR)', '123'); + + $this->assertSame('%% du%%%%y ABC 123', $container->resolveEnvPlaceholders('%bar%', true)); + + unset($_ENV['DUMMY_ENV_VAR'], $_SERVER['DUMMY_SERVER_VAR'], $_SERVER['HTTP_DUMMY_VAR']); + } + + public function testResolveEnvValuesWithArray() + { + $_ENV['ANOTHER_DUMMY_ENV_VAR'] = 'dummy'; + + $dummyArray = array('1' => 'one', '2' => 'two'); + + $container = new ContainerBuilder(); + $container->setParameter('dummy', '%env(ANOTHER_DUMMY_ENV_VAR)%'); + $container->setParameter('dummy2', $dummyArray); + + $container->resolveEnvPlaceholders('%dummy%', true); + $container->resolveEnvPlaceholders('%dummy2%', true); + + $this->assertInternalType('array', $container->resolveEnvPlaceholders('%dummy2%', true)); + + foreach ($dummyArray as $key => $value) { + $this->assertArrayHasKey($key, $container->resolveEnvPlaceholders('%dummy2%', true)); + } + + unset($_ENV['ANOTHER_DUMMY_ENV_VAR']); + } + + public function testCompileWithResolveEnv() + { + putenv('DUMMY_ENV_VAR=du%%y'); + $_SERVER['DUMMY_SERVER_VAR'] = 'ABC'; + $_SERVER['HTTP_DUMMY_VAR'] = 'DEF'; + + $container = new ContainerBuilder(); + $container->setParameter('env(FOO)', 'Foo'); + $container->setParameter('env(DUMMY_ENV_VAR)', 'GHI'); + $container->setParameter('bar', '%% %env(DUMMY_ENV_VAR)% %env(DUMMY_SERVER_VAR)% %env(HTTP_DUMMY_VAR)%'); + $container->setParameter('foo', '%env(FOO)%'); + $container->setParameter('baz', '%foo%'); + $container->setParameter('env(HTTP_DUMMY_VAR)', '123'); + $container->register('teatime', 'stdClass') + ->setProperty('foo', '%env(DUMMY_ENV_VAR)%') + ->setPublic(true) + ; + $container->compile(true); + + $this->assertSame('% du%%y ABC 123', $container->getParameter('bar')); + $this->assertSame('Foo', $container->getParameter('baz')); + $this->assertSame('du%%y', $container->get('teatime')->foo); + + unset($_SERVER['DUMMY_SERVER_VAR'], $_SERVER['HTTP_DUMMY_VAR']); + putenv('DUMMY_ENV_VAR'); + } + + public function testCompileWithArrayResolveEnv() + { + putenv('ARRAY={"foo":"bar"}'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', '%env(json:ARRAY)%'); + $container->compile(true); + + $this->assertSame(array('foo' => 'bar'), $container->getParameter('foo')); + + putenv('ARRAY'); + } + + public function testCompileWithArrayAndAnotherResolveEnv() + { + putenv('DUMMY_ENV_VAR=abc'); + putenv('ARRAY={"foo":"bar"}'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', '%env(json:ARRAY)%'); + $container->setParameter('bar', '%env(DUMMY_ENV_VAR)%'); + $container->compile(true); + + $this->assertSame(array('foo' => 'bar'), $container->getParameter('foo')); + $this->assertSame('abc', $container->getParameter('bar')); + + putenv('DUMMY_ENV_VAR'); + putenv('ARRAY'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage A string value must be composed of strings and/or numbers, but found parameter "env(json:ARRAY)" of type array inside string value "ABC %env(json:ARRAY)%". + */ + public function testCompileWithArrayInStringResolveEnv() + { + putenv('ARRAY={"foo":"bar"}'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', 'ABC %env(json:ARRAY)%'); + $container->compile(true); + + putenv('ARRAY'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvNotFoundException + * @expectedExceptionMessage Environment variable not found: "FOO". + */ + public function testCompileWithResolveMissingEnv() + { + $container = new ContainerBuilder(); + $container->setParameter('foo', '%env(FOO)%'); + $container->compile(true); + } + + public function testDynamicEnv() + { + putenv('DUMMY_FOO=some%foo%'); + putenv('DUMMY_BAR=%bar%'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', 'Foo%env(resolve:DUMMY_BAR)%'); + $container->setParameter('bar', 'Bar'); + $container->setParameter('baz', '%env(resolve:DUMMY_FOO)%'); + + $container->compile(true); + putenv('DUMMY_FOO'); + putenv('DUMMY_BAR'); + + $this->assertSame('someFooBar', $container->getParameter('baz')); + } + + public function testCastEnv() + { + $container = new ContainerBuilder(); + $container->setParameter('env(FAKE)', '123'); + + $container->register('foo', 'stdClass') + ->setPublic(true) + ->setProperties(array( + 'fake' => '%env(int:FAKE)%', + )); + + $container->compile(true); + + $this->assertSame(123, $container->get('foo')->fake); + } + + public function testEnvAreNullable() + { + $container = new ContainerBuilder(); + $container->setParameter('env(FAKE)', null); + + $container->register('foo', 'stdClass') + ->setPublic(true) + ->setProperties(array( + 'fake' => '%env(int:FAKE)%', + )); + + $container->compile(true); + + $this->assertNull($container->get('foo')->fake); + } + + public function testEnvInId() + { + $container = include __DIR__.'/Fixtures/containers/container_env_in_id.php'; + $container->compile(true); + + $expected = array( + 'service_container', + 'foo', + 'bar', + 'bar_%env(BAR)%', + ); + $this->assertSame($expected, array_keys($container->getDefinitions())); + + $expected = array( + PsrContainerInterface::class => true, + ContainerInterface::class => true, + 'baz_%env(BAR)%' => true, + 'bar_%env(BAR)%' => true, + ); + $this->assertSame($expected, $container->getRemovedIds()); + + $this->assertSame(array('baz_bar'), array_keys($container->getDefinition('foo')->getArgument(1))); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException + * @expectedExceptionMessage Circular reference detected for parameter "env(resolve:DUMMY_ENV_VAR)" ("env(resolve:DUMMY_ENV_VAR)" > "env(resolve:DUMMY_ENV_VAR)"). + */ + public function testCircularDynamicEnv() + { + putenv('DUMMY_ENV_VAR=some%foo%'); + + $container = new ContainerBuilder(); + $container->setParameter('foo', '%bar%'); + $container->setParameter('bar', '%env(resolve:DUMMY_ENV_VAR)%'); + + try { + $container->compile(true); + } finally { + putenv('DUMMY_ENV_VAR'); + } + } + + /** + * @expectedException \LogicException + */ + public function testMergeLogicException() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->compile(); + $container->merge(new ContainerBuilder()); + } + + public function testfindTaggedServiceIds() + { + $builder = new ContainerBuilder(); + $builder + ->register('foo', 'Bar\FooClass') + ->addTag('foo', array('foo' => 'foo')) + ->addTag('bar', array('bar' => 'bar')) + ->addTag('foo', array('foofoo' => 'foofoo')) + ; + $this->assertEquals($builder->findTaggedServiceIds('foo'), array( + 'foo' => array( + array('foo' => 'foo'), + array('foofoo' => 'foofoo'), + ), + ), '->findTaggedServiceIds() returns an array of service ids and its tag attributes'); + $this->assertEquals(array(), $builder->findTaggedServiceIds('foobar'), '->findTaggedServiceIds() returns an empty array if there is annotated services'); + } + + public function testFindUnusedTags() + { + $builder = new ContainerBuilder(); + $builder + ->register('foo', 'Bar\FooClass') + ->addTag('kernel.event_listener', array('foo' => 'foo')) + ->addTag('kenrel.event_listener', array('bar' => 'bar')) + ; + $builder->findTaggedServiceIds('kernel.event_listener'); + $this->assertEquals(array('kenrel.event_listener'), $builder->findUnusedTags(), '->findUnusedTags() returns an array with unused tags'); + } + + public function testFindDefinition() + { + $container = new ContainerBuilder(); + $container->setDefinition('foo', $definition = new Definition('Bar\FooClass')); + $container->setAlias('bar', 'foo'); + $container->setAlias('foobar', 'bar'); + $this->assertEquals($definition, $container->findDefinition('foobar'), '->findDefinition() returns a Definition'); + } + + public function testAddObjectResource() + { + $container = new ContainerBuilder(); + + $container->setResourceTracking(false); + $container->addObjectResource(new \BarClass()); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->setResourceTracking(true); + $container->addObjectResource(new \BarClass()); + + $resources = $container->getResources(); + + $this->assertCount(2, $resources, '2 resources were registered'); + + /* @var $resource \Symfony\Component\Config\Resource\FileResource */ + $resource = end($resources); + + $this->assertInstanceOf('Symfony\Component\Config\Resource\FileResource', $resource); + $this->assertSame(realpath(__DIR__.'/Fixtures/includes/classes.php'), realpath($resource->getResource())); + } + + public function testGetReflectionClass() + { + $container = new ContainerBuilder(); + + $container->setResourceTracking(false); + $r1 = $container->getReflectionClass('BarClass'); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->setResourceTracking(true); + $r2 = $container->getReflectionClass('BarClass'); + $r3 = $container->getReflectionClass('BarClass'); + + $this->assertNull($container->getReflectionClass('BarMissingClass')); + + $this->assertEquals($r1, $r2); + $this->assertSame($r2, $r3); + + $resources = $container->getResources(); + + $this->assertCount(3, $resources, '3 resources were registered'); + + $this->assertSame('reflection.BarClass', (string) $resources[1]); + $this->assertSame('BarMissingClass', (string) end($resources)); + } + + public function testGetReflectionClassOnInternalTypes() + { + $container = new ContainerBuilder(); + + $this->assertNull($container->getReflectionClass('int')); + $this->assertNull($container->getReflectionClass('float')); + $this->assertNull($container->getReflectionClass('string')); + $this->assertNull($container->getReflectionClass('bool')); + $this->assertNull($container->getReflectionClass('resource')); + $this->assertNull($container->getReflectionClass('object')); + $this->assertNull($container->getReflectionClass('array')); + $this->assertNull($container->getReflectionClass('null')); + $this->assertNull($container->getReflectionClass('callable')); + $this->assertNull($container->getReflectionClass('iterable')); + $this->assertNull($container->getReflectionClass('mixed')); + } + + public function testCompilesClassDefinitionsOfLazyServices() + { + $container = new ContainerBuilder(); + + $this->assertEmpty($container->getResources(), 'No resources get registered without resource tracking'); + + $container->register('foo', 'BarClass')->setPublic(true); + $container->getDefinition('foo')->setLazy(true); + + $container->compile(); + + $matchingResources = array_filter( + $container->getResources(), + function (ResourceInterface $resource) { + return 'reflection.BarClass' === (string) $resource; + } + ); + + $this->assertNotEmpty($matchingResources); + } + + public function testResources() + { + $container = new ContainerBuilder(); + $container->addResource($a = new FileResource(__DIR__.'/Fixtures/xml/services1.xml')); + $container->addResource($b = new FileResource(__DIR__.'/Fixtures/xml/services2.xml')); + $resources = array(); + foreach ($container->getResources() as $resource) { + if (false === strpos($resource, '.php')) { + $resources[] = $resource; + } + } + $this->assertEquals(array($a, $b), $resources, '->getResources() returns an array of resources read for the current configuration'); + $this->assertSame($container, $container->setResources(array())); + $this->assertEquals(array(), $container->getResources()); + } + + public function testFileExists() + { + $container = new ContainerBuilder(); + $A = new ComposerResource(); + $a = new FileResource(__DIR__.'/Fixtures/xml/services1.xml'); + $b = new FileResource(__DIR__.'/Fixtures/xml/services2.xml'); + $c = new DirectoryResource($dir = \dirname($b)); + + $this->assertTrue($container->fileExists((string) $a) && $container->fileExists((string) $b) && $container->fileExists($dir)); + + $resources = array(); + foreach ($container->getResources() as $resource) { + if (false === strpos($resource, '.php')) { + $resources[] = $resource; + } + } + + $this->assertEquals(array($A, $a, $b, $c), $resources, '->getResources() returns an array of resources read for the current configuration'); + } + + public function testExtension() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $container->registerExtension($extension = new \ProjectExtension()); + $this->assertSame($container->getExtension('project'), $extension, '->registerExtension() registers an extension'); + + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('LogicException'); + $container->getExtension('no_registered'); + } + + public function testRegisteredButNotLoadedExtension() + { + $extension = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface')->getMock(); + $extension->expects($this->once())->method('getAlias')->will($this->returnValue('project')); + $extension->expects($this->never())->method('load'); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->registerExtension($extension); + $container->compile(); + } + + public function testRegisteredAndLoadedExtension() + { + $extension = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface')->getMock(); + $extension->expects($this->exactly(2))->method('getAlias')->will($this->returnValue('project')); + $extension->expects($this->once())->method('load')->with(array(array('foo' => 'bar'))); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->registerExtension($extension); + $container->loadFromExtension('project', array('foo' => 'bar')); + $container->compile(); + } + + public function testPrivateServiceUser() + { + $fooDefinition = new Definition('BarClass'); + $fooUserDefinition = new Definition('BarUserClass', array(new Reference('bar'))); + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + + $fooDefinition->setPublic(false); + + $container->addDefinitions(array( + 'bar' => $fooDefinition, + 'bar_user' => $fooUserDefinition->setPublic(true), + )); + + $container->compile(); + $this->assertInstanceOf('BarClass', $container->get('bar_user')->bar); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsExceptionWhenSetServiceOnACompiledContainer() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->register('a', 'stdClass')->setPublic(true); + $container->compile(); + $container->set('a', new \stdClass()); + } + + public function testThrowsExceptionWhenAddServiceOnACompiledContainer() + { + $container = new ContainerBuilder(); + $container->compile(); + $container->set('a', $foo = new \stdClass()); + $this->assertSame($foo, $container->get('a')); + } + + public function testNoExceptionWhenSetSyntheticServiceOnACompiledContainer() + { + $container = new ContainerBuilder(); + $def = new Definition('stdClass'); + $def->setSynthetic(true)->setPublic(true); + $container->setDefinition('a', $def); + $container->compile(); + $container->set('a', $a = new \stdClass()); + $this->assertEquals($a, $container->get('a')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testThrowsExceptionWhenSetDefinitionOnACompiledContainer() + { + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->compile(); + $container->setDefinition('a', new Definition()); + } + + public function testExtensionConfig() + { + $container = new ContainerBuilder(); + + $configs = $container->getExtensionConfig('foo'); + $this->assertEmpty($configs); + + $first = array('foo' => 'bar'); + $container->prependExtensionConfig('foo', $first); + $configs = $container->getExtensionConfig('foo'); + $this->assertEquals(array($first), $configs); + + $second = array('ding' => 'dong'); + $container->prependExtensionConfig('foo', $second); + $configs = $container->getExtensionConfig('foo'); + $this->assertEquals(array($second, $first), $configs); + } + + public function testAbstractAlias() + { + $container = new ContainerBuilder(); + + $abstract = new Definition('AbstractClass'); + $abstract->setAbstract(true)->setPublic(true); + + $container->setDefinition('abstract_service', $abstract); + $container->setAlias('abstract_alias', 'abstract_service')->setPublic(true); + + $container->compile(); + + $this->assertSame('abstract_service', (string) $container->getAlias('abstract_alias')); + } + + public function testLazyLoadedService() + { + $loader = new ClosureLoader($container = new ContainerBuilder()); + $loader->load(function (ContainerBuilder $container) { + $container->set('a', new \BazClass()); + $definition = new Definition('BazClass'); + $definition->setLazy(true); + $definition->setPublic(true); + $container->setDefinition('a', $definition); + }); + + $container->setResourceTracking(true); + + $container->compile(); + + $r = new \ReflectionProperty($container, 'resources'); + $r->setAccessible(true); + $resources = $r->getValue($container); + + $classInList = false; + foreach ($resources as $resource) { + if ('reflection.BazClass' === (string) $resource) { + $classInList = true; + break; + } + } + + $this->assertTrue($classInList); + } + + public function testInlinedDefinitions() + { + $container = new ContainerBuilder(); + + $definition = new Definition('BarClass'); + + $container->register('bar_user', 'BarUserClass') + ->addArgument($definition) + ->setProperty('foo', $definition); + + $container->register('bar', 'BarClass') + ->setProperty('foo', $definition) + ->addMethodCall('setBaz', array($definition)); + + $barUser = $container->get('bar_user'); + $bar = $container->get('bar'); + + $this->assertSame($barUser->foo, $barUser->bar); + $this->assertSame($bar->foo, $bar->getBaz()); + $this->assertNotSame($bar->foo, $barUser->foo); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + * @expectedExceptionMessage Circular reference detected for service "app.test_class", path: "app.test_class -> App\TestClass -> app.test_class". + */ + public function testThrowsCircularExceptionForCircularAliases() + { + $builder = new ContainerBuilder(); + + $builder->setAliases(array( + 'foo' => new Alias('app.test_class'), + 'app.test_class' => new Alias('App\\TestClass'), + 'App\\TestClass' => new Alias('app.test_class'), + )); + + $builder->findDefinition('foo'); + } + + public function testInitializePropertiesBeforeMethodCalls() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass'); + $container->register('bar', 'MethodCallClass') + ->setPublic(true) + ->setProperty('simple', 'bar') + ->setProperty('complex', new Reference('foo')) + ->addMethodCall('callMe'); + + $container->compile(); + + $this->assertTrue($container->get('bar')->callPassed(), '->compile() initializes properties before method calls'); + } + + public function testAutowiring() + { + $container = new ContainerBuilder(); + + $container->register(A::class)->setPublic(true); + $bDefinition = $container->register('b', __NAMESPACE__.'\B'); + $bDefinition->setAutowired(true); + $bDefinition->setPublic(true); + + $container->compile(); + + $this->assertEquals(A::class, (string) $container->getDefinition('b')->getArgument(0)); + } + + public function testClassFromId() + { + $container = new ContainerBuilder(); + + $unknown = $container->register('Acme\UnknownClass'); + $autoloadClass = $container->register(CaseSensitiveClass::class); + $container->compile(); + + $this->assertSame('Acme\UnknownClass', $unknown->getClass()); + $this->assertEquals(CaseSensitiveClass::class, $autoloadClass->getClass()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The definition for "DateTime" has no class attribute, and appears to reference a class or interface in the global namespace. + */ + public function testNoClassFromGlobalNamespaceClassId() + { + $container = new ContainerBuilder(); + + $definition = $container->register(\DateTime::class); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The definition for "\DateTime" has no class attribute, and appears to reference a class or interface in the global namespace. + */ + public function testNoClassFromGlobalNamespaceClassIdWithLeadingSlash() + { + $container = new ContainerBuilder(); + + $container->register('\\'.\DateTime::class); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The definition for "\Symfony\Component\DependencyInjection\Tests\FooClass" has no class attribute, and appears to reference a class or interface. Please specify the class attribute explicitly or remove the leading backslash by renaming the service to "Symfony\Component\DependencyInjection\Tests\FooClass" to get rid of this error. + */ + public function testNoClassFromNamespaceClassIdWithLeadingSlash() + { + $container = new ContainerBuilder(); + + $container->register('\\'.FooClass::class); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The definition for "123_abc" has no class. + */ + public function testNoClassFromNonClassId() + { + $container = new ContainerBuilder(); + + $definition = $container->register('123_abc'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The definition for "\foo" has no class. + */ + public function testNoClassFromNsSeparatorId() + { + $container = new ContainerBuilder(); + + $definition = $container->register('\\foo'); + $container->compile(); + } + + public function testServiceLocator() + { + $container = new ContainerBuilder(); + $container->register('foo_service', ServiceLocator::class) + ->setPublic(true) + ->addArgument(array( + 'bar' => new ServiceClosureArgument(new Reference('bar_service')), + 'baz' => new ServiceClosureArgument(new TypedReference('baz_service', 'stdClass')), + )) + ; + $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true); + $container->register('baz_service', 'stdClass')->setPublic(false); + $container->compile(); + + $this->assertInstanceOf(ServiceLocator::class, $foo = $container->get('foo_service')); + $this->assertSame($container->get('bar_service'), $foo->get('bar')); + } + + public function testUninitializedReference() + { + $container = include __DIR__.'/Fixtures/containers/container_uninitialized_ref.php'; + $container->compile(); + + $bar = $container->get('bar'); + + $this->assertNull($bar->foo1); + $this->assertNull($bar->foo2); + $this->assertNull($bar->foo3); + $this->assertNull($bar->closures[0]()); + $this->assertNull($bar->closures[1]()); + $this->assertNull($bar->closures[2]()); + $this->assertSame(array(), iterator_to_array($bar->iter)); + + $container = include __DIR__.'/Fixtures/containers/container_uninitialized_ref.php'; + $container->compile(); + + $container->get('foo1'); + $container->get('baz'); + + $bar = $container->get('bar'); + + $this->assertEquals(new \stdClass(), $bar->foo1); + $this->assertNull($bar->foo2); + $this->assertEquals(new \stdClass(), $bar->foo3); + $this->assertEquals(new \stdClass(), $bar->closures[0]()); + $this->assertNull($bar->closures[1]()); + $this->assertEquals(new \stdClass(), $bar->closures[2]()); + $this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter)); + } + + /** + * @dataProvider provideAlmostCircular + */ + public function testAlmostCircular($visibility) + { + $container = include __DIR__.'/Fixtures/containers/container_almost_circular.php'; + + $foo = $container->get('foo'); + $this->assertSame($foo, $foo->bar->foobar->foo); + + $foo2 = $container->get('foo2'); + $this->assertSame($foo2, $foo2->bar->foobar->foo); + + $this->assertSame(array(), (array) $container->get('foobar4')); + + $foo5 = $container->get('foo5'); + $this->assertSame($foo5, $foo5->bar->foo); + } + + public function provideAlmostCircular() + { + yield array('public'); + yield array('private'); + } + + public function testRegisterForAutoconfiguration() + { + $container = new ContainerBuilder(); + $childDefA = $container->registerForAutoconfiguration('AInterface'); + $childDefB = $container->registerForAutoconfiguration('BInterface'); + $this->assertSame(array('AInterface' => $childDefA, 'BInterface' => $childDefB), $container->getAutoconfiguredInstanceof()); + + // when called multiple times, the same instance is returned + $this->assertSame($childDefA, $container->registerForAutoconfiguration('AInterface')); + } + + public function testCaseSensitivity() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true); + $container->register('Foo', 'stdClass')->setProperty('foo', new Reference('foo'))->setPublic(false); + $container->register('fOO', 'stdClass')->setProperty('Foo', new Reference('Foo'))->setPublic(true); + + $this->assertSame(array('service_container', 'foo', 'Foo', 'fOO', 'Psr\Container\ContainerInterface', 'Symfony\Component\DependencyInjection\ContainerInterface'), $container->getServiceIds()); + + $container->compile(); + + $this->assertNotSame($container->get('foo'), $container->get('fOO'), '->get() returns the service for the given id, case sensitively'); + $this->assertSame($container->get('fOO')->Foo->foo, $container->get('foo'), '->get() returns the service for the given id, case sensitively'); + } + + public function testParameterWithMixedCase() + { + $container = new ContainerBuilder(new ParameterBag(array('foo' => 'bar', 'FOO' => 'BAR'))); + $container->register('foo', 'stdClass') + ->setPublic(true) + ->setProperty('foo', '%FOO%'); + + $container->compile(); + + $this->assertSame('BAR', $container->get('foo')->foo); + } + + public function testArgumentsHaveHigherPriorityThanBindings() + { + $container = new ContainerBuilder(); + $container->register('class.via.bindings', CaseSensitiveClass::class)->setArguments(array( + 'via-bindings', + )); + $container->register('class.via.argument', CaseSensitiveClass::class)->setArguments(array( + 'via-argument', + )); + $container->register('foo', SimilarArgumentsDummy::class)->setPublic(true)->setBindings(array( + CaseSensitiveClass::class => new Reference('class.via.bindings'), + '$token' => '1234', + ))->setArguments(array( + '$class1' => new Reference('class.via.argument'), + )); + + $this->assertSame(array('service_container', 'class.via.bindings', 'class.via.argument', 'foo', 'Psr\Container\ContainerInterface', 'Symfony\Component\DependencyInjection\ContainerInterface'), $container->getServiceIds()); + + $container->compile(); + + $this->assertSame('via-argument', $container->get('foo')->class1->identifier); + $this->assertSame('via-bindings', $container->get('foo')->class2->identifier); + } + + public function testUninitializedSyntheticReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true); + $container->register('bar', 'stdClass')->setPublic(true)->setShared(false) + ->setProperty('foo', new Reference('foo', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)); + + $container->compile(); + + $this->assertEquals((object) array('foo' => null), $container->get('bar')); + + $container->set('foo', (object) array(123)); + $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); + } + + public function testIdCanBeAnObjectAsLongAsItCanBeCastToString() + { + $id = new Reference('another_service'); + $aliasId = new Reference('alias_id'); + + $container = new ContainerBuilder(); + $container->set($id, new \stdClass()); + $container->setAlias($aliasId, 'another_service'); + + $this->assertTrue($container->has('another_service')); + $this->assertTrue($container->has($id)); + $this->assertTrue($container->hasAlias('alias_id')); + $this->assertTrue($container->hasAlias($aliasId)); + + $container->removeAlias($aliasId); + $container->removeDefinition($id); + } +} + +class FooClass +{ +} + +class A +{ +} + +class B +{ + public function __construct(A $a) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ContainerTest.php b/vendor/symfony/dependency-injection/Tests/ContainerTest.php new file mode 100644 index 0000000..af5fa0c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ContainerTest.php @@ -0,0 +1,483 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class ContainerTest extends TestCase +{ + public function testConstructor() + { + $sc = new Container(); + $this->assertSame($sc, $sc->get('service_container'), '__construct() automatically registers itself as a service'); + + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '__construct() takes an array of parameters as its first argument'); + } + + /** + * @dataProvider dataForTestCamelize + */ + public function testCamelize($id, $expected) + { + $this->assertEquals($expected, Container::camelize($id), sprintf('Container::camelize("%s")', $id)); + } + + public function dataForTestCamelize() + { + return array( + array('foo_bar', 'FooBar'), + array('foo.bar', 'Foo_Bar'), + array('foo.bar_baz', 'Foo_BarBaz'), + array('foo._bar', 'Foo_Bar'), + array('foo_.bar', 'Foo_Bar'), + array('_foo', 'Foo'), + array('.foo', '_Foo'), + array('foo_', 'Foo'), + array('foo.', 'Foo_'), + array('foo\bar', 'Foo_Bar'), + ); + } + + /** + * @dataProvider dataForTestUnderscore + */ + public function testUnderscore($id, $expected) + { + $this->assertEquals($expected, Container::underscore($id), sprintf('Container::underscore("%s")', $id)); + } + + public function dataForTestUnderscore() + { + return array( + array('FooBar', 'foo_bar'), + array('Foo_Bar', 'foo.bar'), + array('Foo_BarBaz', 'foo.bar_baz'), + array('FooBar_BazQux', 'foo_bar.baz_qux'), + array('_Foo', '.foo'), + array('Foo_', 'foo.'), + ); + } + + public function testCompile() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $this->assertFalse($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag'); + $sc->compile(); + $this->assertTrue($sc->getParameterBag()->isResolved(), '->compile() resolves the parameter bag'); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag', $sc->getParameterBag(), '->compile() changes the parameter bag to a FrozenParameterBag instance'); + $this->assertEquals(array('foo' => 'bar'), $sc->getParameterBag()->all(), '->compile() copies the current parameters to the new parameter bag'); + } + + public function testIsCompiled() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $this->assertFalse($sc->isCompiled(), '->isCompiled() returns false if the container is not compiled'); + $sc->compile(); + $this->assertTrue($sc->isCompiled(), '->isCompiled() returns true if the container is compiled'); + } + + public function testIsCompiledWithFrozenParameters() + { + $sc = new Container(new FrozenParameterBag(array('foo' => 'bar'))); + $this->assertFalse($sc->isCompiled(), '->isCompiled() returns false if the container is not compiled but the parameter bag is already frozen'); + } + + public function testGetParameterBag() + { + $sc = new Container(); + $this->assertEquals(array(), $sc->getParameterBag()->all(), '->getParameterBag() returns an empty array if no parameter has been defined'); + } + + public function testGetSetParameter() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + $sc->setParameter('bar', 'foo'); + $this->assertEquals('foo', $sc->getParameter('bar'), '->setParameter() sets the value of a new parameter'); + + $sc->setParameter('foo', 'baz'); + $this->assertEquals('baz', $sc->getParameter('foo'), '->setParameter() overrides previously set parameter'); + + try { + $sc->getParameter('baba'); + $this->fail('->getParameter() thrown an \InvalidArgumentException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->getParameter() thrown an \InvalidArgumentException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->getParameter() thrown an \InvalidArgumentException if the key does not exist'); + } + } + + public function testGetSetParameterWithMixedCase() + { + $sc = new Container(new ParameterBag(array('foo' => 'bar'))); + + $sc->setParameter('Foo', 'baz1'); + $this->assertEquals('bar', $sc->getParameter('foo')); + $this->assertEquals('baz1', $sc->getParameter('Foo')); + } + + public function testGetServiceIds() + { + $sc = new Container(); + $sc->set('foo', $obj = new \stdClass()); + $sc->set('bar', $obj = new \stdClass()); + $this->assertEquals(array('service_container', 'foo', 'bar'), $sc->getServiceIds(), '->getServiceIds() returns all defined service ids'); + + $sc = new ProjectServiceContainer(); + $sc->set('foo', $obj = new \stdClass()); + $this->assertEquals(array('service_container', 'bar', 'foo_bar', 'foo.baz', 'circular', 'throw_exception', 'throws_exception_on_service_configuration', 'internal_dependency', 'foo'), $sc->getServiceIds(), '->getServiceIds() returns defined service ids by factory methods in the method map, followed by service ids defined by set()'); + } + + public function testSet() + { + $sc = new Container(); + $sc->set('._. \\o/', $foo = new \stdClass()); + $this->assertSame($foo, $sc->get('._. \\o/'), '->set() sets a service'); + } + + public function testSetWithNullResetTheService() + { + $sc = new Container(); + $sc->set('foo', new \stdClass()); + $sc->set('foo', null); + $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); + } + + public function testSetReplacesAlias() + { + $c = new ProjectServiceContainer(); + + $c->set('alias', $foo = new \stdClass()); + $this->assertSame($foo, $c->get('alias'), '->set() replaces an existing alias'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "bar" service is already initialized, you cannot replace it. + */ + public function testSetWithNullOnInitializedPredefinedService() + { + $sc = new Container(); + $sc->set('foo', new \stdClass()); + $sc->set('foo', null); + $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); + + $sc = new ProjectServiceContainer(); + $sc->get('bar'); + $sc->set('bar', null); + $this->assertTrue($sc->has('bar'), '->set() with null service resets the pre-defined service'); + } + + public function testSetWithNullOnUninitializedPredefinedService() + { + $sc = new Container(); + $sc->set('foo', new \stdClass()); + $sc->get('foo', null); + $sc->set('foo', null); + $this->assertFalse($sc->has('foo'), '->set() with null service resets the service'); + + $sc = new ProjectServiceContainer(); + $sc->set('bar', null); + $this->assertTrue($sc->has('bar'), '->set() with null service resets the pre-defined service'); + } + + public function testGet() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', $foo = new \stdClass()); + $this->assertSame($foo, $sc->get('foo'), '->get() returns the service for the given id'); + $this->assertSame($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id'); + $this->assertSame($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined'); + $this->assertSame($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined'); + + try { + $sc->get(''); + $this->fail('->get() throws a \InvalidArgumentException exception if the service is empty'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws a ServiceNotFoundException exception if the service is empty'); + } + $this->assertNull($sc->get('', ContainerInterface::NULL_ON_INVALID_REFERENCE), '->get() returns null if the service is empty'); + } + + public function testCaseSensitivity() + { + $sc = new Container(); + $sc->set('foo', $foo1 = new \stdClass()); + $sc->set('Foo', $foo2 = new \stdClass()); + + $this->assertSame(array('service_container', 'foo', 'Foo'), $sc->getServiceIds()); + $this->assertSame($foo1, $sc->get('foo'), '->get() returns the service for the given id, case sensitively'); + $this->assertSame($foo2, $sc->get('Foo'), '->get() returns the service for the given id, case sensitively'); + } + + public function testGetThrowServiceNotFoundException() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', $foo = new \stdClass()); + $sc->set('baz', $foo = new \stdClass()); + + try { + $sc->get('foo1'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent service "foo1". Did you mean this: "foo"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices'); + } + + try { + $sc->get('bag'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent service "bag". Did you mean one of these: "bar", "baz"?', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException with some advices'); + } + } + + public function testGetCircularReference() + { + $sc = new ProjectServiceContainer(); + try { + $sc->get('circular'); + $this->fail('->get() throws a ServiceCircularReferenceException if it contains circular reference'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException', $e, '->get() throws a ServiceCircularReferenceException if it contains circular reference'); + $this->assertStringStartsWith('Circular reference detected for service "circular"', $e->getMessage(), '->get() throws a \LogicException if it contains circular reference'); + } + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @expectedExceptionMessage The "request" service is synthetic, it needs to be set at boot time before it can be used. + */ + public function testGetSyntheticServiceThrows() + { + require_once __DIR__.'/Fixtures/php/services9_compiled.php'; + + $container = new \ProjectServiceContainer(); + $container->get('request'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @expectedExceptionMessage The "inlined" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead. + */ + public function testGetRemovedServiceThrows() + { + require_once __DIR__.'/Fixtures/php/services9_compiled.php'; + + $container = new \ProjectServiceContainer(); + $container->get('inlined'); + } + + public function testHas() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', new \stdClass()); + $this->assertFalse($sc->has('foo1'), '->has() returns false if the service does not exist'); + $this->assertTrue($sc->has('foo'), '->has() returns true if the service exists'); + $this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined'); + $this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined'); + $this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined'); + } + + public function testInitialized() + { + $sc = new ProjectServiceContainer(); + $sc->set('foo', new \stdClass()); + $this->assertTrue($sc->initialized('foo'), '->initialized() returns true if service is loaded'); + $this->assertFalse($sc->initialized('foo1'), '->initialized() returns false if service is not loaded'); + $this->assertFalse($sc->initialized('bar'), '->initialized() returns false if a service is defined, but not currently loaded'); + $this->assertFalse($sc->initialized('alias'), '->initialized() returns false if an aliased service is not initialized'); + + $sc->get('bar'); + $this->assertTrue($sc->initialized('alias'), '->initialized() returns true for alias if aliased service is initialized'); + } + + public function testInitializedWithPrivateService() + { + $sc = new ProjectServiceContainer(); + $sc->get('internal_dependency'); + $this->assertFalse($sc->initialized('internal')); + } + + public function testReset() + { + $c = new Container(); + $c->set('bar', new \stdClass()); + + $c->reset(); + + $this->assertNull($c->get('bar', ContainerInterface::NULL_ON_INVALID_REFERENCE)); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Something went terribly wrong! + */ + public function testGetThrowsException() + { + $c = new ProjectServiceContainer(); + + try { + $c->get('throw_exception'); + } catch (\Exception $e) { + // Do nothing. + } + + // Retry, to make sure that get*Service() will be called. + $c->get('throw_exception'); + } + + public function testGetThrowsExceptionOnServiceConfiguration() + { + $c = new ProjectServiceContainer(); + + try { + $c->get('throws_exception_on_service_configuration'); + } catch (\Exception $e) { + // Do nothing. + } + + $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); + + // Retry, to make sure that get*Service() will be called. + try { + $c->get('throws_exception_on_service_configuration'); + } catch (\Exception $e) { + // Do nothing. + } + $this->assertFalse($c->initialized('throws_exception_on_service_configuration')); + } + + protected function getField($obj, $field) + { + $reflection = new \ReflectionProperty($obj, $field); + $reflection->setAccessible(true); + + return $reflection->getValue($obj); + } + + public function testAlias() + { + $c = new ProjectServiceContainer(); + + $this->assertTrue($c->has('alias')); + $this->assertSame($c->get('alias'), $c->get('bar')); + } + + public function testThatCloningIsNotSupported() + { + $class = new \ReflectionClass('Symfony\Component\DependencyInjection\Container'); + $clone = $class->getMethod('__clone'); + $this->assertFalse($class->isCloneable()); + $this->assertTrue($clone->isPrivate()); + } + + public function testCheckExistenceOfAnInternalPrivateService() + { + $c = new ProjectServiceContainer(); + $c->get('internal_dependency'); + $this->assertFalse($c->has('internal')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @expectedExceptionMessage You have requested a non-existent service "internal". + */ + public function testRequestAnInternalSharedPrivateService() + { + $c = new ProjectServiceContainer(); + $c->get('internal_dependency'); + $c->get('internal'); + } +} + +class ProjectServiceContainer extends Container +{ + public $__bar; + public $__foo_bar; + public $__foo_baz; + public $__internal; + protected $privates; + protected $methodMap = array( + 'bar' => 'getBarService', + 'foo_bar' => 'getFooBarService', + 'foo.baz' => 'getFoo_BazService', + 'circular' => 'getCircularService', + 'throw_exception' => 'getThrowExceptionService', + 'throws_exception_on_service_configuration' => 'getThrowsExceptionOnServiceConfigurationService', + 'internal_dependency' => 'getInternalDependencyService', + ); + + public function __construct() + { + parent::__construct(); + + $this->__bar = new \stdClass(); + $this->__foo_bar = new \stdClass(); + $this->__foo_baz = new \stdClass(); + $this->__internal = new \stdClass(); + $this->privates = array(); + $this->aliases = array('alias' => 'bar'); + } + + protected function getInternalService() + { + return $this->privates['internal'] = $this->__internal; + } + + protected function getBarService() + { + return $this->services['bar'] = $this->__bar; + } + + protected function getFooBarService() + { + return $this->__foo_bar; + } + + protected function getFoo_BazService() + { + return $this->__foo_baz; + } + + protected function getCircularService() + { + return $this->get('circular'); + } + + protected function getThrowExceptionService() + { + throw new \Exception('Something went terribly wrong!'); + } + + protected function getThrowsExceptionOnServiceConfigurationService() + { + $this->services['throws_exception_on_service_configuration'] = $instance = new \stdClass(); + + throw new \Exception('Something was terribly wrong while trying to configure the service!'); + } + + protected function getInternalDependencyService() + { + $this->services['internal_dependency'] = $instance = new \stdClass(); + + $instance->internal = $this->privates['internal'] ?? $this->getInternalService(); + + return $instance; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/CrossCheckTest.php b/vendor/symfony/dependency-injection/Tests/CrossCheckTest.php new file mode 100644 index 0000000..c15104c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/CrossCheckTest.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\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class CrossCheckTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/Fixtures/'; + + require_once self::$fixturesPath.'/includes/classes.php'; + require_once self::$fixturesPath.'/includes/foo.php'; + } + + /** + * @dataProvider crossCheckLoadersDumpers + */ + public function testCrossCheck($fixture, $type) + { + $loaderClass = 'Symfony\\Component\\DependencyInjection\\Loader\\'.ucfirst($type).'FileLoader'; + $dumperClass = 'Symfony\\Component\\DependencyInjection\\Dumper\\'.ucfirst($type).'Dumper'; + + $tmp = tempnam(sys_get_temp_dir(), 'sf'); + + copy(self::$fixturesPath.'/'.$type.'/'.$fixture, $tmp); + + $container1 = new ContainerBuilder(); + $loader1 = new $loaderClass($container1, new FileLocator()); + $loader1->load($tmp); + + $dumper = new $dumperClass($container1); + file_put_contents($tmp, $dumper->dump()); + + $container2 = new ContainerBuilder(); + $loader2 = new $loaderClass($container2, new FileLocator()); + $loader2->load($tmp); + + unlink($tmp); + + $this->assertEquals($container2->getAliases(), $container1->getAliases(), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container2->getDefinitions(), $container1->getDefinitions(), 'loading a dump from a previously loaded container returns the same container'); + $this->assertEquals($container2->getParameterBag()->all(), $container1->getParameterBag()->all(), '->getParameterBag() returns the same value for both containers'); + $this->assertEquals(serialize($container2), serialize($container1), 'loading a dump from a previously loaded container returns the same container'); + + $services1 = array(); + foreach ($container1 as $id => $service) { + $services1[$id] = serialize($service); + } + $services2 = array(); + foreach ($container2 as $id => $service) { + $services2[$id] = serialize($service); + } + + unset($services1['service_container'], $services2['service_container']); + + $this->assertEquals($services2, $services1, 'Iterator on the containers returns the same services'); + } + + public function crossCheckLoadersDumpers() + { + return array( + array('services1.xml', 'xml'), + array('services2.xml', 'xml'), + array('services6.xml', 'xml'), + array('services8.xml', 'xml'), + array('services9.xml', 'xml'), + array('services1.yml', 'yaml'), + array('services2.yml', 'yaml'), + array('services6.yml', 'yaml'), + array('services8.yml', 'yaml'), + array('services9.yml', 'yaml'), + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/DefinitionTest.php b/vendor/symfony/dependency-injection/Tests/DefinitionTest.php new file mode 100644 index 0000000..0ed1359 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/DefinitionTest.php @@ -0,0 +1,383 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Definition; + +class DefinitionTest extends TestCase +{ + public function testConstructor() + { + $def = new Definition('stdClass'); + $this->assertEquals('stdClass', $def->getClass(), '__construct() takes the class name as its first argument'); + $this->assertSame(array('class' => true), $def->getChanges()); + + $def = new Definition('stdClass', array('foo')); + $this->assertEquals(array('foo'), $def->getArguments(), '__construct() takes an optional array of arguments as its second argument'); + } + + public function testSetGetFactory() + { + $def = new Definition(); + + $this->assertSame($def, $def->setFactory('foo'), '->setFactory() implements a fluent interface'); + $this->assertEquals('foo', $def->getFactory(), '->getFactory() returns the factory'); + + $def->setFactory('Foo::bar'); + $this->assertEquals(array('Foo', 'bar'), $def->getFactory(), '->setFactory() converts string static method call to the array'); + $this->assertSame(array('factory' => true), $def->getChanges()); + } + + public function testSetGetClass() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setClass('foo'), '->setClass() implements a fluent interface'); + $this->assertEquals('foo', $def->getClass(), '->getClass() returns the class name'); + } + + public function testSetGetDecoratedService() + { + $def = new Definition('stdClass'); + $this->assertNull($def->getDecoratedService()); + $def->setDecoratedService('foo', 'foo.renamed', 5); + $this->assertEquals(array('foo', 'foo.renamed', 5), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + + $def = new Definition('stdClass'); + $this->assertNull($def->getDecoratedService()); + $def->setDecoratedService('foo', 'foo.renamed'); + $this->assertEquals(array('foo', 'foo.renamed', 0), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + + $def = new Definition('stdClass'); + $def->setDecoratedService('foo'); + $this->assertEquals(array('foo', null, 0), $def->getDecoratedService()); + $def->setDecoratedService(null); + $this->assertNull($def->getDecoratedService()); + + $def = new Definition('stdClass'); + + if (method_exists($this, 'expectException')) { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('The decorated service inner name for "foo" must be different than the service name itself.'); + } else { + $this->setExpectedException('InvalidArgumentException', 'The decorated service inner name for "foo" must be different than the service name itself.'); + } + + $def->setDecoratedService('foo', 'foo'); + } + + public function testArguments() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setArguments(array('foo')), '->setArguments() implements a fluent interface'); + $this->assertEquals(array('foo'), $def->getArguments(), '->getArguments() returns the arguments'); + $this->assertSame($def, $def->addArgument('bar'), '->addArgument() implements a fluent interface'); + $this->assertEquals(array('foo', 'bar'), $def->getArguments(), '->addArgument() adds an argument'); + } + + public function testMethodCalls() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setMethodCalls(array(array('foo', array('foo')))), '->setMethodCalls() implements a fluent interface'); + $this->assertEquals(array(array('foo', array('foo'))), $def->getMethodCalls(), '->getMethodCalls() returns the methods to call'); + $this->assertSame($def, $def->addMethodCall('bar', array('bar')), '->addMethodCall() implements a fluent interface'); + $this->assertEquals(array(array('foo', array('foo')), array('bar', array('bar'))), $def->getMethodCalls(), '->addMethodCall() adds a method to call'); + $this->assertTrue($def->hasMethodCall('bar'), '->hasMethodCall() returns true if first argument is a method to call registered'); + $this->assertFalse($def->hasMethodCall('no_registered'), '->hasMethodCall() returns false if first argument is not a method to call registered'); + $this->assertSame($def, $def->removeMethodCall('bar'), '->removeMethodCall() implements a fluent interface'); + $this->assertEquals(array(array('foo', array('foo'))), $def->getMethodCalls(), '->removeMethodCall() removes a method to call'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Method name cannot be empty. + */ + public function testExceptionOnEmptyMethodCall() + { + $def = new Definition('stdClass'); + $def->addMethodCall(''); + } + + public function testSetGetFile() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setFile('foo'), '->setFile() implements a fluent interface'); + $this->assertEquals('foo', $def->getFile(), '->getFile() returns the file to include'); + } + + public function testSetIsShared() + { + $def = new Definition('stdClass'); + $this->assertTrue($def->isShared(), '->isShared() returns true by default'); + $this->assertSame($def, $def->setShared(false), '->setShared() implements a fluent interface'); + $this->assertFalse($def->isShared(), '->isShared() returns false if the instance must not be shared'); + } + + public function testSetIsPublic() + { + $def = new Definition('stdClass'); + $this->assertTrue($def->isPublic(), '->isPublic() returns true by default'); + $this->assertSame($def, $def->setPublic(false), '->setPublic() implements a fluent interface'); + $this->assertFalse($def->isPublic(), '->isPublic() returns false if the instance must not be public.'); + } + + public function testSetIsSynthetic() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isSynthetic(), '->isSynthetic() returns false by default'); + $this->assertSame($def, $def->setSynthetic(true), '->setSynthetic() implements a fluent interface'); + $this->assertTrue($def->isSynthetic(), '->isSynthetic() returns true if the service is synthetic.'); + } + + public function testSetIsLazy() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isLazy(), '->isLazy() returns false by default'); + $this->assertSame($def, $def->setLazy(true), '->setLazy() implements a fluent interface'); + $this->assertTrue($def->isLazy(), '->isLazy() returns true if the service is lazy.'); + } + + public function testSetIsAbstract() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAbstract(), '->isAbstract() returns false by default'); + $this->assertSame($def, $def->setAbstract(true), '->setAbstract() implements a fluent interface'); + $this->assertTrue($def->isAbstract(), '->isAbstract() returns true if the instance must not be public.'); + } + + public function testSetIsDeprecated() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isDeprecated(), '->isDeprecated() returns false by default'); + $this->assertSame($def, $def->setDeprecated(true), '->setDeprecated() implements a fluent interface'); + $this->assertTrue($def->isDeprecated(), '->isDeprecated() returns true if the instance should not be used anymore.'); + $this->assertSame('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', $def->getDeprecationMessage('deprecated_service'), '->getDeprecationMessage() should return a formatted message template'); + } + + /** + * @dataProvider invalidDeprecationMessageProvider + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testSetDeprecatedWithInvalidDeprecationTemplate($message) + { + $def = new Definition('stdClass'); + $def->setDeprecated(false, $message); + } + + public function invalidDeprecationMessageProvider() + { + return array( + "With \rs" => array("invalid \r message %service_id%"), + "With \ns" => array("invalid \n message %service_id%"), + 'With */s' => array('invalid */ message %service_id%'), + 'message not containing require %service_id% variable' => array('this is deprecated'), + ); + } + + public function testSetGetConfigurator() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->setConfigurator('foo'), '->setConfigurator() implements a fluent interface'); + $this->assertEquals('foo', $def->getConfigurator(), '->getConfigurator() returns the configurator'); + } + + public function testClearTags() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->clearTags(), '->clearTags() implements a fluent interface'); + $def->addTag('foo', array('foo' => 'bar')); + $def->clearTags(); + $this->assertEquals(array(), $def->getTags(), '->clearTags() removes all current tags'); + } + + public function testClearTag() + { + $def = new Definition('stdClass'); + $this->assertSame($def, $def->clearTags(), '->clearTags() implements a fluent interface'); + $def->addTag('1foo1', array('foo1' => 'bar1')); + $def->addTag('2foo2', array('foo2' => 'bar2')); + $def->addTag('3foo3', array('foo3' => 'bar3')); + $def->clearTag('2foo2'); + $this->assertTrue($def->hasTag('1foo1')); + $this->assertFalse($def->hasTag('2foo2')); + $this->assertTrue($def->hasTag('3foo3')); + $def->clearTag('1foo1'); + $this->assertFalse($def->hasTag('1foo1')); + $this->assertTrue($def->hasTag('3foo3')); + } + + public function testTags() + { + $def = new Definition('stdClass'); + $this->assertEquals(array(), $def->getTag('foo'), '->getTag() returns an empty array if the tag is not defined'); + $this->assertFalse($def->hasTag('foo')); + $this->assertSame($def, $def->addTag('foo'), '->addTag() implements a fluent interface'); + $this->assertTrue($def->hasTag('foo')); + $this->assertEquals(array(array()), $def->getTag('foo'), '->getTag() returns attributes for a tag name'); + $def->addTag('foo', array('foo' => 'bar')); + $this->assertEquals(array(array(), array('foo' => 'bar')), $def->getTag('foo'), '->addTag() can adds the same tag several times'); + $def->addTag('bar', array('bar' => 'bar')); + $this->assertEquals($def->getTags(), array( + 'foo' => array(array(), array('foo' => 'bar')), + 'bar' => array(array('bar' => 'bar')), + ), '->getTags() returns all tags'); + } + + public function testSetArgument() + { + $def = new Definition('stdClass'); + + $def->addArgument('foo'); + $this->assertSame(array('foo'), $def->getArguments()); + + $this->assertSame($def, $def->replaceArgument(0, 'moo')); + $this->assertSame(array('moo'), $def->getArguments()); + + $def->addArgument('moo'); + $def + ->replaceArgument(0, 'foo') + ->replaceArgument(1, 'bar') + ; + $this->assertSame(array('foo', 'bar'), $def->getArguments()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetArgumentShouldCheckBounds() + { + $def = new Definition('stdClass'); + + $def->addArgument('foo'); + $def->getArgument(1); + } + + /** + * @expectedException \OutOfBoundsException + * @expectedExceptionMessage The index "1" is not in the range [0, 0]. + */ + public function testReplaceArgumentShouldCheckBounds() + { + $def = new Definition('stdClass'); + + $def->addArgument('foo'); + $def->replaceArgument(1, 'bar'); + } + + /** + * @expectedException \OutOfBoundsException + * @expectedExceptionMessage Cannot replace arguments if none have been configured yet. + */ + public function testReplaceArgumentWithoutExistingArgumentsShouldCheckBounds() + { + $def = new Definition('stdClass'); + $def->replaceArgument(0, 'bar'); + } + + public function testSetGetProperties() + { + $def = new Definition('stdClass'); + + $this->assertEquals(array(), $def->getProperties()); + $this->assertSame($def, $def->setProperties(array('foo' => 'bar'))); + $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); + } + + public function testSetProperty() + { + $def = new Definition('stdClass'); + + $this->assertEquals(array(), $def->getProperties()); + $this->assertSame($def, $def->setProperty('foo', 'bar')); + $this->assertEquals(array('foo' => 'bar'), $def->getProperties()); + } + + public function testAutowired() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAutowired()); + + $def->setAutowired(true); + $this->assertTrue($def->isAutowired()); + + $def->setAutowired(false); + $this->assertFalse($def->isAutowired()); + } + + public function testChangesNoChanges() + { + $def = new Definition(); + + $this->assertSame(array(), $def->getChanges()); + } + + public function testGetChangesWithChanges() + { + $def = new Definition('stdClass', array('fooarg')); + + $def->setAbstract(true); + $def->setAutowired(true); + $def->setConfigurator('configuration_func'); + $def->setDecoratedService(null); + $def->setDeprecated(true); + $def->setFactory('factory_func'); + $def->setFile('foo.php'); + $def->setLazy(true); + $def->setPublic(true); + $def->setShared(true); + $def->setSynthetic(true); + // changes aren't tracked for these, class or arguments + $def->setInstanceofConditionals(array()); + $def->addTag('foo_tag'); + $def->addMethodCall('methodCall'); + $def->setProperty('fooprop', true); + $def->setAutoconfigured(true); + + $this->assertSame(array( + 'class' => true, + 'autowired' => true, + 'configurator' => true, + 'decorated_service' => true, + 'deprecated' => true, + 'factory' => true, + 'file' => true, + 'lazy' => true, + 'public' => true, + 'shared' => true, + 'autoconfigured' => true, + ), $def->getChanges()); + + $def->setChanges(array()); + $this->assertSame(array(), $def->getChanges()); + } + + public function testShouldAutoconfigure() + { + $def = new Definition('stdClass'); + $this->assertFalse($def->isAutoconfigured()); + $def->setAutoconfigured(true); + $this->assertTrue($def->isAutoconfigured()); + } + + public function testAddError() + { + $def = new Definition('stdClass'); + $this->assertEmpty($def->getErrors()); + $def->addError('First error'); + $def->addError('Second error'); + $this->assertSame(array('First error', 'Second error'), $def->getErrors()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.php b/vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.php new file mode 100644 index 0000000..ffdd073 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Dumper/GraphvizDumperTest.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\DependencyInjection\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\GraphvizDumper; + +class GraphvizDumperTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = __DIR__.'/../Fixtures/'; + } + + public function testDump() + { + $dumper = new GraphvizDumper($container = new ContainerBuilder()); + + $this->assertStringEqualsFile(self::$fixturesPath.'/graphviz/services1.dot', $dumper->dump(), '->dump() dumps an empty container as an empty dot file'); + + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services9.dot')), $dumper->dump(), '->dump() dumps services'); + + $container = include self::$fixturesPath.'/containers/container10.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services10.dot')), $dumper->dump(), '->dump() dumps services'); + + $container = include self::$fixturesPath.'/containers/container10.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals($dumper->dump(array( + 'graph' => array('ratio' => 'normal'), + 'node' => array('fontsize' => 13, 'fontname' => 'Verdana', 'shape' => 'square'), + 'edge' => array('fontsize' => 12, 'fontname' => 'Verdana', 'color' => 'white', 'arrowhead' => 'closed', 'arrowsize' => 1), + 'node.instance' => array('fillcolor' => 'green', 'style' => 'empty'), + 'node.definition' => array('fillcolor' => 'grey'), + 'node.missing' => array('fillcolor' => 'red', 'style' => 'empty'), + )), str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services10-1.dot')), '->dump() dumps services'); + } + + public function testDumpWithFrozenContainer() + { + $container = include self::$fixturesPath.'/containers/container13.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services13.dot')), $dumper->dump(), '->dump() dumps services'); + } + + public function testDumpWithFrozenCustomClassContainer() + { + $container = include self::$fixturesPath.'/containers/container14.php'; + $dumper = new GraphvizDumper($container); + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services14.dot')), $dumper->dump(), '->dump() dumps services'); + } + + public function testDumpWithUnresolvedParameter() + { + $container = include self::$fixturesPath.'/containers/container17.php'; + $dumper = new GraphvizDumper($container); + + $this->assertEquals(str_replace('%path%', __DIR__, file_get_contents(self::$fixturesPath.'/graphviz/services17.dot')), $dumper->dump(), '->dump() dumps services'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php b/vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php new file mode 100644 index 0000000..ffdc1a0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Dumper/PhpDumperTest.php @@ -0,0 +1,989 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition; +use Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator; +use Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\ExpressionLanguage\Expression; + +require_once __DIR__.'/../Fixtures/includes/classes.php'; + +class PhpDumperTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + public function testDump() + { + $container = new ContainerBuilder(); + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services1-1.php', $dumper->dump(array('class' => 'Container', 'base_class' => 'AbstractContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Dump')), '->dump() takes a class and a base_class options'); + } + + public function testDumpOptimizationString() + { + $definition = new Definition(); + $definition->setClass('stdClass'); + $definition->addArgument(array( + 'only dot' => '.', + 'concatenation as value' => '.\'\'.', + 'concatenation from the start value' => '\'\'.', + '.' => 'dot as a key', + '.\'\'.' => 'concatenation as a key', + '\'\'.' => 'concatenation from the start key', + 'optimize concatenation' => 'string1%some_string%string2', + 'optimize concatenation with empty string' => 'string1%empty_value%string2', + 'optimize concatenation from the start' => '%empty_value%start', + 'optimize concatenation at the end' => 'end%empty_value%', + 'new line' => "string with \nnew line", + )); + $definition->setPublic(true); + + $container = new ContainerBuilder(); + $container->setResourceTracking(false); + $container->setDefinition('test', $definition); + $container->setParameter('empty_value', ''); + $container->setParameter('some_string', '-'); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services10.php', $dumper->dump(), '->dump() dumps an empty container as an empty PHP class'); + } + + public function testDumpRelativeDir() + { + $definition = new Definition(); + $definition->setClass('stdClass'); + $definition->addArgument('%foo%'); + $definition->addArgument(array('%foo%' => '%buz%/')); + $definition->setPublic(true); + + $container = new ContainerBuilder(); + $container->setDefinition('test', $definition); + $container->setParameter('foo', 'wiz'.\dirname(__DIR__)); + $container->setParameter('bar', __DIR__); + $container->setParameter('baz', '%bar%/PhpDumperTest.php'); + $container->setParameter('buz', \dirname(\dirname(__DIR__))); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services12.php', $dumper->dump(array('file' => __FILE__)), '->dump() dumps __DIR__ relative strings'); + } + + public function testDumpCustomContainerClassWithoutConstructor() + { + $container = new ContainerBuilder(); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_without_constructor.php', $dumper->dump(array('base_class' => 'NoConstructorContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container'))); + } + + public function testDumpCustomContainerClassConstructorWithoutArguments() + { + $container = new ContainerBuilder(); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_constructor_without_arguments.php', $dumper->dump(array('base_class' => 'ConstructorWithoutArgumentsContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container'))); + } + + public function testDumpCustomContainerClassWithOptionalArgumentLessConstructor() + { + $container = new ContainerBuilder(); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_with_optional_constructor_arguments.php', $dumper->dump(array('base_class' => 'ConstructorWithOptionalArgumentsContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container'))); + } + + public function testDumpCustomContainerClassWithMandatoryArgumentLessConstructor() + { + $container = new ContainerBuilder(); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/custom_container_class_with_mandatory_constructor_arguments.php', $dumper->dump(array('base_class' => 'ConstructorWithMandatoryArgumentsContainer', 'namespace' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\Container'))); + } + + /** + * @dataProvider provideInvalidParameters + * @expectedException \InvalidArgumentException + */ + public function testExportParameters($parameters) + { + $container = new ContainerBuilder(new ParameterBag($parameters)); + $container->compile(); + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + public function provideInvalidParameters() + { + return array( + array(array('foo' => new Definition('stdClass'))), + array(array('foo' => new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))), + array(array('foo' => new Reference('foo'))), + array(array('foo' => new Variable('foo'))), + ); + } + + public function testAddParameters() + { + $container = include self::$fixturesPath.'/containers/container8.php'; + $container->compile(); + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services8.php', $dumper->dump(), '->dump() dumps parameters'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage Cannot dump an uncompiled container. + */ + public function testAddServiceWithoutCompilation() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + new PhpDumper($container); + } + + public function testAddService() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $container->compile(); + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services9_compiled.php', str_replace(str_replace('\\', '\\\\', self::$fixturesPath.\DIRECTORY_SEPARATOR.'includes'.\DIRECTORY_SEPARATOR), '%path%', $dumper->dump()), '->dump() dumps services'); + + $container = new ContainerBuilder(); + $container->register('foo', 'FooClass')->addArgument(new \stdClass())->setPublic(true); + $container->compile(); + $dumper = new PhpDumper($container); + try { + $dumper->dump(); + $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } catch (\Exception $e) { + $this->assertInstanceOf('\Symfony\Component\DependencyInjection\Exception\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } + } + + public function testDumpAsFiles() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $container->getDefinition('bar')->addTag('hot'); + $container->compile(); + $dumper = new PhpDumper($container); + $dump = print_r($dumper->dump(array('as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot')), true); + if ('\\' === \DIRECTORY_SEPARATOR) { + $dump = str_replace('\\\\Fixtures\\\\includes\\\\foo.php', '/Fixtures/includes/foo.php', $dump); + } + $this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump); + } + + public function testServicesWithAnonymousFactories() + { + $container = include self::$fixturesPath.'/containers/container19.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services19.php', $dumper->dump(), '->dump() dumps services with anonymous factories'); + } + + public function testAddServiceIdWithUnsupportedCharacters() + { + $class = 'Symfony_DI_PhpDumper_Test_Unsupported_Characters'; + $container = new ContainerBuilder(); + $container->register('bar$', 'FooClass')->setPublic(true); + $container->register('bar$!', 'FooClass')->setPublic(true); + $container->compile(); + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => $class))); + + $this->assertTrue(method_exists($class, 'getBarService')); + $this->assertTrue(method_exists($class, 'getBar2Service')); + } + + public function testConflictingServiceIds() + { + $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Service_Ids'; + $container = new ContainerBuilder(); + $container->register('foo_bar', 'FooClass')->setPublic(true); + $container->register('foobar', 'FooClass')->setPublic(true); + $container->compile(); + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => $class))); + + $this->assertTrue(method_exists($class, 'getFooBarService')); + $this->assertTrue(method_exists($class, 'getFoobar2Service')); + } + + public function testConflictingMethodsWithParent() + { + $class = 'Symfony_DI_PhpDumper_Test_Conflicting_Method_With_Parent'; + $container = new ContainerBuilder(); + $container->register('bar', 'FooClass')->setPublic(true); + $container->register('foo_bar', 'FooClass')->setPublic(true); + $container->compile(); + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array( + 'class' => $class, + 'base_class' => 'Symfony\Component\DependencyInjection\Tests\Fixtures\containers\CustomContainer', + ))); + + $this->assertTrue(method_exists($class, 'getBar2Service')); + $this->assertTrue(method_exists($class, 'getFoobar2Service')); + } + + /** + * @dataProvider provideInvalidFactories + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Cannot dump definition + */ + public function testInvalidFactories($factory) + { + $container = new ContainerBuilder(); + $def = new Definition('stdClass'); + $def->setPublic(true); + $def->setFactory($factory); + $container->setDefinition('bar', $def); + $container->compile(); + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + public function provideInvalidFactories() + { + return array( + array(array('', 'method')), + array(array('class', '')), + array(array('...', 'method')), + array(array('class', '...')), + ); + } + + public function testAliases() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $container->setParameter('foo_bar', 'foo_bar'); + $container->compile(); + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Aliases'))); + + $container = new \Symfony_DI_PhpDumper_Test_Aliases(); + $foo = $container->get('foo'); + $this->assertSame($foo, $container->get('alias_for_foo')); + $this->assertSame($foo, $container->get('alias_for_alias')); + } + + public function testFrozenContainerWithoutAliases() + { + $container = new ContainerBuilder(); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Frozen_No_Aliases'))); + + $container = new \Symfony_DI_PhpDumper_Test_Frozen_No_Aliases(); + $this->assertFalse($container->has('foo')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "decorator_service" service is already initialized, you cannot replace it. + */ + public function testOverrideServiceWhenUsingADumpedContainer() + { + require_once self::$fixturesPath.'/php/services9_compiled.php'; + + $container = new \ProjectServiceContainer(); + $container->get('decorator_service'); + $container->set('decorator_service', $decorator = new \stdClass()); + + $this->assertSame($decorator, $container->get('decorator_service'), '->set() overrides an already defined service'); + } + + public function testDumpAutowireData() + { + $container = include self::$fixturesPath.'/containers/container24.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services24.php', $dumper->dump()); + } + + public function testEnvInId() + { + $container = include self::$fixturesPath.'/containers/container_env_in_id.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_env_in_id.php', $dumper->dump()); + } + + public function testEnvParameter() + { + $rand = mt_rand(); + putenv('Baz='.$rand); + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services26.yml'); + $container->setParameter('env(json_file)', self::$fixturesPath.'/array.json'); + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services26.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_EnvParameters', 'file' => self::$fixturesPath.'/php/services26.php'))); + + require self::$fixturesPath.'/php/services26.php'; + $container = new \Symfony_DI_PhpDumper_Test_EnvParameters(); + $this->assertSame($rand, $container->getParameter('baz')); + $this->assertSame(array(123, 'abc'), $container->getParameter('json')); + $this->assertSame('sqlite:///foo/bar/var/data.db', $container->getParameter('db_dsn')); + putenv('Baz'); + } + + public function testResolvedBase64EnvParameters() + { + $container = new ContainerBuilder(); + $container->setParameter('env(foo)', base64_encode('world')); + $container->setParameter('hello', '%env(base64:foo)%'); + $container->compile(true); + + $expected = array( + 'env(foo)' => 'd29ybGQ=', + 'hello' => 'world', + ); + $this->assertSame($expected, $container->getParameterBag()->all()); + } + + public function testDumpedBase64EnvParameters() + { + $container = new ContainerBuilder(); + $container->setParameter('env(foo)', base64_encode('world')); + $container->setParameter('hello', '%env(base64:foo)%'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_base64_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Base64Parameters'))); + + require self::$fixturesPath.'/php/services_base64_env.php'; + $container = new \Symfony_DI_PhpDumper_Test_Base64Parameters(); + $this->assertSame('world', $container->getParameter('hello')); + } + + public function testCustomEnvParameters() + { + $container = new ContainerBuilder(); + $container->setParameter('env(foo)', str_rot13('world')); + $container->setParameter('hello', '%env(rot13:foo)%'); + $container->register(Rot13EnvVarProcessor::class)->addTag('container.env_var_processor')->setPublic(true); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_rot13_env.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Rot13Parameters'))); + + require self::$fixturesPath.'/php/services_rot13_env.php'; + $container = new \Symfony_DI_PhpDumper_Test_Rot13Parameters(); + $this->assertSame('world', $container->getParameter('hello')); + } + + public function testFileEnvProcessor() + { + $container = new ContainerBuilder(); + $container->setParameter('env(foo)', __FILE__); + $container->setParameter('random', '%env(file:foo)%'); + $container->compile(true); + + $this->assertStringEqualsFile(__FILE__, $container->getParameter('random')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\EnvParameterException + * @expectedExceptionMessage Environment variables "FOO" are never used. Please, check your container's configuration. + */ + public function testUnusedEnvParameter() + { + $container = new ContainerBuilder(); + $container->getParameter('env(FOO)'); + $container->compile(); + $dumper = new PhpDumper($container); + $dumper->dump(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException + * @expectedExceptionMessage Circular reference detected for parameter "env(resolve:DUMMY_ENV_VAR)" ("env(resolve:DUMMY_ENV_VAR)" > "env(resolve:DUMMY_ENV_VAR)"). + */ + public function testCircularDynamicEnv() + { + $container = new ContainerBuilder(); + $container->setParameter('foo', '%bar%'); + $container->setParameter('bar', '%env(resolve:DUMMY_ENV_VAR)%'); + $container->compile(); + + $dumper = new PhpDumper($container); + $dump = $dumper->dump(array('class' => $class = __FUNCTION__)); + + eval('?>'.$dump); + $container = new $class(); + + putenv('DUMMY_ENV_VAR=%foo%'); + try { + $container->getParameter('bar'); + } finally { + putenv('DUMMY_ENV_VAR'); + } + } + + public function testInlinedDefinitionReferencingServiceContainer() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->addMethodCall('add', array(new Reference('service_container')))->setPublic(false); + $container->register('bar', 'stdClass')->addArgument(new Reference('foo'))->setPublic(true); + $container->compile(); + + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services13.php', $dumper->dump(), '->dump() dumps inline definitions which reference service_container'); + } + + public function testNonSharedLazyDefinitionReferences() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setShared(false)->setLazy(true); + $container->register('bar', 'stdClass')->addArgument(new Reference('foo', ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, false))->setPublic(true); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new \DummyProxyDumper()); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_non_shared_lazy.php', $dumper->dump()); + } + + public function testInitializePropertiesBeforeMethodCalls() + { + require_once self::$fixturesPath.'/includes/classes.php'; + + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true); + $container->register('bar', 'MethodCallClass') + ->setPublic(true) + ->setProperty('simple', 'bar') + ->setProperty('complex', new Reference('foo')) + ->addMethodCall('callMe'); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls'))); + + $container = new \Symfony_DI_PhpDumper_Test_Properties_Before_Method_Calls(); + $this->assertTrue($container->get('bar')->callPassed(), '->dump() initializes properties before method calls'); + } + + public function testCircularReferenceAllowanceForLazyServices() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->addArgument(new Reference('bar'))->setPublic(true); + $container->register('bar', 'stdClass')->setLazy(true)->addArgument(new Reference('foo'))->setPublic(true); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->dump(); + + $this->addToAssertionCount(1); + } + + public function testCircularReferenceAllowanceForInlinedDefinitionsForLazyServices() + { + /* + * test graph: + * [connection] -> [event_manager] --> [entity_manager](lazy) + * | + * --(call)- addEventListener ("@lazy_service") + * + * [lazy_service](lazy) -> [entity_manager](lazy) + * + */ + + $container = new ContainerBuilder(); + + $eventManagerDefinition = new Definition('stdClass'); + + $connectionDefinition = $container->register('connection', 'stdClass')->setPublic(true); + $connectionDefinition->addArgument($eventManagerDefinition); + + $container->register('entity_manager', 'stdClass') + ->setPublic(true) + ->setLazy(true) + ->addArgument(new Reference('connection')); + + $lazyServiceDefinition = $container->register('lazy_service', 'stdClass'); + $lazyServiceDefinition->setPublic(true); + $lazyServiceDefinition->setLazy(true); + $lazyServiceDefinition->addArgument(new Reference('entity_manager')); + + $eventManagerDefinition->addMethodCall('addEventListener', array(new Reference('lazy_service'))); + + $container->compile(); + + $dumper = new PhpDumper($container); + + $dumper->setProxyDumper(new \DummyProxyDumper()); + $dumper->dump(); + + $this->addToAssertionCount(1); + } + + public function testDedupLazyProxy() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setLazy(true)->setPublic(true); + $container->register('bar', 'stdClass')->setLazy(true)->setPublic(true); + $container->compile(); + + $dumper = new PhpDumper($container); + $dumper->setProxyDumper(new \DummyProxyDumper()); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_dedup_lazy_proxy.php', $dumper->dump()); + } + + public function testLazyArgumentProvideGenerator() + { + require_once self::$fixturesPath.'/includes/classes.php'; + + $container = new ContainerBuilder(); + $container->register('lazy_referenced', 'stdClass')->setPublic(true); + $container + ->register('lazy_context', 'LazyContext') + ->setPublic(true) + ->setArguments(array( + new IteratorArgument(array('k1' => new Reference('lazy_referenced'), 'k2' => new Reference('service_container'))), + new IteratorArgument(array()), + )) + ; + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator'))); + + $container = new \Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator(); + $lazyContext = $container->get('lazy_context'); + + $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyValues); + $this->assertInstanceOf(RewindableGenerator::class, $lazyContext->lazyEmptyValues); + $this->assertCount(2, $lazyContext->lazyValues); + $this->assertCount(0, $lazyContext->lazyEmptyValues); + + $i = -1; + foreach ($lazyContext->lazyValues as $k => $v) { + switch (++$i) { + case 0: + $this->assertEquals('k1', $k); + $this->assertInstanceOf('stdCLass', $v); + break; + case 1: + $this->assertEquals('k2', $k); + $this->assertInstanceOf('Symfony_DI_PhpDumper_Test_Lazy_Argument_Provide_Generator', $v); + break; + } + } + + $this->assertEmpty(iterator_to_array($lazyContext->lazyEmptyValues)); + } + + public function testNormalizedId() + { + $container = include self::$fixturesPath.'/containers/container33.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services33.php', $dumper->dump()); + } + + public function testDumpContainerBuilderWithFrozenConstructorIncludingPrivateServices() + { + $container = new ContainerBuilder(); + $container->register('foo_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true); + $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true); + $container->register('baz_service', 'stdClass')->setPublic(false); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_frozen.php', $dumper->dump()); + } + + public function testServiceLocator() + { + $container = new ContainerBuilder(); + $container->register('foo_service', ServiceLocator::class) + ->setPublic(true) + ->addArgument(array( + 'bar' => new ServiceClosureArgument(new Reference('bar_service')), + 'baz' => new ServiceClosureArgument(new TypedReference('baz_service', 'stdClass')), + 'nil' => $nil = new ServiceClosureArgument(new Reference('nil')), + )) + ; + + // no method calls + $container->register('translator.loader_1', 'stdClass')->setPublic(true); + $container->register('translator.loader_1_locator', ServiceLocator::class) + ->setPublic(false) + ->addArgument(array( + 'translator.loader_1' => new ServiceClosureArgument(new Reference('translator.loader_1')), + )); + $container->register('translator_1', StubbedTranslator::class) + ->setPublic(true) + ->addArgument(new Reference('translator.loader_1_locator')); + + // one method calls + $container->register('translator.loader_2', 'stdClass')->setPublic(true); + $container->register('translator.loader_2_locator', ServiceLocator::class) + ->setPublic(false) + ->addArgument(array( + 'translator.loader_2' => new ServiceClosureArgument(new Reference('translator.loader_2')), + )); + $container->register('translator_2', StubbedTranslator::class) + ->setPublic(true) + ->addArgument(new Reference('translator.loader_2_locator')) + ->addMethodCall('addResource', array('db', new Reference('translator.loader_2'), 'nl')); + + // two method calls + $container->register('translator.loader_3', 'stdClass')->setPublic(true); + $container->register('translator.loader_3_locator', ServiceLocator::class) + ->setPublic(false) + ->addArgument(array( + 'translator.loader_3' => new ServiceClosureArgument(new Reference('translator.loader_3')), + )); + $container->register('translator_3', StubbedTranslator::class) + ->setPublic(true) + ->addArgument(new Reference('translator.loader_3_locator')) + ->addMethodCall('addResource', array('db', new Reference('translator.loader_3'), 'nl')) + ->addMethodCall('addResource', array('db', new Reference('translator.loader_3'), 'en')); + + $nil->setValues(array(null)); + $container->register('bar_service', 'stdClass')->setArguments(array(new Reference('baz_service')))->setPublic(true); + $container->register('baz_service', 'stdClass')->setPublic(false); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_locator.php', $dumper->dump()); + } + + public function testServiceSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo_service', TestServiceSubscriber::class) + ->setPublic(true) + ->setAutowired(true) + ->addArgument(new Reference(ContainerInterface::class)) + ->addTag('container.service_subscriber', array( + 'key' => 'bar', + 'id' => TestServiceSubscriber::class, + )) + ; + $container->register(TestServiceSubscriber::class, TestServiceSubscriber::class)->setPublic(true); + + $container->register(CustomDefinition::class, CustomDefinition::class) + ->setPublic(false); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_subscriber.php', $dumper->dump()); + } + + public function testPrivateWithIgnoreOnInvalidReference() + { + require_once self::$fixturesPath.'/includes/classes.php'; + + $container = new ContainerBuilder(); + $container->register('not_invalid', 'BazClass') + ->setPublic(false); + $container->register('bar', 'BarClass') + ->setPublic(true) + ->addMethodCall('setBaz', array(new Reference('not_invalid', SymfonyContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference'))); + + $container = new \Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference(); + $this->assertInstanceOf('BazClass', $container->get('bar')->getBaz()); + } + + public function testArrayParameters() + { + $container = new ContainerBuilder(); + $container->setParameter('array_1', array(123)); + $container->setParameter('array_2', array(__DIR__)); + $container->register('bar', 'BarClass') + ->setPublic(true) + ->addMethodCall('setBaz', array('%array_1%', '%array_2%', '%%array_1%%', array(123))); + $container->compile(); + + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_array_params.php', str_replace('\\\\Dumper', '/Dumper', $dumper->dump(array('file' => self::$fixturesPath.'/php/services_array_params.php')))); + } + + public function testExpressionReferencingPrivateService() + { + $container = new ContainerBuilder(); + $container->register('private_bar', 'stdClass') + ->setPublic(false); + $container->register('private_foo', 'stdClass') + ->setPublic(false); + $container->register('public_foo', 'stdClass') + ->setPublic(true) + ->addArgument(new Expression('service("private_foo")')); + + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_private_in_expression.php', $dumper->dump()); + } + + public function testUninitializedReference() + { + $container = include self::$fixturesPath.'/containers/container_uninitialized_ref.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_uninitialized_ref.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Uninitialized_Reference'))); + + require self::$fixturesPath.'/php/services_uninitialized_ref.php'; + + $container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference(); + + $bar = $container->get('bar'); + + $this->assertNull($bar->foo1); + $this->assertNull($bar->foo2); + $this->assertNull($bar->foo3); + $this->assertNull($bar->closures[0]()); + $this->assertNull($bar->closures[1]()); + $this->assertNull($bar->closures[2]()); + $this->assertSame(array(), iterator_to_array($bar->iter)); + + $container = new \Symfony_DI_PhpDumper_Test_Uninitialized_Reference(); + + $container->get('foo1'); + $container->get('baz'); + + $bar = $container->get('bar'); + + $this->assertEquals(new \stdClass(), $bar->foo1); + $this->assertNull($bar->foo2); + $this->assertEquals(new \stdClass(), $bar->foo3); + $this->assertEquals(new \stdClass(), $bar->closures[0]()); + $this->assertNull($bar->closures[1]()); + $this->assertEquals(new \stdClass(), $bar->closures[2]()); + $this->assertEquals(array('foo1' => new \stdClass(), 'foo3' => new \stdClass()), iterator_to_array($bar->iter)); + } + + /** + * @dataProvider provideAlmostCircular + */ + public function testAlmostCircular($visibility) + { + $container = include self::$fixturesPath.'/containers/container_almost_circular.php'; + $container->compile(); + $dumper = new PhpDumper($container); + + $container = 'Symfony_DI_PhpDumper_Test_Almost_Circular_'.ucfirst($visibility); + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php', $dumper->dump(array('class' => $container))); + + require self::$fixturesPath.'/php/services_almost_circular_'.$visibility.'.php'; + + $container = new $container(); + + $foo = $container->get('foo'); + $this->assertSame($foo, $foo->bar->foobar->foo); + + $foo2 = $container->get('foo2'); + $this->assertSame($foo2, $foo2->bar->foobar->foo); + + $this->assertSame(array(), (array) $container->get('foobar4')); + + $foo5 = $container->get('foo5'); + $this->assertSame($foo5, $foo5->bar->foo); + } + + public function provideAlmostCircular() + { + yield array('public'); + yield array('private'); + } + + public function testHotPathOptimizations() + { + $container = include self::$fixturesPath.'/containers/container_inline_requires.php'; + $container->setParameter('inline_requires', true); + $container->compile(); + $dumper = new PhpDumper($container); + + $dump = $dumper->dump(array('hot_path_tag' => 'container.hot_path', 'inline_class_loader_parameter' => 'inline_requires', 'file' => self::$fixturesPath.'/php/services_inline_requires.php')); + if ('\\' === \DIRECTORY_SEPARATOR) { + $dump = str_replace("'\\\\includes\\\\HotPath\\\\", "'/includes/HotPath/", $dump); + } + + $this->assertStringEqualsFile(self::$fixturesPath.'/php/services_inline_requires.php', $dump); + } + + public function testDumpHandlesLiteralClassWithRootNamespace() + { + $container = new ContainerBuilder(); + $container->register('foo', '\\stdClass')->setPublic(true); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace'))); + + $container = new \Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace(); + + $this->assertInstanceOf('stdClass', $container->get('foo')); + } + + public function testDumpHandlesObjectClassNames() + { + $container = new ContainerBuilder(new ParameterBag(array( + 'class' => 'stdClass', + ))); + + $container->setDefinition('foo', new Definition(new Parameter('class'))); + $container->setDefinition('bar', new Definition('stdClass', array( + new Reference('foo'), + )))->setPublic(true); + + $container->setParameter('inline_requires', true); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array( + 'class' => 'Symfony_DI_PhpDumper_Test_Object_Class_Name', + 'inline_class_loader_parameter' => 'inline_requires', + ))); + + $container = new \Symfony_DI_PhpDumper_Test_Object_Class_Name(); + + $this->assertInstanceOf('stdClass', $container->get('bar')); + } + + public function testUninitializedSyntheticReference() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setPublic(true)->setSynthetic(true); + $container->register('bar', 'stdClass')->setPublic(true)->setShared(false) + ->setProperty('foo', new Reference('foo', ContainerBuilder::IGNORE_ON_UNINITIALIZED_REFERENCE)); + + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array( + 'class' => 'Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference', + 'inline_class_loader_parameter' => 'inline_requires', + ))); + + $container = new \Symfony_DI_PhpDumper_Test_UninitializedSyntheticReference(); + + $this->assertEquals((object) array('foo' => null), $container->get('bar')); + + $container->set('foo', (object) array(123)); + $this->assertEquals((object) array('foo' => (object) array(123)), $container->get('bar')); + } + + /** + * This test checks the trigger of a deprecation note and should not be removed in major releases. + * + * @group legacy + * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed. + */ + public function testPrivateServiceTriggersDeprecation() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass') + ->setPublic(false) + ->setDeprecated(true); + $container->register('bar', 'stdClass') + ->setPublic(true) + ->setProperty('foo', new Reference('foo')); + + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation'))); + + $container = new \Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation(); + + $container->get('bar'); + } + + public function testParameterWithMixedCase() + { + $container = new ContainerBuilder(new ParameterBag(array('Foo' => 'bar', 'BAR' => 'foo'))); + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case'))); + + $container = new \Symfony_DI_PhpDumper_Test_Parameter_With_Mixed_Case(); + + $this->assertSame('bar', $container->getParameter('Foo')); + $this->assertSame('foo', $container->getParameter('BAR')); + } +} + +class Rot13EnvVarProcessor implements EnvVarProcessorInterface +{ + public function getEnv($prefix, $name, \Closure $getEnv) + { + return str_rot13($getEnv($name)); + } + + public static function getProvidedTypes() + { + return array('rot13' => 'string'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.php b/vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.php new file mode 100644 index 0000000..f33b3b8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Dumper/XmlDumperTest.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\DependencyInjection\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Dumper\XmlDumper; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Reference; + +class XmlDumperTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + public function testDump() + { + $dumper = new XmlDumper(new ContainerBuilder()); + + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services1.xml', $dumper->dump(), '->dump() dumps an empty container as an empty XML file'); + } + + public function testExportParameters() + { + $container = include self::$fixturesPath.'//containers/container8.php'; + $dumper = new XmlDumper($container); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services8.xml', $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddParameters() + { + $container = include self::$fixturesPath.'//containers/container8.php'; + $dumper = new XmlDumper($container); + $this->assertXmlStringEqualsXmlFile(self::$fixturesPath.'/xml/services8.xml', $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddService() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(str_replace('%path%', self::$fixturesPath.\DIRECTORY_SEPARATOR.'includes'.\DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/xml/services9.xml')), $dumper->dump(), '->dump() dumps services'); + + $dumper = new XmlDumper($container = new ContainerBuilder()); + $container->register('foo', 'FooClass')->addArgument(new \stdClass())->setPublic(true); + try { + $dumper->dump(); + $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } + } + + public function testDumpAnonymousServices() + { + $container = include self::$fixturesPath.'/containers/container11.php'; + $dumper = new XmlDumper($container); + $this->assertEquals(' + + + + + + + + + + + + + + + + +', $dumper->dump()); + } + + public function testDumpEntities() + { + $container = include self::$fixturesPath.'/containers/container12.php'; + $dumper = new XmlDumper($container); + $this->assertEquals(" + + + + + + foo<>&bar + + + + + +", $dumper->dump()); + } + + /** + * @dataProvider provideDecoratedServicesData + */ + public function testDumpDecoratedServices($expectedXmlDump, $container) + { + $dumper = new XmlDumper($container); + $this->assertEquals($expectedXmlDump, $dumper->dump()); + } + + public function provideDecoratedServicesData() + { + $fixturesPath = realpath(__DIR__.'/../Fixtures/'); + + return array( + array(" + + + + + + + + +", include $fixturesPath.'/containers/container15.php'), + array(" + + + + + + + + +", include $fixturesPath.'/containers/container16.php'), + ); + } + + /** + * @dataProvider provideCompiledContainerData + */ + public function testCompiledContainerCanBeDumped($containerFile) + { + $fixturesPath = __DIR__.'/../Fixtures'; + $container = require $fixturesPath.'/containers/'.$containerFile.'.php'; + $container->compile(); + $dumper = new XmlDumper($container); + $dumper->dump(); + + $this->addToAssertionCount(1); + } + + public function provideCompiledContainerData() + { + return array( + array('container8'), + array('container9'), + array('container11'), + array('container12'), + array('container14'), + ); + } + + public function testDumpInlinedServices() + { + $container = include self::$fixturesPath.'/containers/container21.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services21.xml'), $dumper->dump()); + } + + public function testDumpAutowireData() + { + $container = include self::$fixturesPath.'/containers/container24.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services24.xml'), $dumper->dump()); + } + + public function testDumpLoad() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_dump_load.xml'); + + $this->assertEquals(array(new Reference('bar', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)), $container->getDefinition('foo')->getArguments()); + + $dumper = new XmlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/xml/services_dump_load.xml', $dumper->dump()); + } + + public function testDumpAbstractServices() + { + $container = include self::$fixturesPath.'/containers/container_abstract.php'; + $dumper = new XmlDumper($container); + + $this->assertEquals(file_get_contents(self::$fixturesPath.'/xml/services_abstract.xml'), $dumper->dump()); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.php b/vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.php new file mode 100644 index 0000000..ff2239f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Dumper/YamlDumperTest.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\DependencyInjection\Tests\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Dumper\YamlDumper; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Yaml\Parser; +use Symfony\Component\Yaml\Yaml; + +class YamlDumperTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + public function testDump() + { + $dumper = new YamlDumper($container = new ContainerBuilder()); + + $this->assertEqualYamlStructure(file_get_contents(self::$fixturesPath.'/yaml/services1.yml'), $dumper->dump(), '->dump() dumps an empty container as an empty YAML file'); + } + + public function testAddParameters() + { + $container = include self::$fixturesPath.'/containers/container8.php'; + $dumper = new YamlDumper($container); + $this->assertEqualYamlStructure(file_get_contents(self::$fixturesPath.'/yaml/services8.yml'), $dumper->dump(), '->dump() dumps parameters'); + } + + public function testAddService() + { + $container = include self::$fixturesPath.'/containers/container9.php'; + $dumper = new YamlDumper($container); + $this->assertEqualYamlStructure(str_replace('%path%', self::$fixturesPath.\DIRECTORY_SEPARATOR.'includes'.\DIRECTORY_SEPARATOR, file_get_contents(self::$fixturesPath.'/yaml/services9.yml')), $dumper->dump(), '->dump() dumps services'); + + $dumper = new YamlDumper($container = new ContainerBuilder()); + $container->register('foo', 'FooClass')->addArgument(new \stdClass())->setPublic(true); + try { + $dumper->dump(); + $this->fail('->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } catch (\Exception $e) { + $this->assertInstanceOf('\RuntimeException', $e, '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + $this->assertEquals('Unable to dump a service container if a parameter is an object or a resource.', $e->getMessage(), '->dump() throws a RuntimeException if the container to be dumped has reference to objects or resources'); + } + } + + public function testDumpAutowireData() + { + $container = include self::$fixturesPath.'/containers/container24.php'; + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services24.yml', $dumper->dump()); + } + + public function testDumpLoad() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_dump_load.yml'); + + $this->assertEquals(array(new Reference('bar', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)), $container->getDefinition('foo')->getArguments()); + + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_dump_load.yml', $dumper->dump()); + } + + public function testInlineServices() + { + $container = new ContainerBuilder(); + $container->register('foo', 'Class1') + ->setPublic(true) + ->addArgument((new Definition('Class2')) + ->addArgument(new Definition('Class2')) + ) + ; + + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile(self::$fixturesPath.'/yaml/services_inline.yml', $dumper->dump()); + } + + private function assertEqualYamlStructure($expected, $yaml, $message = '') + { + $parser = new Parser(); + + $this->assertEquals($parser->parse($expected, Yaml::PARSE_CUSTOM_TAGS), $parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS), $message); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/EnvVarProcessorTest.php b/vendor/symfony/dependency-injection/Tests/EnvVarProcessorTest.php new file mode 100644 index 0000000..b1ee044 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/EnvVarProcessorTest.php @@ -0,0 +1,304 @@ +setParameter('env(foo)', $value); + $container->compile(); + + $processor = new EnvVarProcessor($container); + + $result = $processor->getEnv('string', 'foo', function () { + $this->fail('Should not be called'); + }); + + $this->assertSame($processed, $result); + } + + public function validStrings() + { + return array( + array('hello', 'hello'), + array('true', 'true'), + array('false', 'false'), + array('null', 'null'), + array('1', '1'), + array('0', '0'), + array('1.1', '1.1'), + array('1e1', '1e1'), + ); + } + + /** + * @dataProvider validBools + */ + public function testGetEnvBool($value, $processed) + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('bool', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + + $this->assertSame($processed, $result); + } + + public function validBools() + { + return array( + array('true', true), + array('false', false), + array('null', false), + array('1', true), + array('0', false), + array('1.1', true), + array('1e1', true), + ); + } + + /** + * @dataProvider validInts + */ + public function testGetEnvInt($value, $processed) + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('int', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + + $this->assertSame($processed, $result); + } + + public function validInts() + { + return array( + array('1', 1), + array('1.1', 1), + array('1e1', 10), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Non-numeric env var + * @dataProvider invalidInts + */ + public function testGetEnvIntInvalid($value) + { + $processor = new EnvVarProcessor(new Container()); + + $processor->getEnv('int', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + } + + public function invalidInts() + { + return array( + array('foo'), + array('true'), + array('null'), + ); + } + + /** + * @dataProvider validFloats + */ + public function testGetEnvFloat($value, $processed) + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('float', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + + $this->assertSame($processed, $result); + } + + public function validFloats() + { + return array( + array('1', 1.0), + array('1.1', 1.1), + array('1e1', 10.0), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Non-numeric env var + * @dataProvider invalidFloats + */ + public function testGetEnvFloatInvalid($value) + { + $processor = new EnvVarProcessor(new Container()); + + $processor->getEnv('float', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + } + + public function invalidFloats() + { + return array( + array('foo'), + array('true'), + array('null'), + ); + } + + /** + * @dataProvider validConsts + */ + public function testGetEnvConst($value, $processed) + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('const', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + + $this->assertSame($processed, $result); + } + + public function validConsts() + { + return array( + array('Symfony\Component\DependencyInjection\Tests\EnvVarProcessorTest::TEST_CONST', self::TEST_CONST), + array('E_ERROR', E_ERROR), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage undefined constant + * @dataProvider invalidConsts + */ + public function testGetEnvConstInvalid($value) + { + $processor = new EnvVarProcessor(new Container()); + + $processor->getEnv('const', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return $value; + }); + } + + public function invalidConsts() + { + return array( + array('Symfony\Component\DependencyInjection\Tests\EnvVarProcessorTest::UNDEFINED_CONST'), + array('UNDEFINED_CONST'), + ); + } + + public function testGetEnvBase64() + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('base64', 'foo', function ($name) { + $this->assertSame('foo', $name); + + return base64_encode('hello'); + }); + + $this->assertSame('hello', $result); + } + + public function testGetEnvJson() + { + $processor = new EnvVarProcessor(new Container()); + + $result = $processor->getEnv('json', 'foo', function ($name) { + $this->assertSame('foo', $name); + + return json_encode(array(1)); + }); + + $this->assertSame(array(1), $result); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Syntax error + */ + public function testGetEnvInvalidJson() + { + $processor = new EnvVarProcessor(new Container()); + + $processor->getEnv('json', 'foo', function ($name) { + $this->assertSame('foo', $name); + + return 'invalid_json'; + }); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Invalid JSON env var + * @dataProvider otherJsonValues + */ + public function testGetEnvJsonOther($value) + { + $processor = new EnvVarProcessor(new Container()); + + $processor->getEnv('json', 'foo', function ($name) use ($value) { + $this->assertSame('foo', $name); + + return json_encode($value); + }); + } + + public function otherJsonValues() + { + return array( + array(1), + array(1.1), + array(true), + array(false), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Unsupported env var prefix + */ + public function testGetEnvUnknown() + { + $processor = new EnvVarProcessor(new Container()); + + $processor->getEnv('unknown', 'foo', function ($name) { + $this->assertSame('foo', $name); + + return 'foo'; + }); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.php b/vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.php new file mode 100644 index 0000000..9f66bfd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Extension/ExtensionTest.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\DependencyInjection\Tests\Extension; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; + +class ExtensionTest extends TestCase +{ + /** + * @dataProvider getResolvedEnabledFixtures + */ + public function testIsConfigEnabledReturnsTheResolvedValue($enabled) + { + $extension = new EnableableExtension(); + $this->assertSame($enabled, $extension->isConfigEnabled(new ContainerBuilder(), array('enabled' => $enabled))); + } + + public function getResolvedEnabledFixtures() + { + return array( + array(true), + array(false), + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The config array has no 'enabled' key. + */ + public function testIsConfigEnabledOnNonEnableableConfig() + { + $extension = new EnableableExtension(); + + $extension->isConfigEnabled(new ContainerBuilder(), array()); + } +} + +class EnableableExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + } + + public function isConfigEnabled(ContainerBuilder $container, array $config) + { + return parent::isConfigEnabled($container, $config); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/Bar.php b/vendor/symfony/dependency-injection/Tests/Fixtures/Bar.php new file mode 100644 index 0000000..d243866 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/Bar.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\DependencyInjection\Tests\Fixtures; + +class Bar implements BarInterface +{ + public function __construct($quz = null, \NonExistent $nonExistent = null, BarInterface $decorated = null, array $foo = array()) + { + } + + public static function create(\NonExistent $nonExistent = null, $factory = null) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/BarInterface.php b/vendor/symfony/dependency-injection/Tests/Fixtures/BarInterface.php new file mode 100644 index 0000000..dc81f33 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/BarInterface.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\DependencyInjection\Tests\Fixtures; + +interface BarInterface +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/CaseSensitiveClass.php b/vendor/symfony/dependency-injection/Tests/Fixtures/CaseSensitiveClass.php new file mode 100644 index 0000000..7c6f0ff --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/CaseSensitiveClass.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\DependencyInjection\Tests\Fixtures; + +class CaseSensitiveClass +{ + public $identifier; + + public function __construct($identifier = null) + { + $this->identifier = $identifier; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/Container/ConstructorWithMandatoryArgumentsContainer.php b/vendor/symfony/dependency-injection/Tests/Fixtures/Container/ConstructorWithMandatoryArgumentsContainer.php new file mode 100644 index 0000000..ba55fb7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/Container/ConstructorWithMandatoryArgumentsContainer.php @@ -0,0 +1,10 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Fixtures; + +use Symfony\Component\DependencyInjection\Definition; + +class CustomDefinition extends Definition +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/DeprecatedClass.php b/vendor/symfony/dependency-injection/Tests/Fixtures/DeprecatedClass.php new file mode 100644 index 0000000..33f37a0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/DeprecatedClass.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\DependencyInjection\Tests\Fixtures; + +@trigger_error('deprecated', E_USER_DEPRECATED); + +class DeprecatedClass +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.php b/vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.php new file mode 100644 index 0000000..b7ceac7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/FactoryDummy.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\DependencyInjection\Tests\Fixtures; + +class FactoryDummy extends FactoryParent +{ + public static function createFactory(): FactoryDummy + { + } + + public function create(): \stdClass + { + } + + public function createBuiltin(): int + { + } + + public static function createSelf(): self + { + } + + public static function createParent(): parent + { + } +} + +class FactoryParent +{ +} + +function factoryFunction(): FactoryDummy +{ +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/NamedArgumentsDummy.php b/vendor/symfony/dependency-injection/Tests/Fixtures/NamedArgumentsDummy.php new file mode 100644 index 0000000..09d907d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/NamedArgumentsDummy.php @@ -0,0 +1,21 @@ + + */ +class NamedArgumentsDummy +{ + public function __construct(CaseSensitiveClass $c, $apiKey, $hostName) + { + } + + public function setApiKey($apiKey) + { + } + + public function setSensitiveClass(CaseSensitiveClass $c) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ParentNotExists.php b/vendor/symfony/dependency-injection/Tests/Fixtures/ParentNotExists.php new file mode 100644 index 0000000..ae637f9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ParentNotExists.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\DependencyInjection\Tests\Fixtures; + +class SimilarArgumentsDummy +{ + public $class1; + public $class2; + + public function __construct(CaseSensitiveClass $class1, $token, CaseSensitiveClass $class2) + { + $this->class1 = $class1; + $this->class2 = $class2; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/StubbedTranslator.php b/vendor/symfony/dependency-injection/Tests/Fixtures/StubbedTranslator.php new file mode 100644 index 0000000..8e1c2a6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/StubbedTranslator.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\DependencyInjection\Tests\Fixtures; + +use Psr\Container\ContainerInterface; + +/** + * @author Iltar van der Berg + */ +class StubbedTranslator +{ + public function __construct(ContainerInterface $container) + { + + } + + public function addResource($format, $resource, $locale, $domain = null) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/TestServiceSubscriber.php b/vendor/symfony/dependency-injection/Tests/Fixtures/TestServiceSubscriber.php new file mode 100644 index 0000000..875abe9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/TestServiceSubscriber.php @@ -0,0 +1,22 @@ + CustomDefinition::class, + 'baz' => '?'.CustomDefinition::class, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/array.json b/vendor/symfony/dependency-injection/Tests/Fixtures/array.json new file mode 100644 index 0000000..dc27f0f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/array.json @@ -0,0 +1 @@ +[123, "abc"] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.expected.yml new file mode 100644 index 0000000..1137961 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.expected.yml @@ -0,0 +1,10 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + App\BarService: + class: App\BarService + public: true + arguments: [!service { class: FooClass }] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.php new file mode 100644 index 0000000..b98e894 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/basic.php @@ -0,0 +1,11 @@ +services(); + $s->set(BarService::class) + ->args(array(inline('FooClass'))); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/child.expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/config/child.expected.yml new file mode 100644 index 0000000..aaab713 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/child.expected.yml @@ -0,0 +1,15 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + foo: + class: Class2 + public: true + file: file.php + lazy: true + arguments: [!service { class: Class1, public: false }] + bar: + alias: foo + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/child.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/child.php new file mode 100644 index 0000000..6fd8448 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/child.php @@ -0,0 +1,22 @@ +services() + ->set('bar', 'Class1') + ->set(BarService::class) + ->abstract(true) + ->lazy() + ->set('foo') + ->parent(BarService::class) + ->decorate('bar', 'b', 1) + ->args(array(ref('b'))) + ->class('Class2') + ->file('file.php') + ->parent('bar') + ->parent(BarService::class) + ; +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.expected.yml new file mode 100644 index 0000000..a534f72 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.expected.yml @@ -0,0 +1,27 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + App\BarService: + class: App\BarService + public: true + arguments: [!service { class: FooClass }] + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + tags: + - { name: t, a: b } + autowire: true + autoconfigure: true + arguments: ['@bar'] + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: false + tags: + - { name: t, a: b } + autowire: true + calls: + - [setFoo, ['@bar']] + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.php new file mode 100644 index 0000000..de3b99d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/defaults.php @@ -0,0 +1,21 @@ +import('basic.php'); + + $s = $c->services()->defaults() + ->public() + ->private() + ->autoconfigure() + ->autowire() + ->tag('t', array('a' => 'b')) + ->bind(Foo::class, ref('bar')) + ->private(); + + $s->set(Foo::class)->args(array(ref('bar')))->public(); + $s->set('bar', Foo::class)->call('setFoo')->autoconfigure(false); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/factory_short_notation.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/factory_short_notation.php new file mode 100644 index 0000000..5b37793 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/factory_short_notation.php @@ -0,0 +1,9 @@ +services() + ->set('service', \stdClass::class) + ->factory('factory:method'); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.expected.yml new file mode 100644 index 0000000..b12a304 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.expected.yml @@ -0,0 +1,21 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + tags: + - { name: tag, k: v } + lazy: true + properties: { p: 1 } + calls: + - [setFoo, ['@foo']] + + shared: false + configurator: c + foo: + class: App\FooService + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.php new file mode 100644 index 0000000..062e8c0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/instanceof.php @@ -0,0 +1,22 @@ +services(); + $s->instanceof(Prototype\Foo::class) + ->property('p', 0) + ->call('setFoo', array(ref('foo'))) + ->tag('tag', array('k' => 'v')) + ->share(false) + ->lazy() + ->configurator('c') + ->property('p', 1); + + $s->load(Prototype::class.'\\', '../Prototype')->exclude('../Prototype/*/*'); + + $s->set('foo', FooService::class); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.expected.yml new file mode 100644 index 0000000..7c5b714 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.expected.yml @@ -0,0 +1,19 @@ +parameters: + foo: Foo + bar: Bar + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + arguments: ['@bar'] + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + calls: + - [setFoo, { }] + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.php new file mode 100644 index 0000000..7711624 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/php7.php @@ -0,0 +1,19 @@ +parameters() + ('foo', 'Foo') + ('bar', 'Bar') + ; + $c->services() + (Foo::class) + ->arg('$bar', ref('bar')) + ->public() + ('bar', Foo::class) + ->call('setFoo') + ; +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.expected.yml new file mode 100644 index 0000000..ebfe087 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.expected.yml @@ -0,0 +1,25 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo + public: true + tags: + - { name: foo } + - { name: baz } + deprecated: '%service_id%' + arguments: [1] + factory: f + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar + public: true + tags: + - { name: foo } + - { name: baz } + deprecated: '%service_id%' + lazy: true + arguments: [1] + factory: f diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.php new file mode 100644 index 0000000..622c51a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/prototype.php @@ -0,0 +1,22 @@ +services()->defaults() + ->tag('baz'); + $di->load(Prototype::class.'\\', '../Prototype') + ->autoconfigure() + ->exclude('../Prototype/{OtherDir,BadClasses}') + ->factory('f') + ->deprecate('%service_id%') + ->args(array(0)) + ->args(array(1)) + ->autoconfigure(false) + ->tag('foo') + ->parent('foo'); + $di->set('foo')->lazy()->abstract(); + $di->get(Prototype\Foo::class)->lazy(false); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/services9.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/services9.php new file mode 100644 index 0000000..4bf3b89 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/services9.php @@ -0,0 +1,133 @@ +parameters(); + $p->set('baz_class', 'BazClass'); + $p->set('foo_class', FooClass::class) + ->set('foo', 'bar'); + + $s = $c->services()->defaults() + ->public(); + $s->set('foo') + ->args(array('foo', ref('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, ref('service_container'))) + ->class(FooClass::class) + ->tag('foo', array('foo' => 'foo')) + ->tag('foo', array('bar' => 'bar', 'baz' => 'baz')) + ->factory(array(FooClass::class, 'getInstance')) + ->property('foo', 'bar') + ->property('moo', ref('foo.baz')) + ->property('qux', array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%')) + ->call('setBar', array(ref('bar'))) + ->call('initialize') + ->configurator('sc_configure'); + + $s->set('foo.baz', '%baz_class%') + ->factory(array('%baz_class%', 'getInstance')) + ->configurator(array('%baz_class%', 'configureStatic1')); + + $s->set('bar', FooClass::class) + ->args(array('foo', ref('foo.baz'), new Parameter('foo_bar'))) + ->configurator(array(ref('foo.baz'), 'configure')); + + $s->set('foo_bar', '%foo_class%') + ->args(array(ref('deprecated_service'))) + ->share(false); + + $s->set('method_call1', 'Bar\FooClass') + ->file(realpath(__DIR__.'/../includes/foo.php')) + ->call('setBar', array(ref('foo'))) + ->call('setBar', array(ref('foo2')->nullOnInvalid())) + ->call('setBar', array(ref('foo3')->ignoreOnInvalid())) + ->call('setBar', array(ref('foobaz')->ignoreOnInvalid())) + ->call('setBar', array(expr('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))); + + $s->set('foo_with_inline', 'Foo') + ->call('setBar', array(ref('inlined'))); + + $s->set('inlined', 'Bar') + ->property('pub', 'pub') + ->call('setBaz', array(ref('baz'))) + ->private(); + + $s->set('baz', 'Baz') + ->call('setFoo', array(ref('foo_with_inline'))); + + $s->set('request', 'Request') + ->synthetic(); + + $s->set('configurator_service', 'ConfClass') + ->private() + ->call('setFoo', array(ref('baz'))); + + $s->set('configured_service', 'stdClass') + ->configurator(array(ref('configurator_service'), 'configureStdClass')); + + $s->set('configurator_service_simple', 'ConfClass') + ->args(array('bar')) + ->private(); + + $s->set('configured_service_simple', 'stdClass') + ->configurator(array(ref('configurator_service_simple'), 'configureStdClass')); + + $s->set('decorated', 'stdClass'); + + $s->set('decorator_service', 'stdClass') + ->decorate('decorated'); + + $s->set('decorator_service_with_name', 'stdClass') + ->decorate('decorated', 'decorated.pif-pouf'); + + $s->set('deprecated_service', 'stdClass') + ->deprecate(); + + $s->set('new_factory', 'FactoryClass') + ->property('foo', 'bar') + ->private(); + + $s->set('factory_service', 'Bar') + ->factory(array(ref('foo.baz'), 'getInstance')); + + $s->set('new_factory_service', 'FooBarBaz') + ->property('foo', 'bar') + ->factory(array(ref('new_factory'), 'getInstance')); + + $s->set('service_from_static_method', 'Bar\FooClass') + ->factory(array('Bar\FooClass', 'getInstance')); + + $s->set('factory_simple', 'SimpleFactoryClass') + ->deprecate() + ->args(array('foo')) + ->private(); + + $s->set('factory_service_simple', 'Bar') + ->factory(array(ref('factory_simple'), 'getInstance')); + + $s->set('lazy_context', 'LazyContext') + ->args(array(iterator(array('k1' => ref('foo.baz'), 'k2' => ref('service_container'))), iterator(array()))); + + $s->set('lazy_context_ignore_invalid_ref', 'LazyContext') + ->args(array(iterator(array(ref('foo.baz'), ref('invalid')->ignoreOnInvalid())), iterator(array()))); + + $s->set('BAR', 'stdClass')->property('bar', ref('bar')); + $s->set('bar2', 'stdClass'); + $s->set('BAR2', 'stdClass'); + + $s->set('tagged_iterator_foo', 'Bar') + ->private() + ->tag('foo'); + + $s->set('tagged_iterator', 'Bar') + ->public() + ->args(array(tagged('foo'))); + + $s->alias('alias_for_foo', 'foo')->private()->public(); + $s->alias('alias_for_alias', ref('alias_for_foo')); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/config/services_autoconfigure_with_parent.php b/vendor/symfony/dependency-injection/Tests/Fixtures/config/services_autoconfigure_with_parent.php new file mode 100644 index 0000000..f8ffb1d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/config/services_autoconfigure_with_parent.php @@ -0,0 +1,9 @@ +services() + ->set('parent_service', \stdClass::class) + ->set('child_service')->parent('parent_service')->autoconfigure(true); +}; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php new file mode 100644 index 0000000..2251435 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/CustomContainer.php @@ -0,0 +1,17 @@ + + register('foo', 'FooClass')-> + addArgument(new Reference('bar')) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php new file mode 100644 index 0000000..150cd79 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container11.php @@ -0,0 +1,13 @@ + + register('foo', 'FooClass')-> + addArgument(new Definition('BarClass', array(new Definition('BazClass')))) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php new file mode 100644 index 0000000..bc527eb --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container12.php @@ -0,0 +1,13 @@ + + register('foo', 'FooClass\\Foo')-> + addArgument('foo<>&bar')-> + addTag('foo"bar\\bar', array('foo' => 'foo"barřž€')) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php new file mode 100644 index 0000000..df598d4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container13.php @@ -0,0 +1,18 @@ + + register('foo', 'FooClass')-> + addArgument(new Reference('bar')) + ->setPublic(true) +; +$container-> + register('bar', 'BarClass') + ->setPublic(true) +; +$container->compile(); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php new file mode 100644 index 0000000..191afb8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container14.php @@ -0,0 +1,17 @@ +register('foo', 'FooClass\\Foo') + ->setDecoratedService('bar', 'bar.woozy') + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php new file mode 100644 index 0000000..88619ec --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container16.php @@ -0,0 +1,12 @@ +register('foo', 'FooClass\\Foo') + ->setDecoratedService('bar') + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php new file mode 100644 index 0000000..7f1db67 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container17.php @@ -0,0 +1,11 @@ +register('foo', '%foo.class%') + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php new file mode 100644 index 0000000..823a77f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container19.php @@ -0,0 +1,24 @@ +register('service_from_anonymous_factory', 'Bar\FooClass') + ->setFactory(array(new Definition('Bar\FooClass'), 'getInstance')) + ->setPublic(true) +; + +$anonymousServiceWithFactory = new Definition('Bar\FooClass'); +$anonymousServiceWithFactory->setFactory('Bar\FooClass::getInstance'); +$container + ->register('service_with_method_call_and_factory', 'Bar\FooClass') + ->addMethodCall('setBar', array($anonymousServiceWithFactory)) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php new file mode 100644 index 0000000..298c926 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container21.php @@ -0,0 +1,21 @@ +setConfigurator(array(new Definition('Baz'), 'configureBar')); + +$fooFactory = new Definition('FooFactory'); +$fooFactory->setFactory(array(new Definition('Foobar'), 'createFooFactory')); + +$container + ->register('foo', 'Foo') + ->setFactory(array($fooFactory, 'createFoo')) + ->setConfigurator(array($bar, 'configureFoo')) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php new file mode 100644 index 0000000..b9d0b91 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container24.php @@ -0,0 +1,13 @@ +register('foo', 'Foo') + ->setAutowired(true) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container33.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container33.php new file mode 100644 index 0000000..673abe2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container33.php @@ -0,0 +1,12 @@ +register(\Foo\Foo::class)->setPublic(true); +$container->register(\Bar\Foo::class)->setPublic(true); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php new file mode 100644 index 0000000..1a4e5ab --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container8.php @@ -0,0 +1,14 @@ + '%baz%', + 'baz' => 'bar', + 'bar' => 'foo is %%foo bar', + 'escape' => '@escapeme', + 'values' => array(true, false, null, 0, 1000.3, 'true', 'false', 'null'), +))); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php new file mode 100644 index 0000000..06789bd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container9.php @@ -0,0 +1,183 @@ +register('foo', '\Bar\FooClass') + ->addTag('foo', array('foo' => 'foo')) + ->addTag('foo', array('bar' => 'bar', 'baz' => 'baz')) + ->setFactory(array('Bar\\FooClass', 'getInstance')) + ->setArguments(array('foo', new Reference('foo.baz'), array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'), true, new Reference('service_container'))) + ->setProperties(array('foo' => 'bar', 'moo' => new Reference('foo.baz'), 'qux' => array('%foo%' => 'foo is %foo%', 'foobar' => '%foo%'))) + ->addMethodCall('setBar', array(new Reference('bar'))) + ->addMethodCall('initialize') + ->setConfigurator('sc_configure') + ->setPublic(true) +; +$container + ->register('foo.baz', '%baz_class%') + ->setFactory(array('%baz_class%', 'getInstance')) + ->setConfigurator(array('%baz_class%', 'configureStatic1')) + ->setPublic(true) +; +$container + ->register('bar', 'Bar\FooClass') + ->setArguments(array('foo', new Reference('foo.baz'), new Parameter('foo_bar'))) + ->setConfigurator(array(new Reference('foo.baz'), 'configure')) + ->setPublic(true) +; +$container + ->register('foo_bar', '%foo_class%') + ->addArgument(new Reference('deprecated_service')) + ->setShared(false) + ->setPublic(true) +; +$container->getParameterBag()->clear(); +$container->getParameterBag()->add(array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\FooClass', + 'foo' => 'bar', +)); +$container + ->register('method_call1', 'Bar\FooClass') + ->setFile(realpath(__DIR__.'/../includes/foo.php')) + ->addMethodCall('setBar', array(new Reference('foo'))) + ->addMethodCall('setBar', array(new Reference('foo2', ContainerInterface::NULL_ON_INVALID_REFERENCE))) + ->addMethodCall('setBar', array(new Reference('foo3', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) + ->addMethodCall('setBar', array(new Reference('foobaz', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))) + ->addMethodCall('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'))) + ->setPublic(true) +; +$container + ->register('foo_with_inline', 'Foo') + ->addMethodCall('setBar', array(new Reference('inlined'))) + ->setPublic(true) +; +$container + ->register('inlined', 'Bar') + ->setProperty('pub', 'pub') + ->addMethodCall('setBaz', array(new Reference('baz'))) + ->setPublic(false) +; +$container + ->register('baz', 'Baz') + ->addMethodCall('setFoo', array(new Reference('foo_with_inline'))) + ->setPublic(true) +; +$container + ->register('request', 'Request') + ->setSynthetic(true) + ->setPublic(true) +; +$container + ->register('configurator_service', 'ConfClass') + ->setPublic(false) + ->addMethodCall('setFoo', array(new Reference('baz'))) +; +$container + ->register('configured_service', 'stdClass') + ->setConfigurator(array(new Reference('configurator_service'), 'configureStdClass')) + ->setPublic(true) +; +$container + ->register('configurator_service_simple', 'ConfClass') + ->addArgument('bar') + ->setPublic(false) +; +$container + ->register('configured_service_simple', 'stdClass') + ->setConfigurator(array(new Reference('configurator_service_simple'), 'configureStdClass')) + ->setPublic(true) +; +$container + ->register('decorated', 'stdClass') + ->setPublic(true) +; +$container + ->register('decorator_service', 'stdClass') + ->setDecoratedService('decorated') + ->setPublic(true) +; +$container + ->register('decorator_service_with_name', 'stdClass') + ->setDecoratedService('decorated', 'decorated.pif-pouf') + ->setPublic(true) +; +$container + ->register('deprecated_service', 'stdClass') + ->setDeprecated(true) + ->setPublic(true) +; +$container + ->register('new_factory', 'FactoryClass') + ->setProperty('foo', 'bar') + ->setPublic(false) +; +$container + ->register('factory_service', 'Bar') + ->setFactory(array(new Reference('foo.baz'), 'getInstance')) + ->setPublic(true) +; +$container + ->register('new_factory_service', 'FooBarBaz') + ->setProperty('foo', 'bar') + ->setFactory(array(new Reference('new_factory'), 'getInstance')) + ->setPublic(true) +; +$container + ->register('service_from_static_method', 'Bar\FooClass') + ->setFactory(array('Bar\FooClass', 'getInstance')) + ->setPublic(true) +; +$container + ->register('factory_simple', 'SimpleFactoryClass') + ->addArgument('foo') + ->setDeprecated(true) + ->setPublic(false) +; +$container + ->register('factory_service_simple', 'Bar') + ->setFactory(array(new Reference('factory_simple'), 'getInstance')) + ->setPublic(true) +; +$container + ->register('lazy_context', 'LazyContext') + ->setArguments(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))), new IteratorArgument(array()))) + ->setPublic(true) +; +$container + ->register('lazy_context_ignore_invalid_ref', 'LazyContext') + ->setArguments(array(new IteratorArgument(array(new Reference('foo.baz'), new Reference('invalid', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))), new IteratorArgument(array()))) + ->setPublic(true) +; +$container + ->register('BAR', 'stdClass') + ->setProperty('bar', new Reference('bar')) + ->setPublic(true) +; +$container->register('bar2', 'stdClass')->setPublic(true); +$container->register('BAR2', 'stdClass')->setPublic(true); +$container + ->register('tagged_iterator_foo', 'Bar') + ->addTag('foo') + ->setPublic(false) +; +$container + ->register('tagged_iterator', 'Bar') + ->addArgument(new TaggedIteratorArgument('foo')) + ->setPublic(true) +; +$container->setAlias('alias_for_foo', 'foo')->setPublic(true); +$container->setAlias('alias_for_alias', 'alias_for_foo')->setPublic(true); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_abstract.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_abstract.php new file mode 100644 index 0000000..308e225 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_abstract.php @@ -0,0 +1,13 @@ +register('foo', 'Foo') + ->setAbstract(true) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_almost_circular.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_almost_circular.php new file mode 100644 index 0000000..dff937c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_almost_circular.php @@ -0,0 +1,58 @@ +register('foo', FooCircular::class)->setPublic(true) + ->addArgument(new Reference('bar')); + +$container->register('bar', BarCircular::class)->setPublic($public) + ->addMethodCall('addFoobar', array(new Reference('foobar'))); + +$container->register('foobar', FoobarCircular::class)->setPublic($public) + ->addArgument(new Reference('foo')); + +// mixed visibility for deps + +$container->register('foo2', FooCircular::class)->setPublic(true) + ->addArgument(new Reference('bar2')); + +$container->register('bar2', BarCircular::class)->setPublic(!$public) + ->addMethodCall('addFoobar', array(new Reference('foobar2'))); + +$container->register('foobar2', FoobarCircular::class)->setPublic($public) + ->addArgument(new Reference('foo2')); + +// simple inline setter with internal reference + +$container->register('bar3', BarCircular::class)->setPublic(true) + ->addMethodCall('addFoobar', array(new Reference('foobar3'), new Reference('foobar3'))); + +$container->register('foobar3', FoobarCircular::class)->setPublic($public); + +// loop with non-shared dep + +$container->register('foo4', 'stdClass')->setPublic($public) + ->setShared(false) + ->setProperty('foobar', new Reference('foobar4')); + +$container->register('foobar4', 'stdClass')->setPublic(true) + ->addArgument(new Reference('foo4')); + +// loop on the constructor of a setter-injected dep with property + +$container->register('foo5', 'stdClass')->setPublic(true) + ->setProperty('bar', new Reference('bar5')); + +$container->register('bar5', 'stdClass')->setPublic($public) + ->addArgument(new Reference('foo5')) + ->setProperty('foo', new Reference('foo5')); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_env_in_id.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_env_in_id.php new file mode 100644 index 0000000..4699f41 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_env_in_id.php @@ -0,0 +1,22 @@ +setParameter('env(BAR)', 'bar'); + +$container->register('foo', 'stdClass')->setPublic(true) + ->addArgument(new Reference('bar_%env(BAR)%')) + ->addArgument(array('baz_%env(BAR)%' => new Reference('baz_%env(BAR)%'))); + +$container->register('bar', 'stdClass')->setPublic(true) + ->addArgument(new Reference('bar_%env(BAR)%')); + +$container->register('bar_%env(BAR)%', 'stdClass')->setPublic(false); +$container->register('baz_%env(BAR)%', 'stdClass')->setPublic(false); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_inline_requires.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_inline_requires.php new file mode 100644 index 0000000..3bbfa31 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_inline_requires.php @@ -0,0 +1,19 @@ +register(HotPath\C1::class)->addTag('container.hot_path')->setPublic(true); +$container->register(HotPath\C2::class)->addArgument(new Reference(HotPath\C3::class))->setPublic(true); +$container->register(HotPath\C3::class); +$container->register(ParentNotExists::class)->setPublic(true); + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_uninitialized_ref.php b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_uninitialized_ref.php new file mode 100644 index 0000000..7aeefb4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/containers/container_uninitialized_ref.php @@ -0,0 +1,49 @@ +register('foo1', 'stdClass') + ->setPublic(true) +; + +$container + ->register('foo2', 'stdClass') + ->setPublic(false) +; + +$container + ->register('foo3', 'stdClass') + ->setPublic(false) +; + +$container + ->register('baz', 'stdClass') + ->setProperty('foo3', new Reference('foo3')) + ->setPublic(true) +; + +$container + ->register('bar', 'stdClass') + ->setProperty('foo1', new Reference('foo1', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)) + ->setProperty('foo2', new Reference('foo2', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)) + ->setProperty('foo3', new Reference('foo3', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)) + ->setProperty('closures', array( + new ServiceClosureArgument(new Reference('foo1', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)), + new ServiceClosureArgument(new Reference('foo2', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)), + new ServiceClosureArgument(new Reference('foo3', $container::IGNORE_ON_UNINITIALIZED_REFERENCE)), + )) + ->setProperty('iter', new IteratorArgument(array( + 'foo1' => new Reference('foo1', $container::IGNORE_ON_UNINITIALIZED_REFERENCE), + 'foo2' => new Reference('foo2', $container::IGNORE_ON_UNINITIALIZED_REFERENCE), + 'foo3' => new Reference('foo3', $container::IGNORE_ON_UNINITIALIZED_REFERENCE), + ))) + ->setPublic(true) +; + +return $container; diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml new file mode 100644 index 0000000..35ec4a0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/import/import.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../recurse/ } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini new file mode 100644 index 0000000..0984cda --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.ini @@ -0,0 +1,2 @@ +[parameters] + ini = ini diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml new file mode 100644 index 0000000..f98ef12 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/recurse/simple.yml @@ -0,0 +1,2 @@ +parameters: + yaml: yaml diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php new file mode 100644 index 0000000..4750324 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/directory/simple.php @@ -0,0 +1,3 @@ +setParameter('php', 'php'); diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot new file mode 100644 index 0000000..e740108 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services1.dot @@ -0,0 +1,7 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container (Psr\Container\ContainerInterface, Symfony\Component\DependencyInjection\ContainerInterface)\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=record, fillcolor="#eeeeee", style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot new file mode 100644 index 0000000..9fdf341 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10-1.dot @@ -0,0 +1,10 @@ +digraph sc { + ratio="normal" + node [fontsize="13" fontname="Verdana" shape="square"]; + edge [fontsize="12" fontname="Verdana" color="white" arrowhead="closed" arrowsize="1"]; + + node_service_container [label="service_container (Psr\Container\ContainerInterface, Symfony\Component\DependencyInjection\ContainerInterface)\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=square, fillcolor="grey", style="filled"]; + node_foo [label="foo\nFooClass\n", shape=square, fillcolor="grey", style="filled"]; + node_bar [label="bar\n\n", shape=square, fillcolor="red", style="empty"]; + node_foo -> node_bar [label="" style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot new file mode 100644 index 0000000..309388e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services10.dot @@ -0,0 +1,10 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container (Psr\Container\ContainerInterface, Symfony\Component\DependencyInjection\ContainerInterface)\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar [label="bar\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foo -> node_bar [label="" style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot new file mode 100644 index 0000000..fffe8f1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services13.dot @@ -0,0 +1,10 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar [label="bar\nBarClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo -> node_bar [label="" style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot new file mode 100644 index 0000000..e740108 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services14.dot @@ -0,0 +1,7 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container (Psr\Container\ContainerInterface, Symfony\Component\DependencyInjection\ContainerInterface)\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=record, fillcolor="#eeeeee", style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot new file mode 100644 index 0000000..e177fae --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services17.dot @@ -0,0 +1,8 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container (Psr\Container\ContainerInterface, Symfony\Component\DependencyInjection\ContainerInterface)\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo [label="foo\n%foo.class%\n", shape=record, fillcolor="#eeeeee", style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot new file mode 100644 index 0000000..4fbccee --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services18.dot @@ -0,0 +1,8 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_foo [label="foo\nFooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_container [label="service_container\nSymfony\\Component\\DependencyInjection\\ContainerBuilder\n", shape=record, fillcolor="#9999ff", style="filled"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot new file mode 100644 index 0000000..2c11697 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/graphviz/services9.dot @@ -0,0 +1,60 @@ +digraph sc { + ratio="compress" + node [fontsize="11" fontname="Arial" shape="record"]; + edge [fontsize="9" fontname="Arial" color="grey" arrowhead="open" arrowsize="0.5"]; + + node_service_container [label="service_container (Psr\Container\ContainerInterface, Symfony\Component\DependencyInjection\ContainerInterface)\nSymfony\\Component\\DependencyInjection\\ContainerInterface\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo [label="foo (alias_for_foo)\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_baz [label="foo.baz\nBazClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar [label="bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_bar [label="foo_bar\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="dotted"]; + node_method_call1 [label="method_call1\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo_with_inline [label="foo_with_inline\nFoo\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_inlined [label="inlined\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_baz [label="baz\nBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_request [label="request\nRequest\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configurator_service [label="configurator_service\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configured_service [label="configured_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configurator_service_simple [label="configurator_service_simple\nConfClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_configured_service_simple [label="configured_service_simple\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_decorated [label="decorated\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_decorator_service [label="decorator_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_decorator_service_with_name [label="decorator_service_with_name\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_deprecated_service [label="deprecated_service\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_new_factory [label="new_factory\nFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_service [label="factory_service\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_new_factory_service [label="new_factory_service\nFooBarBaz\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_service_from_static_method [label="service_from_static_method\nBar\\FooClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_simple [label="factory_simple\nSimpleFactoryClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_factory_service_simple [label="factory_service_simple\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_lazy_context [label="lazy_context\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_lazy_context_ignore_invalid_ref [label="lazy_context_ignore_invalid_ref\nLazyContext\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_BAR [label="BAR\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_bar2 [label="bar2\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_BAR2 [label="BAR2\nstdClass\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_tagged_iterator_foo [label="tagged_iterator_foo\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_tagged_iterator [label="tagged_iterator\nBar\n", shape=record, fillcolor="#eeeeee", style="filled"]; + node_foo2 [label="foo2\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foo3 [label="foo3\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foobaz [label="foobaz\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_invalid [label="invalid\n\n", shape=record, fillcolor="#ff9999", style="filled"]; + node_foo -> node_foo_baz [label="" style="filled"]; + node_foo -> node_service_container [label="" style="filled"]; + node_foo -> node_foo_baz [label="" style="dashed"]; + node_foo -> node_bar [label="setBar()" style="dashed"]; + node_bar -> node_foo_baz [label="" style="filled"]; + node_foo_bar -> node_deprecated_service [label="" style="filled"]; + node_method_call1 -> node_foo [label="setBar()" style="dashed"]; + node_method_call1 -> node_foo2 [label="setBar()" style="dashed"]; + node_method_call1 -> node_foo3 [label="setBar()" style="dashed"]; + node_method_call1 -> node_foobaz [label="setBar()" style="dashed"]; + node_foo_with_inline -> node_inlined [label="setBar()" style="dashed"]; + node_inlined -> node_baz [label="setBaz()" style="dashed"]; + node_baz -> node_foo_with_inline [label="setFoo()" style="dashed"]; + node_configurator_service -> node_baz [label="setFoo()" style="dashed"]; + node_lazy_context -> node_foo_baz [label="" style="filled" color="#9999ff"]; + node_lazy_context -> node_service_container [label="" style="filled" color="#9999ff"]; + node_lazy_context_ignore_invalid_ref -> node_foo_baz [label="" style="filled" color="#9999ff"]; + node_lazy_context_ignore_invalid_ref -> node_invalid [label="" style="filled" color="#9999ff"]; + node_BAR -> node_bar [label="" style="dashed"]; +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/FooVariadic.php b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/FooVariadic.php new file mode 100644 index 0000000..12861c5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/FooVariadic.php @@ -0,0 +1,16 @@ +setParameter('project.configs', $configs); + $configs = array_filter($configs); + + if ($configs) { + $config = call_user_func_array('array_merge', $configs); + } else { + $config = array(); + } + + $configuration->register('project.service.bar', 'FooClass')->setPublic(true); + $configuration->setParameter('project.parameter.bar', isset($config['foo']) ? $config['foo'] : 'foobar'); + + $configuration->register('project.service.foo', 'FooClass')->setPublic(true); + $configuration->setParameter('project.parameter.foo', isset($config['foo']) ? $config['foo'] : 'foobar'); + + return $configuration; + } + + public function getXsdValidationBasePath() + { + return false; + } + + public function getNamespace() + { + return 'http://www.example.com/schema/project'; + } + + public function getAlias() + { + return 'project'; + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php new file mode 100644 index 0000000..2ee2f12 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/ProjectWithXsdExtension.php @@ -0,0 +1,19 @@ +9ZF829)cP2qlv4X_J zzrf1OpTLe_VeL-5W{LMstcEZy0@`@Zkp``){HON4^-Lnf=$gbDLerhScQua<{zIcIFuhk!dy`p zv$5$^oT&;v%oye^<(nUKM%|cd+kVWHBL6%g0b_2kKrOtsU)Tg6kqzU>C!>t}8Y@7? zps^y4SsVq-EVP+|KGR`&J%<-0LzJL1$PcECD5$56cuw zq1FNmJ()}#n6gB~;P`1W)1Nyu!PY^8B5vAZWzelSVrpr?ndyI*JgK*)Nlc!BG#%QY zSQ$!y`zgjAH|YLgcjFGl-m&dEo~K&4K#;;!gZ_UiH1{p1S5fbb#N@V<%CUQ^QrUD5 zd)-HQ%M=97Bvx+W?HSrRO)P{Uh6K3YO=YqTBbLRwL60+RhfxFp#ViTQ^PJ%b$8t94 z5HBpz&&B-H{R70(VFjlH+&l$t=4s|iOluhpGdxRt^D12{q)9@x8^$EYxml}0Tm>CJpcdz literal 0 HcmV?d00001 diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/autowiring_classes.php b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/autowiring_classes.php new file mode 100644 index 0000000..77f8f61 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/autowiring_classes.php @@ -0,0 +1,354 @@ + prefix is on purpose */ + public function setWithCallsConfigured(A $a) + { + } + + /** @required */ + public function setChildMethodWithoutDocBlock(A $a) + { + } +} + +class NotWireable +{ + public function setNotAutowireable(NotARealClass $n) + { + } + + public function setNotAutowireableBecauseOfATypo(lesTilleuls $sam) + { + } + + public function setBar() + { + } + + public function setOptionalNotAutowireable(NotARealClass $n = null) + { + } + + public function setDifferentNamespace(\stdClass $n) + { + } + + public function setOptionalNoTypeHint($foo = null) + { + } + + public function setOptionalArgNoAutowireable($other = 'default_val') + { + } + + /** @required */ + protected function setProtectedMethod(A $a) + { + } +} + +class PrivateConstructor +{ + private function __construct() + { + } +} + +class ScalarSetter +{ + /** + * @required + */ + public function setDefaultLocale($defaultLocale) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/classes.php b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/classes.php new file mode 100644 index 0000000..33b043f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/classes.php @@ -0,0 +1,136 @@ +configure(); +} + +class BarClass extends BazClass +{ + protected $baz; + public $foo = 'foo'; + + public function setBaz(BazClass $baz) + { + $this->baz = $baz; + } + + public function getBaz() + { + return $this->baz; + } +} + +class BazClass +{ + protected $foo; + + public function setFoo(Foo $foo) + { + $this->foo = $foo; + } + + public function configure($instance) + { + $instance->configure(); + } + + public static function getInstance() + { + return new self(); + } + + public static function configureStatic($instance) + { + $instance->configure(); + } + + public static function configureStatic1() + { + } +} + +class BarUserClass +{ + public $bar; + + public function __construct(BarClass $bar) + { + $this->bar = $bar; + } +} + +class MethodCallClass +{ + public $simple; + public $complex; + private $callPassed = false; + + public function callMe() + { + $this->callPassed = is_scalar($this->simple) && is_object($this->complex); + } + + public function callPassed() + { + return $this->callPassed; + } +} + +class DummyProxyDumper implements ProxyDumper +{ + public function isProxyCandidate(Definition $definition) + { + return $definition->isLazy(); + } + + public function getProxyFactoryCode(Definition $definition, $id, $factoryCall = null) + { + return " // lazy factory for {$definition->getClass()}\n\n"; + } + + public function getProxyCode(Definition $definition) + { + return "// proxy code for {$definition->getClass()}\n"; + } +} + +class LazyContext +{ + public $lazyValues; + public $lazyEmptyValues; + + public function __construct($lazyValues, $lazyEmptyValues) + { + $this->lazyValues = $lazyValues; + $this->lazyEmptyValues = $lazyEmptyValues; + } +} + +class FoobarCircular +{ + public function __construct(FooCircular $foo) + { + $this->foo = $foo; + } +} + +class FooCircular +{ + public function __construct(BarCircular $bar) + { + $this->bar = $bar; + } +} + +class BarCircular +{ + public function addFoobar(FoobarCircular $foobar) + { + $this->foobar = $foobar; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php new file mode 100644 index 0000000..c675478 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/createphar.php @@ -0,0 +1,47 @@ +addFromString('ProjectWithXsdExtensionInPhar.php', <<<'EOT' +addFromString('schema/project-1.0.xsd', <<<'EOT' + + + + + + + + + + +EOT +); +$phar->setStub(''); diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php new file mode 100644 index 0000000..bcb4e20 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/foo.php @@ -0,0 +1,43 @@ +arguments = $arguments; + } + + public static function getInstance($arguments = array()) + { + $obj = new self($arguments); + $obj->called = true; + + return $obj; + } + + public function initialize() + { + $this->initialized = true; + } + + public function configure() + { + $this->configured = true; + } + + public function setBar($value = null) + { + $this->bar = $value; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd new file mode 100644 index 0000000..282884e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/includes/schema/project-1.0.xsd @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini new file mode 100644 index 0000000..9fdef85 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/almostvalid.ini @@ -0,0 +1,2 @@ +foo = ' +bar = bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/ini_with_wrong_ext.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/ini_with_wrong_ext.xml new file mode 100644 index 0000000..7f1b4d3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/ini_with_wrong_ext.xml @@ -0,0 +1,2 @@ +[parameters] + with_wrong_ext = 'from ini' diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini new file mode 100644 index 0000000..9f84a60 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/nonvalid.ini @@ -0,0 +1,2 @@ +{NOT AN INI FILE} +{JUST A PLAIN TEXT FILE} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini new file mode 100644 index 0000000..df92f75 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters.ini @@ -0,0 +1,3 @@ +[parameters] + foo = bar + bar = %foo% diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini new file mode 100644 index 0000000..e50f722 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters1.ini @@ -0,0 +1,3 @@ +[parameters] + FOO = foo + baz = baz diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini new file mode 100644 index 0000000..75fbac6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/parameters2.ini @@ -0,0 +1,2 @@ +[parameters] + imported_from_ini = true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini new file mode 100644 index 0000000..19cc5b3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/ini/types.ini @@ -0,0 +1,27 @@ +[parameters] + true = true + true_comment = true ; comment + false = false + null = null + on = on + off = off + yes = yes + no = no + none = none + constant = PHP_VERSION + 12 = 12 + 12_string = '12' + 12_comment = 12 ; comment + 12_string_comment = '12' ; comment + 12_string_comment_again = "12" ; comment + -12 = -12 + 0 = 0 + 1 = 1 + 0b0110 = 0b0110 + 11112222333344445555 = 1111,2222,3333,4444,5555 + 0777 = 0777 + 255 = 0xFF + 100.0 = 1e2 + -120.0 = -1.2E2 + -10100.1 = -10100.1 + -10,100.1 = -10,100.1 diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php new file mode 100644 index 0000000..a6b9962 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php @@ -0,0 +1,62 @@ +parameterBag = null; + + $this->services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php new file mode 100644 index 0000000..e513fd7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php @@ -0,0 +1,59 @@ +services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php new file mode 100644 index 0000000..84986f1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php @@ -0,0 +1,62 @@ +parameterBag = null; + + $this->services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_without_constructor.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_without_constructor.php new file mode 100644 index 0000000..0a4975b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/custom_container_class_without_constructor.php @@ -0,0 +1,59 @@ +services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/php_with_wrong_ext.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/php/php_with_wrong_ext.yml new file mode 100644 index 0000000..fdd3125 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/php_with_wrong_ext.yml @@ -0,0 +1,3 @@ +setParameter('with_wrong_ext', 'from php'); diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php new file mode 100644 index 0000000..a04d80a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1-1.php @@ -0,0 +1,59 @@ +services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1.php new file mode 100644 index 0000000..f25c59b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services1.php @@ -0,0 +1,57 @@ +services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services10.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services10.php new file mode 100644 index 0000000..31c4475 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services10.php @@ -0,0 +1,141 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'test' => 'getTestService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'test' shared service. + * + * @return \stdClass + */ + protected function getTestService() + { + return $this->services['test'] = new \stdClass(array('only dot' => '.', 'concatenation as value' => '.\'\'.', 'concatenation from the start value' => '\'\'.', '.' => 'dot as a key', '.\'\'.' => 'concatenation as a key', '\'\'.' => 'concatenation from the start key', 'optimize concatenation' => 'string1-string2', 'optimize concatenation with empty string' => 'string1string2', 'optimize concatenation from the start' => 'start', 'optimize concatenation at the end' => 'end', 'new line' => 'string with '."\n".'new line')); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array(); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'empty_value' => '', + 'some_string' => '-', + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php new file mode 100644 index 0000000..d4f872f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services12.php @@ -0,0 +1,155 @@ +targetDirs[$i] = $dir = \dirname($dir); + } + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'test' => 'getTestService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'test' shared service. + * + * @return \stdClass + */ + protected function getTestService() + { + return $this->services['test'] = new \stdClass(('wiz'.$this->targetDirs[1]), array(('wiz'.$this->targetDirs[1]) => ($this->targetDirs[2].'/'))); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'foo' => false, + 'buz' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'foo': $value = ('wiz'.$this->targetDirs[1]); break; + case 'buz': $value = $this->targetDirs[2]; break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'bar' => __DIR__, + 'baz' => (__DIR__.'/PhpDumperTest.php'), + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php new file mode 100644 index 0000000..8c90280 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services13.php @@ -0,0 +1,74 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'foo' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected function getBarService() + { + $a = new \stdClass(); + $a->add($this); + + return $this->services['bar'] = new \stdClass($a); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php new file mode 100644 index 0000000..673c9d5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services19.php @@ -0,0 +1,85 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'service_from_anonymous_factory' => 'getServiceFromAnonymousFactoryService', + 'service_with_method_call_and_factory' => 'getServiceWithMethodCallAndFactoryService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'service_from_anonymous_factory' shared service. + * + * @return \Bar\FooClass + */ + protected function getServiceFromAnonymousFactoryService() + { + return $this->services['service_from_anonymous_factory'] = (new \Bar\FooClass())->getInstance(); + } + + /** + * Gets the public 'service_with_method_call_and_factory' shared service. + * + * @return \Bar\FooClass + */ + protected function getServiceWithMethodCallAndFactoryService() + { + $this->services['service_with_method_call_and_factory'] = $instance = new \Bar\FooClass(); + + $instance->setBar(\Bar\FooClass::getInstance()); + + return $instance; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php new file mode 100644 index 0000000..090a77d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services24.php @@ -0,0 +1,70 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'foo' => 'getFooService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'foo' shared autowired service. + * + * @return \Foo + */ + protected function getFooService() + { + return $this->services['foo'] = new \Foo(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php new file mode 100644 index 0000000..942eb0e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services26.php @@ -0,0 +1,175 @@ +targetDirs[$i] = $dir = \dirname($dir); + } + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'test' => 'getTestService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\Bar + */ + protected function getBarService() + { + return $this->services['bar'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\Bar($this->getEnv('QUZ')); + } + + /** + * Gets the public 'test' shared service. + * + * @return object A %env(FOO)% instance + */ + protected function getTestService() + { + $class = $this->getEnv('FOO'); + + return $this->services['test'] = new $class($this->getEnv('Bar'), 'foo'.$this->getEnv('string:FOO').'baz', $this->getEnv('int:Baz')); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'bar' => false, + 'baz' => false, + 'json' => false, + 'db_dsn' => false, + 'env(json_file)' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'bar': $value = $this->getEnv('FOO'); break; + case 'baz': $value = $this->getEnv('int:Baz'); break; + case 'json': $value = $this->getEnv('json:file:json_file'); break; + case 'db_dsn': $value = $this->getEnv('resolve:DB'); break; + case 'env(json_file)': $value = ($this->targetDirs[1].'/array.json'); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'project_dir' => '/foo/bar', + 'env(FOO)' => 'foo', + 'env(DB)' => 'sqlite://%project_dir%/var/data.db', + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services33.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services33.php new file mode 100644 index 0000000..1c70b0e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services33.php @@ -0,0 +1,81 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'Bar\\Foo' => 'getFooService', + 'Foo\\Foo' => 'getFoo2Service', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'Bar\Foo' shared service. + * + * @return \Bar\Foo + */ + protected function getFooService() + { + return $this->services['Bar\Foo'] = new \Bar\Foo(); + } + + /** + * Gets the public 'Foo\Foo' shared service. + * + * @return \Foo\Foo + */ + protected function getFoo2Service() + { + return $this->services['Foo\Foo'] = new \Foo\Foo(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php new file mode 100644 index 0000000..dc7da1c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services8.php @@ -0,0 +1,140 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array(); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'foo' => 'bar', + 'baz' => 'bar', + 'bar' => 'foo is %foo bar', + 'escape' => '@escapeme', + 'values' => array( + 0 => true, + 1 => false, + 2 => NULL, + 3 => 0, + 4 => 1000.3, + 5 => 'true', + 6 => 'false', + 7 => 'null', + ), + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_as_files.txt b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_as_files.txt new file mode 100644 index 0000000..2db7ef4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_as_files.txt @@ -0,0 +1,521 @@ +Array +( + [Container%s/removed-ids.php] => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'configurator_service' => true, + 'configurator_service_simple' => true, + 'decorated.pif-pouf' => true, + 'decorator_service.inner' => true, + 'factory_simple' => true, + 'inlined' => true, + 'new_factory' => true, + 'tagged_iterator_foo' => true, +); + + [Container%s/getBAR2Service.php] => services['BAR'] = $instance = new \stdClass(); + +$instance->bar = ($this->services['bar'] ?? $this->getBarService()); + +return $instance; + + [Container%s/getBAR22Service.php] => services['BAR2'] = new \stdClass(); + + [Container%s/getBar23Service.php] => services['bar2'] = new \stdClass(); + + [Container%s/getBazService.php] => services['baz'] = $instance = new \Baz(); + +$instance->setFoo(($this->services['foo_with_inline'] ?? $this->load('getFooWithInlineService.php'))); + +return $instance; + + [Container%s/getConfiguredServiceService.php] => services['configured_service'] = $instance = new \stdClass(); + +$a = new \ConfClass(); +$a->setFoo(($this->services['baz'] ?? $this->load('getBazService.php'))); + +$a->configureStdClass($instance); + +return $instance; + + [Container%s/getConfiguredServiceSimpleService.php] => services['configured_service_simple'] = $instance = new \stdClass(); + +(new \ConfClass('bar'))->configureStdClass($instance); + +return $instance; + + [Container%s/getDecoratorServiceService.php] => services['decorator_service'] = new \stdClass(); + + [Container%s/getDecoratorServiceWithNameService.php] => services['decorator_service_with_name'] = new \stdClass(); + + [Container%s/getDeprecatedServiceService.php] => services['deprecated_service'] = new \stdClass(); + + [Container%s/getFactoryServiceService.php] => services['factory_service'] = ($this->services['foo.baz'] ?? $this->load('getFoo_BazService.php'))->getInstance(); + + [Container%s/getFactoryServiceSimpleService.php] => services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->load('getFactorySimpleService.php'))->getInstance(); + + [Container%s/getFactorySimpleService.php] => privates['factory_simple'] = new \SimpleFactoryClass('foo'); + + [Container%s/getFooService.php] => services['foo.baz'] ?? $this->load('getFoo_BazService.php')); + +$this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); + +$instance->foo = 'bar'; +$instance->moo = $a; +$instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); +$instance->setBar(($this->services['bar'] ?? $this->getBarService())); +$instance->initialize(); +sc_configure($instance); + +return $instance; + + [Container%s/getFoo_BazService.php] => services['foo.baz'] = $instance = \BazClass::getInstance(); + +\BazClass::configureStatic1($instance); + +return $instance; + + [Container%s/getFooWithInlineService.php] => services['foo_with_inline'] = $instance = new \Foo(); + +$a = new \Bar(); + +$a->pub = 'pub'; +$a->setBaz(($this->services['baz'] ?? $this->load('getBazService.php'))); + +$instance->setBar($a); + +return $instance; + + [Container%s/getLazyContextService.php] => services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { + yield 'k1' => ($this->services['foo.baz'] ?? $this->load('getFoo_BazService.php')); + yield 'k2' => $this; +}, 2), new RewindableGenerator(function () { + return new \EmptyIterator(); +}, 0)); + + [Container%s/getLazyContextIgnoreInvalidRefService.php] => services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () { + yield 0 => ($this->services['foo.baz'] ?? $this->load('getFoo_BazService.php')); +}, 1), new RewindableGenerator(function () { + return new \EmptyIterator(); +}, 0)); + + [Container%s/getMethodCall1Service.php] => targetDirs[0].'/Fixtures/includes/foo.php'); + +$this->services['method_call1'] = $instance = new \Bar\FooClass(); + +$instance->setBar(($this->services['foo'] ?? $this->load('getFooService.php'))); +$instance->setBar(NULL); +$instance->setBar((($this->services['foo'] ?? $this->load('getFooService.php'))->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + +return $instance; + + [Container%s/getNewFactoryServiceService.php] => foo = 'bar'; + +$this->services['new_factory_service'] = $instance = $a->getInstance(); + +$instance->foo = 'bar'; + +return $instance; + + [Container%s/getServiceFromStaticMethodService.php] => services['service_from_static_method'] = \Bar\FooClass::getInstance(); + + [Container%s/getTaggedIteratorService.php] => services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { + yield 0 => ($this->services['foo'] ?? $this->load('getFooService.php')); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); +}, 2)); + + [Container%s/getTaggedIteratorFooService.php] => privates['tagged_iterator_foo'] = new \Bar(); + + [Container%s/ProjectServiceContainer.php] => targetDirs[0] = \dirname($containerDir); + for ($i = 1; $i <= 5; ++$i) { + $this->targetDirs[$i] = $dir = \dirname($dir); + } + $this->buildParameters = $buildParameters; + $this->containerDir = $containerDir; + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->syntheticIds = array( + 'request' => true, + ); + $this->methodMap = array( + 'bar' => 'getBarService', + 'foo_bar' => 'getFooBarService', + ); + $this->fileMap = array( + 'BAR' => 'getBAR2Service.php', + 'BAR2' => 'getBAR22Service.php', + 'bar2' => 'getBar23Service.php', + 'baz' => 'getBazService.php', + 'configured_service' => 'getConfiguredServiceService.php', + 'configured_service_simple' => 'getConfiguredServiceSimpleService.php', + 'decorator_service' => 'getDecoratorServiceService.php', + 'decorator_service_with_name' => 'getDecoratorServiceWithNameService.php', + 'deprecated_service' => 'getDeprecatedServiceService.php', + 'factory_service' => 'getFactoryServiceService.php', + 'factory_service_simple' => 'getFactoryServiceSimpleService.php', + 'foo' => 'getFooService.php', + 'foo.baz' => 'getFoo_BazService.php', + 'foo_with_inline' => 'getFooWithInlineService.php', + 'lazy_context' => 'getLazyContextService.php', + 'lazy_context_ignore_invalid_ref' => 'getLazyContextIgnoreInvalidRefService.php', + 'method_call1' => 'getMethodCall1Service.php', + 'new_factory_service' => 'getNewFactoryServiceService.php', + 'service_from_static_method' => 'getServiceFromStaticMethodService.php', + 'tagged_iterator' => 'getTaggedIteratorService.php', + ); + $this->aliases = array( + 'alias_for_alias' => 'foo', + 'alias_for_foo' => 'foo', + 'decorated' => 'decorator_service_with_name', + ); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return require $this->containerDir.\DIRECTORY_SEPARATOR.'removed-ids.php'; + } + + protected function load($file, $lazyLoad = true) + { + return require $this->containerDir.\DIRECTORY_SEPARATOR.$file; + } + + /** + * Gets the public 'bar' shared service. + * + * @return \Bar\FooClass + */ + protected function getBarService() + { + $a = ($this->services['foo.baz'] ?? $this->load('getFoo_BazService.php')); + + $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); + + $a->configure($instance); + + return $instance; + } + + /** + * Gets the public 'foo_bar' service. + * + * @return \Bar\FooClass + */ + protected function getFooBarService() + { + return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->load('getDeprecatedServiceService.php'))); + } + + public function getParameter($name) + { + $name = (string) $name; + if (isset($this->buildParameters[$name])) { + return $this->buildParameters[$name]; + } + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + if (isset($this->buildParameters[$name])) { + return true; + } + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + foreach ($this->buildParameters as $name => $value) { + $parameters[$name] = $value; + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array(); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + } +} + + [ProjectServiceContainer.php] => '%s', + 'container.build_id' => '%s', + 'container.build_time' => %d, +), __DIR__.\DIRECTORY_SEPARATOR.'Container%s'); + +) diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php new file mode 100644 index 0000000..a454561 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services9_compiled.php @@ -0,0 +1,481 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->syntheticIds = array( + 'request' => true, + ); + $this->methodMap = array( + 'BAR' => 'getBARService', + 'BAR2' => 'getBAR2Service', + 'bar' => 'getBar3Service', + 'bar2' => 'getBar22Service', + 'baz' => 'getBazService', + 'configured_service' => 'getConfiguredServiceService', + 'configured_service_simple' => 'getConfiguredServiceSimpleService', + 'decorator_service' => 'getDecoratorServiceService', + 'decorator_service_with_name' => 'getDecoratorServiceWithNameService', + 'deprecated_service' => 'getDeprecatedServiceService', + 'factory_service' => 'getFactoryServiceService', + 'factory_service_simple' => 'getFactoryServiceSimpleService', + 'foo' => 'getFooService', + 'foo.baz' => 'getFoo_BazService', + 'foo_bar' => 'getFooBarService', + 'foo_with_inline' => 'getFooWithInlineService', + 'lazy_context' => 'getLazyContextService', + 'lazy_context_ignore_invalid_ref' => 'getLazyContextIgnoreInvalidRefService', + 'method_call1' => 'getMethodCall1Service', + 'new_factory_service' => 'getNewFactoryServiceService', + 'service_from_static_method' => 'getServiceFromStaticMethodService', + 'tagged_iterator' => 'getTaggedIteratorService', + ); + $this->aliases = array( + 'alias_for_alias' => 'foo', + 'alias_for_foo' => 'foo', + 'decorated' => 'decorator_service_with_name', + ); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'configurator_service' => true, + 'configurator_service_simple' => true, + 'decorated.pif-pouf' => true, + 'decorator_service.inner' => true, + 'factory_simple' => true, + 'inlined' => true, + 'new_factory' => true, + 'tagged_iterator_foo' => true, + ); + } + + /** + * Gets the public 'BAR' shared service. + * + * @return \stdClass + */ + protected function getBARService() + { + $this->services['BAR'] = $instance = new \stdClass(); + + $instance->bar = ($this->services['bar'] ?? $this->getBar3Service()); + + return $instance; + } + + /** + * Gets the public 'BAR2' shared service. + * + * @return \stdClass + */ + protected function getBAR2Service() + { + return $this->services['BAR2'] = new \stdClass(); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \Bar\FooClass + */ + protected function getBar3Service() + { + $a = ($this->services['foo.baz'] ?? $this->getFoo_BazService()); + + $this->services['bar'] = $instance = new \Bar\FooClass('foo', $a, $this->getParameter('foo_bar')); + + $a->configure($instance); + + return $instance; + } + + /** + * Gets the public 'bar2' shared service. + * + * @return \stdClass + */ + protected function getBar22Service() + { + return $this->services['bar2'] = new \stdClass(); + } + + /** + * Gets the public 'baz' shared service. + * + * @return \Baz + */ + protected function getBazService() + { + $this->services['baz'] = $instance = new \Baz(); + + $instance->setFoo(($this->services['foo_with_inline'] ?? $this->getFooWithInlineService())); + + return $instance; + } + + /** + * Gets the public 'configured_service' shared service. + * + * @return \stdClass + */ + protected function getConfiguredServiceService() + { + $this->services['configured_service'] = $instance = new \stdClass(); + + $a = new \ConfClass(); + $a->setFoo(($this->services['baz'] ?? $this->getBazService())); + + $a->configureStdClass($instance); + + return $instance; + } + + /** + * Gets the public 'configured_service_simple' shared service. + * + * @return \stdClass + */ + protected function getConfiguredServiceSimpleService() + { + $this->services['configured_service_simple'] = $instance = new \stdClass(); + + (new \ConfClass('bar'))->configureStdClass($instance); + + return $instance; + } + + /** + * Gets the public 'decorator_service' shared service. + * + * @return \stdClass + */ + protected function getDecoratorServiceService() + { + return $this->services['decorator_service'] = new \stdClass(); + } + + /** + * Gets the public 'decorator_service_with_name' shared service. + * + * @return \stdClass + */ + protected function getDecoratorServiceWithNameService() + { + return $this->services['decorator_service_with_name'] = new \stdClass(); + } + + /** + * Gets the public 'deprecated_service' shared service. + * + * @return \stdClass + * + * @deprecated The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed. + */ + protected function getDeprecatedServiceService() + { + @trigger_error('The "deprecated_service" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); + + return $this->services['deprecated_service'] = new \stdClass(); + } + + /** + * Gets the public 'factory_service' shared service. + * + * @return \Bar + */ + protected function getFactoryServiceService() + { + return $this->services['factory_service'] = ($this->services['foo.baz'] ?? $this->getFoo_BazService())->getInstance(); + } + + /** + * Gets the public 'factory_service_simple' shared service. + * + * @return \Bar + */ + protected function getFactoryServiceSimpleService() + { + return $this->services['factory_service_simple'] = ($this->privates['factory_simple'] ?? $this->getFactorySimpleService())->getInstance(); + } + + /** + * Gets the public 'foo' shared service. + * + * @return \Bar\FooClass + */ + protected function getFooService() + { + $a = ($this->services['foo.baz'] ?? $this->getFoo_BazService()); + + $this->services['foo'] = $instance = \Bar\FooClass::getInstance('foo', $a, array('bar' => 'foo is bar', 'foobar' => 'bar'), true, $this); + + $instance->foo = 'bar'; + $instance->moo = $a; + $instance->qux = array('bar' => 'foo is bar', 'foobar' => 'bar'); + $instance->setBar(($this->services['bar'] ?? $this->getBar3Service())); + $instance->initialize(); + sc_configure($instance); + + return $instance; + } + + /** + * Gets the public 'foo.baz' shared service. + * + * @return \BazClass + */ + protected function getFoo_BazService() + { + $this->services['foo.baz'] = $instance = \BazClass::getInstance(); + + \BazClass::configureStatic1($instance); + + return $instance; + } + + /** + * Gets the public 'foo_bar' service. + * + * @return \Bar\FooClass + */ + protected function getFooBarService() + { + return new \Bar\FooClass(($this->services['deprecated_service'] ?? $this->getDeprecatedServiceService())); + } + + /** + * Gets the public 'foo_with_inline' shared service. + * + * @return \Foo + */ + protected function getFooWithInlineService() + { + $this->services['foo_with_inline'] = $instance = new \Foo(); + + $a = new \Bar(); + + $a->pub = 'pub'; + $a->setBaz(($this->services['baz'] ?? $this->getBazService())); + + $instance->setBar($a); + + return $instance; + } + + /** + * Gets the public 'lazy_context' shared service. + * + * @return \LazyContext + */ + protected function getLazyContextService() + { + return $this->services['lazy_context'] = new \LazyContext(new RewindableGenerator(function () { + yield 'k1' => ($this->services['foo.baz'] ?? $this->getFoo_BazService()); + yield 'k2' => $this; + }, 2), new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); + } + + /** + * Gets the public 'lazy_context_ignore_invalid_ref' shared service. + * + * @return \LazyContext + */ + protected function getLazyContextIgnoreInvalidRefService() + { + return $this->services['lazy_context_ignore_invalid_ref'] = new \LazyContext(new RewindableGenerator(function () { + yield 0 => ($this->services['foo.baz'] ?? $this->getFoo_BazService()); + }, 1), new RewindableGenerator(function () { + return new \EmptyIterator(); + }, 0)); + } + + /** + * Gets the public 'method_call1' shared service. + * + * @return \Bar\FooClass + */ + protected function getMethodCall1Service() + { + include_once '%path%foo.php'; + + $this->services['method_call1'] = $instance = new \Bar\FooClass(); + + $instance->setBar(($this->services['foo'] ?? $this->getFooService())); + $instance->setBar(NULL); + $instance->setBar((($this->services['foo'] ?? $this->getFooService())->foo() . (($this->hasParameter("foo")) ? ($this->getParameter("foo")) : ("default")))); + + return $instance; + } + + /** + * Gets the public 'new_factory_service' shared service. + * + * @return \FooBarBaz + */ + protected function getNewFactoryServiceService() + { + $a = new \FactoryClass(); + $a->foo = 'bar'; + + $this->services['new_factory_service'] = $instance = $a->getInstance(); + + $instance->foo = 'bar'; + + return $instance; + } + + /** + * Gets the public 'service_from_static_method' shared service. + * + * @return \Bar\FooClass + */ + protected function getServiceFromStaticMethodService() + { + return $this->services['service_from_static_method'] = \Bar\FooClass::getInstance(); + } + + /** + * Gets the public 'tagged_iterator' shared service. + * + * @return \Bar + */ + protected function getTaggedIteratorService() + { + return $this->services['tagged_iterator'] = new \Bar(new RewindableGenerator(function () { + yield 0 => ($this->services['foo'] ?? $this->getFooService()); + yield 1 => ($this->privates['tagged_iterator_foo'] ?? $this->privates['tagged_iterator_foo'] = new \Bar()); + }, 2)); + } + + /** + * Gets the private 'factory_simple' shared service. + * + * @return \SimpleFactoryClass + * + * @deprecated The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed. + */ + protected function getFactorySimpleService() + { + @trigger_error('The "factory_simple" service is deprecated. You should stop using it, as it will soon be removed.', E_USER_DEPRECATED); + + return $this->privates['factory_simple'] = new \SimpleFactoryClass('foo'); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array(); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'baz_class' => 'BazClass', + 'foo_class' => 'Bar\\FooClass', + 'foo' => 'bar', + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_private.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_private.php new file mode 100644 index 0000000..4de6bfc --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_private.php @@ -0,0 +1,167 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar2' => 'getBar2Service', + 'bar3' => 'getBar3Service', + 'foo' => 'getFooService', + 'foo2' => 'getFoo2Service', + 'foo5' => 'getFoo5Service', + 'foobar4' => 'getFoobar4Service', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar' => true, + 'bar5' => true, + 'foo4' => true, + 'foobar' => true, + 'foobar2' => true, + 'foobar3' => true, + ); + } + + /** + * Gets the public 'bar2' shared service. + * + * @return \BarCircular + */ + protected function getBar2Service() + { + $this->services['bar2'] = $instance = new \BarCircular(); + + $instance->addFoobar(new \FoobarCircular(($this->services['foo2'] ?? $this->getFoo2Service()))); + + return $instance; + } + + /** + * Gets the public 'bar3' shared service. + * + * @return \BarCircular + */ + protected function getBar3Service() + { + $this->services['bar3'] = $instance = new \BarCircular(); + + $a = new \FoobarCircular(); + + $instance->addFoobar($a, $a); + + return $instance; + } + + /** + * Gets the public 'foo' shared service. + * + * @return \FooCircular + */ + protected function getFooService() + { + $a = new \BarCircular(); + + $this->services['foo'] = $instance = new \FooCircular($a); + + $a->addFoobar(new \FoobarCircular($instance)); + + return $instance; + } + + /** + * Gets the public 'foo2' shared service. + * + * @return \FooCircular + */ + protected function getFoo2Service() + { + $a = ($this->services['bar2'] ?? $this->getBar2Service()); + + if (isset($this->services['foo2'])) { + return $this->services['foo2']; + } + + return $this->services['foo2'] = new \FooCircular($a); + } + + /** + * Gets the public 'foo5' shared service. + * + * @return \stdClass + */ + protected function getFoo5Service() + { + $this->services['foo5'] = $instance = new \stdClass(); + + $a = new \stdClass(($this->services['foo5'] ?? $this->getFoo5Service())); + + $a->foo = $instance; + + $instance->bar = $a; + + return $instance; + } + + /** + * Gets the public 'foobar4' shared service. + * + * @return \stdClass + */ + protected function getFoobar4Service() + { + $a = new \stdClass(); + + $this->services['foobar4'] = $instance = new \stdClass($a); + + $a->foobar = $instance; + + return $instance; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_public.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_public.php new file mode 100644 index 0000000..79a0c11 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_almost_circular_public.php @@ -0,0 +1,239 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'bar3' => 'getBar3Service', + 'bar5' => 'getBar5Service', + 'foo' => 'getFooService', + 'foo2' => 'getFoo2Service', + 'foo4' => 'getFoo4Service', + 'foo5' => 'getFoo5Service', + 'foobar' => 'getFoobarService', + 'foobar2' => 'getFoobar2Service', + 'foobar3' => 'getFoobar3Service', + 'foobar4' => 'getFoobar4Service', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar2' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \BarCircular + */ + protected function getBarService() + { + $this->services['bar'] = $instance = new \BarCircular(); + + $instance->addFoobar(($this->services['foobar'] ?? $this->getFoobarService())); + + return $instance; + } + + /** + * Gets the public 'bar3' shared service. + * + * @return \BarCircular + */ + protected function getBar3Service() + { + $this->services['bar3'] = $instance = new \BarCircular(); + + $a = ($this->services['foobar3'] ?? $this->services['foobar3'] = new \FoobarCircular()); + + $instance->addFoobar($a, $a); + + return $instance; + } + + /** + * Gets the public 'bar5' shared service. + * + * @return \stdClass + */ + protected function getBar5Service() + { + $a = ($this->services['foo5'] ?? $this->getFoo5Service()); + + if (isset($this->services['bar5'])) { + return $this->services['bar5']; + } + + $this->services['bar5'] = $instance = new \stdClass($a); + + $instance->foo = $a; + + return $instance; + } + + /** + * Gets the public 'foo' shared service. + * + * @return \FooCircular + */ + protected function getFooService() + { + $a = ($this->services['bar'] ?? $this->getBarService()); + + if (isset($this->services['foo'])) { + return $this->services['foo']; + } + + return $this->services['foo'] = new \FooCircular($a); + } + + /** + * Gets the public 'foo2' shared service. + * + * @return \FooCircular + */ + protected function getFoo2Service() + { + $a = new \BarCircular(); + + $this->services['foo2'] = $instance = new \FooCircular($a); + + $a->addFoobar(($this->services['foobar2'] ?? $this->getFoobar2Service())); + + return $instance; + } + + /** + * Gets the public 'foo4' service. + * + * @return \stdClass + */ + protected function getFoo4Service() + { + $instance = new \stdClass(); + + $instance->foobar = ($this->services['foobar4'] ?? $this->getFoobar4Service()); + + return $instance; + } + + /** + * Gets the public 'foo5' shared service. + * + * @return \stdClass + */ + protected function getFoo5Service() + { + $this->services['foo5'] = $instance = new \stdClass(); + + $instance->bar = ($this->services['bar5'] ?? $this->getBar5Service()); + + return $instance; + } + + /** + * Gets the public 'foobar' shared service. + * + * @return \FoobarCircular + */ + protected function getFoobarService() + { + $a = ($this->services['foo'] ?? $this->getFooService()); + + if (isset($this->services['foobar'])) { + return $this->services['foobar']; + } + + return $this->services['foobar'] = new \FoobarCircular($a); + } + + /** + * Gets the public 'foobar2' shared service. + * + * @return \FoobarCircular + */ + protected function getFoobar2Service() + { + $a = ($this->services['foo2'] ?? $this->getFoo2Service()); + + if (isset($this->services['foobar2'])) { + return $this->services['foobar2']; + } + + return $this->services['foobar2'] = new \FoobarCircular($a); + } + + /** + * Gets the public 'foobar3' shared service. + * + * @return \FoobarCircular + */ + protected function getFoobar3Service() + { + return $this->services['foobar3'] = new \FoobarCircular(); + } + + /** + * Gets the public 'foobar4' shared service. + * + * @return \stdClass + */ + protected function getFoobar4Service() + { + $a = new \stdClass(); + + $this->services['foobar4'] = $instance = new \stdClass($a); + + $a->foobar = $instance; + + return $instance; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_array_params.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_array_params.php new file mode 100644 index 0000000..d72e6fd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_array_params.php @@ -0,0 +1,160 @@ +targetDirs[$i] = $dir = \dirname($dir); + } + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \BarClass + */ + protected function getBarService() + { + $this->services['bar'] = $instance = new \BarClass(); + + $instance->setBaz($this->parameters['array_1'], $this->getParameter('array_2'), '%array_1%', $this->parameters['array_1']); + + return $instance; + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'array_2' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'array_2': $value = array( + 0 => ($this->targetDirs[2].'/Dumper'), + ); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'array_1' => array( + 0 => 123, + ), + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_base64_env.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_base64_env.php new file mode 100644 index 0000000..8af802f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_base64_env.php @@ -0,0 +1,135 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'hello' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'hello': $value = $this->getEnv('base64:foo'); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'env(foo)' => 'd29ybGQ=', + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_dedup_lazy_proxy.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_dedup_lazy_proxy.php new file mode 100644 index 0000000..1653e6e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_dedup_lazy_proxy.php @@ -0,0 +1,92 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'foo' => 'getFooService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected function getBarService($lazyLoad = true) + { + // lazy factory for stdClass + + return new \stdClass(); + } + + /** + * Gets the public 'foo' shared service. + * + * @return \stdClass + */ + protected function getFooService($lazyLoad = true) + { + // lazy factory for stdClass + + return new \stdClass(); + } +} + +// proxy code for stdClass diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_env_in_id.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_env_in_id.php new file mode 100644 index 0000000..4100dcd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_env_in_id.php @@ -0,0 +1,153 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'foo' => 'getFooService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'bar_%env(BAR)%' => true, + 'baz_%env(BAR)%' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected function getBarService() + { + return $this->services['bar'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? $this->privates['bar_%env(BAR)%'] = new \stdClass())); + } + + /** + * Gets the public 'foo' shared service. + * + * @return \stdClass + */ + protected function getFooService() + { + return $this->services['foo'] = new \stdClass(($this->privates['bar_%env(BAR)%'] ?? $this->privates['bar_%env(BAR)%'] = new \stdClass()), array('baz_'.$this->getEnv('string:BAR') => new \stdClass())); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array(); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'env(BAR)' => 'bar', + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_inline_requires.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_inline_requires.php new file mode 100644 index 0000000..7a882f4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_inline_requires.php @@ -0,0 +1,177 @@ +targetDirs[$i] = $dir = \dirname($dir); + } + $this->parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\ParentNotExists' => 'getParentNotExistsService', + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C1' => 'getC1Service', + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C2' => 'getC2Service', + ); + + $this->aliases = array(); + + $this->privates['service_container'] = function () { + include_once $this->targetDirs[1].'/includes/HotPath/I1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/P1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/T1.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C1.php'; + }; + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\includes\\HotPath\\C3' => true, + ); + } + + /** + * Gets the public 'Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists + */ + protected function getParentNotExistsService() + { + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\ParentNotExists(); + } + + /** + * Gets the public 'Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C1' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C1 + */ + protected function getC1Service() + { + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C1'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C1(); + } + + /** + * Gets the public 'Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2 + */ + protected function getC2Service() + { + include_once $this->targetDirs[1].'/includes/HotPath/C3.php'; + include_once $this->targetDirs[1].'/includes/HotPath/C2.php'; + + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C2(new \Symfony\Component\DependencyInjection\Tests\Fixtures\includes\HotPath\C3()); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array(); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'inline_requires' => true, + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_locator.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_locator.php new file mode 100644 index 0000000..59bbce3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_locator.php @@ -0,0 +1,174 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar_service' => 'getBarServiceService', + 'foo_service' => 'getFooServiceService', + 'translator.loader_1' => 'getTranslator_Loader1Service', + 'translator.loader_2' => 'getTranslator_Loader2Service', + 'translator.loader_3' => 'getTranslator_Loader3Service', + 'translator_1' => 'getTranslator1Service', + 'translator_2' => 'getTranslator2Service', + 'translator_3' => 'getTranslator3Service', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'baz_service' => true, + 'translator.loader_1_locator' => true, + 'translator.loader_2_locator' => true, + 'translator.loader_3_locator' => true, + ); + } + + /** + * Gets the public 'bar_service' shared service. + * + * @return \stdClass + */ + protected function getBarServiceService() + { + return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); + } + + /** + * Gets the public 'foo_service' shared service. + * + * @return \Symfony\Component\DependencyInjection\ServiceLocator + */ + protected function getFooServiceService() + { + return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('bar' => function () { + return ($this->services['bar_service'] ?? $this->getBarServiceService()); + }, 'baz' => function (): \stdClass { + return ($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass()); + }, 'nil' => function () { + return NULL; + })); + } + + /** + * Gets the public 'translator.loader_1' shared service. + * + * @return \stdClass + */ + protected function getTranslator_Loader1Service() + { + return $this->services['translator.loader_1'] = new \stdClass(); + } + + /** + * Gets the public 'translator.loader_2' shared service. + * + * @return \stdClass + */ + protected function getTranslator_Loader2Service() + { + return $this->services['translator.loader_2'] = new \stdClass(); + } + + /** + * Gets the public 'translator.loader_3' shared service. + * + * @return \stdClass + */ + protected function getTranslator_Loader3Service() + { + return $this->services['translator.loader_3'] = new \stdClass(); + } + + /** + * Gets the public 'translator_1' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator + */ + protected function getTranslator1Service() + { + return $this->services['translator_1'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_1' => function () { + return ($this->services['translator.loader_1'] ?? $this->services['translator.loader_1'] = new \stdClass()); + }))); + } + + /** + * Gets the public 'translator_2' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator + */ + protected function getTranslator2Service() + { + $this->services['translator_2'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_2' => function () { + return ($this->services['translator.loader_2'] ?? $this->services['translator.loader_2'] = new \stdClass()); + }))); + + $instance->addResource('db', ($this->services['translator.loader_2'] ?? $this->services['translator.loader_2'] = new \stdClass()), 'nl'); + + return $instance; + } + + /** + * Gets the public 'translator_3' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator + */ + protected function getTranslator3Service() + { + $this->services['translator_3'] = $instance = new \Symfony\Component\DependencyInjection\Tests\Fixtures\StubbedTranslator(new \Symfony\Component\DependencyInjection\ServiceLocator(array('translator.loader_3' => function () { + return ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass()); + }))); + + $a = ($this->services['translator.loader_3'] ?? $this->services['translator.loader_3'] = new \stdClass()); + + $instance->addResource('db', $a, 'nl'); + $instance->addResource('db', $a, 'en'); + + return $instance; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_non_shared_lazy.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_non_shared_lazy.php new file mode 100644 index 0000000..cd5707e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_non_shared_lazy.php @@ -0,0 +1,90 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'foo' => true, + ); + } + + protected function createProxy($class, \Closure $factory) + { + return $factory(); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected function getBarService() + { + return $this->services['bar'] = new \stdClass($this->getFooService()); + } + + /** + * Gets the private 'foo' service. + * + * @return \stdClass + */ + protected function getFooService($lazyLoad = true) + { + // lazy factory for stdClass + + return new \stdClass(); + } +} + +// proxy code for stdClass diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_frozen.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_frozen.php new file mode 100644 index 0000000..86315e2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_frozen.php @@ -0,0 +1,82 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar_service' => 'getBarServiceService', + 'foo_service' => 'getFooServiceService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'baz_service' => true, + ); + } + + /** + * Gets the public 'bar_service' shared service. + * + * @return \stdClass + */ + protected function getBarServiceService() + { + return $this->services['bar_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); + } + + /** + * Gets the public 'foo_service' shared service. + * + * @return \stdClass + */ + protected function getFooServiceService() + { + return $this->services['foo_service'] = new \stdClass(($this->privates['baz_service'] ?? $this->privates['baz_service'] = new \stdClass())); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_in_expression.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_in_expression.php new file mode 100644 index 0000000..5caf910 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_private_in_expression.php @@ -0,0 +1,72 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'public_foo' => 'getPublicFooService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'private_bar' => true, + 'private_foo' => true, + ); + } + + /** + * Gets the public 'public_foo' shared service. + * + * @return \stdClass + */ + protected function getPublicFooService() + { + return $this->services['public_foo'] = new \stdClass(($this->privates['private_foo'] ?? $this->privates['private_foo'] = new \stdClass())); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_rot13_env.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_rot13_env.php new file mode 100644 index 0000000..012a360 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_rot13_env.php @@ -0,0 +1,161 @@ +parameters = $this->getDefaultParameters(); + + $this->services = $this->privates = array(); + $this->methodMap = array( + 'Symfony\\Component\\DependencyInjection\\Tests\\Dumper\\Rot13EnvVarProcessor' => 'getRot13EnvVarProcessorService', + 'container.env_var_processors_locator' => 'getContainer_EnvVarProcessorsLocatorService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + ); + } + + /** + * Gets the public 'Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor + */ + protected function getRot13EnvVarProcessorService() + { + return $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor(); + } + + /** + * Gets the public 'container.env_var_processors_locator' shared service. + * + * @return \Symfony\Component\DependencyInjection\ServiceLocator + */ + protected function getContainer_EnvVarProcessorsLocatorService() + { + return $this->services['container.env_var_processors_locator'] = new \Symfony\Component\DependencyInjection\ServiceLocator(array('rot13' => function () { + return ($this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\Rot13EnvVarProcessor()); + })); + } + + public function getParameter($name) + { + $name = (string) $name; + + if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters))) { + throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name)); + } + if (isset($this->loadedDynamicParameters[$name])) { + return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + + return $this->parameters[$name]; + } + + public function hasParameter($name) + { + $name = (string) $name; + + return isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || array_key_exists($name, $this->parameters); + } + + public function setParameter($name, $value) + { + throw new LogicException('Impossible to call set() on a frozen ParameterBag.'); + } + + public function getParameterBag() + { + if (null === $this->parameterBag) { + $parameters = $this->parameters; + foreach ($this->loadedDynamicParameters as $name => $loaded) { + $parameters[$name] = $loaded ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name); + } + $this->parameterBag = new FrozenParameterBag($parameters); + } + + return $this->parameterBag; + } + + private $loadedDynamicParameters = array( + 'hello' => false, + ); + private $dynamicParameters = array(); + + /** + * Computes a dynamic parameter. + * + * @param string The name of the dynamic parameter to load + * + * @return mixed The value of the dynamic parameter + * + * @throws InvalidArgumentException When the dynamic parameter does not exist + */ + private function getDynamicParameter($name) + { + switch ($name) { + case 'hello': $value = $this->getEnv('rot13:foo'); break; + default: throw new InvalidArgumentException(sprintf('The dynamic parameter "%s" must be defined.', $name)); + } + $this->loadedDynamicParameters[$name] = true; + + return $this->dynamicParameters[$name] = $value; + } + + /** + * Gets the default parameters. + * + * @return array An array of the default parameters + */ + protected function getDefaultParameters() + { + return array( + 'env(foo)' => 'jbeyq', + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_subscriber.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_subscriber.php new file mode 100644 index 0000000..4f41330 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_subscriber.php @@ -0,0 +1,92 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => 'getTestServiceSubscriberService', + 'foo_service' => 'getFooServiceService', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true, + 'service_locator.MtGsMEd' => true, + 'service_locator.MtGsMEd.foo_service' => true, + ); + } + + /** + * Gets the public 'Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber' shared service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber + */ + protected function getTestServiceSubscriberService() + { + return $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber(); + } + + /** + * Gets the public 'foo_service' shared autowired service. + * + * @return \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber + */ + protected function getFooServiceService() + { + return $this->services['foo_service'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber((new \Symfony\Component\DependencyInjection\ServiceLocator(array('Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => function (): ?\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { + return ($this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()); + }, 'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\TestServiceSubscriber' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber { + return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()); + }, 'bar' => function (): \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { + return ($this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] ?? $this->services['Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\TestServiceSubscriber()); + }, 'baz' => function (): ?\Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition { + return ($this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] ?? $this->privates['Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition'] = new \Symfony\Component\DependencyInjection\Tests\Fixtures\CustomDefinition()); + })))->withContext('foo_service', $this)); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_uninitialized_ref.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_uninitialized_ref.php new file mode 100644 index 0000000..0f5090c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/services_uninitialized_ref.php @@ -0,0 +1,124 @@ +services = $this->privates = array(); + $this->methodMap = array( + 'bar' => 'getBarService', + 'baz' => 'getBazService', + 'foo1' => 'getFoo1Service', + ); + + $this->aliases = array(); + } + + public function reset() + { + $this->privates = array(); + parent::reset(); + } + + public function compile() + { + throw new LogicException('You cannot compile a dumped container that was already compiled.'); + } + + public function isCompiled() + { + return true; + } + + public function getRemovedIds() + { + return array( + 'Psr\\Container\\ContainerInterface' => true, + 'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true, + 'foo2' => true, + 'foo3' => true, + ); + } + + /** + * Gets the public 'bar' shared service. + * + * @return \stdClass + */ + protected function getBarService() + { + $this->services['bar'] = $instance = new \stdClass(); + + $instance->foo1 = ($this->services['foo1'] ?? null); + $instance->foo2 = null; + $instance->foo3 = ($this->privates['foo3'] ?? null); + $instance->closures = array(0 => function () { + return ($this->services['foo1'] ?? null); + }, 1 => function () { + return null; + }, 2 => function () { + return ($this->privates['foo3'] ?? null); + }); + $instance->iter = new RewindableGenerator(function () { + if (isset($this->services['foo1'])) { + yield 'foo1' => ($this->services['foo1'] ?? null); + } + if (false) { + yield 'foo2' => null; + } + if (isset($this->privates['foo3'])) { + yield 'foo3' => ($this->privates['foo3'] ?? null); + } + }, function () { + return 0 + (int) (isset($this->services['foo1'])) + (int) (false) + (int) (isset($this->privates['foo3'])); + }); + + return $instance; + } + + /** + * Gets the public 'baz' shared service. + * + * @return \stdClass + */ + protected function getBazService() + { + $this->services['baz'] = $instance = new \stdClass(); + + $instance->foo3 = ($this->privates['foo3'] ?? $this->privates['foo3'] = new \stdClass()); + + return $instance; + } + + /** + * Gets the public 'foo1' shared service. + * + * @return \stdClass + */ + protected function getFoo1Service() + { + return $this->services['foo1'] = new \stdClass(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php b/vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php new file mode 100644 index 0000000..aa4df99 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/php/simple.php @@ -0,0 +1,3 @@ +setParameter('foo', 'foo'); diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/class_from_id.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/class_from_id.xml new file mode 100644 index 0000000..e302ce3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/class_from_id.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml new file mode 100644 index 0000000..52df38d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension1/services.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml new file mode 100644 index 0000000..21a7ef5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extension2/services.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml new file mode 100644 index 0000000..b1cc390 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services1.xml @@ -0,0 +1,19 @@ + + + + + + BAR + + + + + + + + + %project.parameter.foo% + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml new file mode 100644 index 0000000..2cdcfb4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services2.xml @@ -0,0 +1,19 @@ + + + + + + BAR + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml new file mode 100644 index 0000000..ed5df6e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services3.xml @@ -0,0 +1,19 @@ + + + + + + BAR + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml new file mode 100644 index 0000000..2c33c3a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services4.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml new file mode 100644 index 0000000..0eaaff2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services5.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml new file mode 100644 index 0000000..a9c0103 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services6.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml new file mode 100644 index 0000000..e77780d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/extensions/services7.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/invalid_alias_definition.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/invalid_alias_definition.xml new file mode 100644 index 0000000..8e99561 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/invalid_alias_definition.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml new file mode 100644 index 0000000..5a05ced --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/namespaces.xml @@ -0,0 +1,17 @@ + + + + + + + + + foo + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nested_service_without_id.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nested_service_without_id.xml new file mode 100644 index 0000000..f8eb009 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nested_service_without_id.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml new file mode 100644 index 0000000..e7b5bc9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/nonvalid.xml @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml new file mode 100644 index 0000000..6dc3ea6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services1.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml new file mode 100644 index 0000000..a4da1bf --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services10.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml new file mode 100644 index 0000000..1ac4938 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services13.xml @@ -0,0 +1,9 @@ + + + + + true + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml new file mode 100644 index 0000000..7344621 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services14.xml @@ -0,0 +1,19 @@ + + + + + app + + + + + + app + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml new file mode 100644 index 0000000..0d2d669 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services2.xml @@ -0,0 +1,31 @@ + + + + + a string + bar + + 0 + 4 + null + true + true + false + on + off + 1.3 + 1000.3 + a string + + foo + bar + + + + value + + PHP_EOL + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml new file mode 100644 index 0000000..36fc8cf --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services21.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml new file mode 100644 index 0000000..3f9e15f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services23.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml new file mode 100644 index 0000000..abf389b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services24.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services28.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services28.xml new file mode 100644 index 0000000..0076cc3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services28.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml new file mode 100644 index 0000000..87bf183 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services3.xml @@ -0,0 +1,13 @@ + + + + + foo + + true + false + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml new file mode 100644 index 0000000..47ec04e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml new file mode 100644 index 0000000..0b7f10a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services4_bad_import.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml new file mode 100644 index 0000000..7610cb4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services5.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml new file mode 100644 index 0000000..cffd5df --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services6.xml @@ -0,0 +1,65 @@ + + + + + + + + + %path%/foo.php + + + foo + + + true + false + + + + + + + + + + + + + + + service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default") + + + + + foo + + + true + false + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml new file mode 100644 index 0000000..824d8b5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services7.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml new file mode 100644 index 0000000..bc1186b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services8.xml @@ -0,0 +1,27 @@ + + + + + %baz% + bar + foo is %%foo bar + @escapeme + + true + false + null + 0 + 1000.3 + true + false + null + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml new file mode 100644 index 0000000..3848a83 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services9.xml @@ -0,0 +1,152 @@ + + + + BazClass + Bar\FooClass + bar + + + + + + + foo + + + foo is %foo% + %foo% + + true + + bar + + + foo is %foo% + %foo% + + + + + + + + + + + + + + foo + + %foo_bar% + + + + + + + %path%foo.php + + + + + + + + + + + + + + service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default") + + + + + + + + + pub + + + + + + + + + + + + + + + + + + + + bar + + + + + + + + + The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + + + bar + + + + + + bar + + + + + + + foo + The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_abstract.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_abstract.xml new file mode 100644 index 0000000..22abf54 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_abstract.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure.xml new file mode 100644 index 0000000..5e855c0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml new file mode 100644 index 0000000..1872470 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_autoconfigure_with_parent.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_bindings.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_bindings.xml new file mode 100644 index 0000000..dd3c41a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_bindings.xml @@ -0,0 +1,21 @@ + + + + + null + quz + factory + + + + + + null + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_case.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_case.xml new file mode 100644 index 0000000..9555824 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_case.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_defaults_with_parent.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_defaults_with_parent.xml new file mode 100644 index 0000000..2bbef62 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_defaults_with_parent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml new file mode 100644 index 0000000..c19a47a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_deprecated.xml @@ -0,0 +1,11 @@ + + + + + + + + The "%service_id%" service is deprecated. + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_dump_load.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_dump_load.xml new file mode 100644 index 0000000..e70be18 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_dump_load.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_inline_not_candidate.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_inline_not_candidate.xml new file mode 100644 index 0000000..7256058 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_inline_not_candidate.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof.xml new file mode 100644 index 0000000..839776a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof_with_parent.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof_with_parent.xml new file mode 100644 index 0000000..949e73f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_instanceof_with_parent.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_named_args.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_named_args.xml new file mode 100644 index 0000000..8968186 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_named_args.xml @@ -0,0 +1,13 @@ + + + + + + ABCD + null + + 123 + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_prototype.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_prototype.xml new file mode 100644 index 0000000..381f95d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_prototype.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_without_id.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_without_id.xml new file mode 100644 index 0000000..bc0f719 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/services_without_id.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml new file mode 100644 index 0000000..1646292 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_with_empty_name.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml new file mode 100644 index 0000000..bc7373d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/tag_without_name.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml new file mode 100644 index 0000000..5f3a284 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/with_key_outside_collection.xml @@ -0,0 +1,9 @@ + + + + + foo + bar + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml new file mode 100644 index 0000000..f217d5b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/withdoctype.xml @@ -0,0 +1,3 @@ + + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/xml/xml_with_wrong_ext.php b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/xml_with_wrong_ext.php new file mode 100644 index 0000000..91ff5d4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/xml/xml_with_wrong_ext.php @@ -0,0 +1,9 @@ + + + + + from xml + + diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services.yml new file mode 100644 index 0000000..fe54b89 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services.yml @@ -0,0 +1,14 @@ +imports: + # Ensure the anonymous services count is reset after importing a file + - { resource: anonymous_services_in_instanceof.yml } + +services: + _defaults: + autowire: true + + Foo: + arguments: + - !service + class: Bar + autowire: true + factory: [ !service { class: Quz }, 'constructFoo' ] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_alias.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_alias.yml new file mode 100644 index 0000000..96546b8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_alias.yml @@ -0,0 +1,7 @@ +services: + Bar: ~ + + Foo: + arguments: + - !service + alias: Bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_instanceof.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_instanceof.yml new file mode 100644 index 0000000..ea0beba --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_instanceof.yml @@ -0,0 +1,15 @@ +services: + _instanceof: + # Ensure previous conditionals aren't applied on anonymous services + Quz: + autowire: true + + DummyInterface: + properties: + foo: !service { class: Anonymous } + + # Ensure next conditionals are not considered as services + Bar: + autowire: true + + Dummy: ~ diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_parameters.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_parameters.yml new file mode 100644 index 0000000..9d9bea3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/anonymous_services_in_parameters.yml @@ -0,0 +1,2 @@ +parameters: + foo: [ !service { } ] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_alias.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_alias.yml new file mode 100644 index 0000000..78975e5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_alias.yml @@ -0,0 +1,11 @@ +services: + foo: + class: stdClass + public: false + + bar: + alias: foo + public: true + # keys other than "alias" and "public" are invalid when defining an alias. + calls: + - [doSomething] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml new file mode 100644 index 0000000..3f34b07 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_calls.yml @@ -0,0 +1,4 @@ +services: + method_call1: + class: FooClass + calls: foo diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml new file mode 100644 index 0000000..79c048a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_decorates.yml @@ -0,0 +1,7 @@ +services: + foo: + class: stdClass + bar: + class: stdClass + decorates: "@foo" + arguments: ["@bar.inner"] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_defaults.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_defaults.yml new file mode 100644 index 0000000..85d910e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_defaults.yml @@ -0,0 +1,2 @@ +services: + _defaults: diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_instanceof.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_instanceof.yml new file mode 100644 index 0000000..2c8ce09 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_empty_instanceof.yml @@ -0,0 +1,2 @@ +services: + _instanceof: diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml new file mode 100644 index 0000000..1f58cac --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_format.yml @@ -0,0 +1,2 @@ +parameters: + FOO: bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml new file mode 100644 index 0000000..2dbbcbf --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_import.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ~ } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml new file mode 100644 index 0000000..1ce9d57 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_imports.yml @@ -0,0 +1,2 @@ +imports: + foo:bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_keyword.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_keyword.yml new file mode 100644 index 0000000..8487e85 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_keyword.yml @@ -0,0 +1,10 @@ +services: + # This definition is valid and should not raise any deprecation notice + foo: + class: stdClass + arguments: [ 'foo', 'bar' ] + + # This definition is invalid and must raise a deprecation notice + bar: + class: stdClass + private: true # the "private" keyword is invalid diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml new file mode 100644 index 0000000..bbd13ac --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_parameters.yml @@ -0,0 +1,2 @@ +parameters: + foo:bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml new file mode 100644 index 0000000..811af3c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_service.yml @@ -0,0 +1,2 @@ +services: + foo: bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml new file mode 100644 index 0000000..cfbf17c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bad_services.yml @@ -0,0 +1 @@ +services: foo diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml new file mode 100644 index 0000000..14536fd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag1.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + # tags is not an array + tags: string diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml new file mode 100644 index 0000000..9028814 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag2.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag is missing the name key + foo_tag: { foo: bar } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml new file mode 100644 index 0000000..72ec4e8 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/badtag3.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag-attribute is not a scalar + - { name: foo, bar: { foo: foo, bar: bar } } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bar/services.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bar/services.yml new file mode 100644 index 0000000..0f846f5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/bar/services.yml @@ -0,0 +1,4 @@ +services: + AppBundle\Foo: + arguments: + - !service {class: AppBundle\Bar } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/class_from_id.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/class_from_id.yml new file mode 100644 index 0000000..5a6532f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/class_from_id.yml @@ -0,0 +1,4 @@ +services: + Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass: + autowire: true + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/foo/services.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/foo/services.yml new file mode 100644 index 0000000..76eee55 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/foo/services.yml @@ -0,0 +1,4 @@ +services: + AppBundle\Hello: + arguments: + - !service {class: AppBundle\World} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml new file mode 100644 index 0000000..89d8b91 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/_child.yml @@ -0,0 +1,4 @@ +services: + child_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml new file mode 100644 index 0000000..157b584 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/expected.yml @@ -0,0 +1,11 @@ +services: + child_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # the parent has autoconfigure true, but that does not cascade to the child + autoconfigure: false + # an autoconfigured "instanceof" is setup for IntegrationTestStub + # but its calls are NOT added, because the class was only + # set on the parent, not the child + #calls: + # - [enableSummer, [true]] + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml new file mode 100644 index 0000000..5a522cc --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_child_not_applied/main.yml @@ -0,0 +1,8 @@ +imports: + - { resource: _child.yml } + +services: + parent_service: + autoconfigure: true + abstract: true + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml new file mode 100644 index 0000000..5319c20 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/_child.yml @@ -0,0 +1,3 @@ +services: + child_service: + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml new file mode 100644 index 0000000..3c1c89c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/expected.yml @@ -0,0 +1,6 @@ +services: + child_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # autoconfigure is set on the parent, but not on the child + autoconfigure: false + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml new file mode 100644 index 0000000..4f70ba7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child/main.yml @@ -0,0 +1,8 @@ +imports: + - { resource: _child.yml } + +services: + parent_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + autoconfigure: true + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml new file mode 100644 index 0000000..5319c20 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/_child.yml @@ -0,0 +1,3 @@ +services: + child_service: + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml new file mode 100644 index 0000000..4e32bea --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/expected.yml @@ -0,0 +1,7 @@ +services: + child_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # from an autoconfigured "instanceof" applied to parent class + # but NOT inherited down to child + # tags: [from_autoconfigure] + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml new file mode 100644 index 0000000..4f70ba7 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/autoconfigure_parent_child_tags/main.yml @@ -0,0 +1,8 @@ +imports: + - { resource: _child.yml } + +services: + parent_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + autoconfigure: true + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/expected.yml new file mode 100644 index 0000000..54cd91c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/expected.yml @@ -0,0 +1,9 @@ +services: + # child_service in the end should be identical to this + child_service_expected: + class: stdClass + autowire: false + public: true + lazy: true + # ONLY the child tag, the parent tag does not inherit + tags: [from_child] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/main.yml new file mode 100644 index 0000000..edaa3a3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/child_parent/main.yml @@ -0,0 +1,13 @@ +services: + parent_service: + abstract: true + lazy: true + autowire: false + public: false + tags: [from_parent] + + child_service: + class: stdClass + parent: parent_service + public: true + tags: [from_child] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml new file mode 100644 index 0000000..5f8df80 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/expected.yml @@ -0,0 +1,9 @@ +services: + child_service_expected: + class: stdClass + # set explicitly on child (not overridden by parent) + autoconfigure: false + # set explicitly on child + autowire: true + tags: [from_defaults] + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml new file mode 100644 index 0000000..621eee9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_child_tags/main.yml @@ -0,0 +1,19 @@ +services: + _defaults: + autoconfigure: true + autowire: true + tags: [from_defaults] + + parent_service: + class: stdClass + # will not override child + autoconfigure: true + # parent definitions are not applied to children + tags: [from_parent] + public: true + + child_service: + parent: parent_service + # _defaults is ok because these are explicitly set + autoconfigure: false + autowire: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml new file mode 100644 index 0000000..e9161dc --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/expected.yml @@ -0,0 +1,26 @@ +services: + # main_service should look like this in the end + main_service_expected: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + # _instanceof overrides _defaults + autowire: false + # inherited from _defaults + autoconfigure: true + # from _instanceof + shared: false + # service definition overrides _instanceof + public: true + + tags: + - { name: foo_tag, tag_option: from_service } + # these 2 are from instanceof + - { name: foo_tag, tag_option: from_instanceof } + - { name: bar_tag } + - { name: from_defaults } + # calls from instanceof are kept, but this comes later + calls: + # first call is from instanceof + - [setSunshine, [bright]] + # + - [enableSummer, [true]] + - [setSunshine, [warm]] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml new file mode 100644 index 0000000..406a351 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_instanceof_importance/main.yml @@ -0,0 +1,30 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: true + tags: [from_defaults] + + _instanceof: + Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStubParent: + autowire: false + shared: false + public: false + tags: + - { name: foo_tag, tag_option: from_instanceof } + calls: + - [setSunshine, [bright]] + + Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub: + tags: + - { name: bar_tag } + + main_service: + class: Symfony\Component\DependencyInjection\Tests\Compiler\IntegrationTestStub + public: true + tags: + - { name: foo_tag, tag_option: from_service } + # calls from instanceof are kept, but this comes later + calls: + - [enableSummer, [true]] + - [setSunshine, [warm]] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml new file mode 100644 index 0000000..86ef83c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/_child.yml @@ -0,0 +1,4 @@ +services: + # loaded here to avoid defaults in other file + child_service: + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml new file mode 100644 index 0000000..272c375 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/expected.yml @@ -0,0 +1,7 @@ +services: + child_service_expected: + class: stdClass + # _defaults is applied to the parent, but autoconfigure: true + # does not cascade to the child + autoconfigure: false + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml new file mode 100644 index 0000000..d8cd43c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/defaults_parent_child/main.yml @@ -0,0 +1,10 @@ +imports: + - { resource: _child.yml } + +services: + _defaults: + autoconfigure: true + + parent_service: + class: stdClass + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml new file mode 100644 index 0000000..86ef83c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/_child.yml @@ -0,0 +1,4 @@ +services: + # loaded here to avoid defaults in other file + child_service: + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml new file mode 100644 index 0000000..79ba81d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/expected.yml @@ -0,0 +1,8 @@ +services: + child_service_expected: + class: stdClass + # applied to _instanceof of parent + autowire: true + # from _instanceof, applies to parent, but does NOT inherit to here + # tags: [from_instanceof] + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml new file mode 100644 index 0000000..7569dd9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/integration/instanceof_parent_child/main.yml @@ -0,0 +1,12 @@ +imports: + - { resource: _child.yml } + +services: + _instanceof: + stdClass: + autowire: true + tags: [from_instanceof] + + parent_service: + class: stdClass + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml new file mode 100644 index 0000000..00c011c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/legacy_invalid_alias_definition.yml @@ -0,0 +1,5 @@ +services: + foo: + alias: bar + factory: foo + parent: quz diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml new file mode 100644 index 0000000..4eddb87 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid1.yml @@ -0,0 +1,2 @@ +foo: + bar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml new file mode 100644 index 0000000..c508d53 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/nonvalid2.yml @@ -0,0 +1 @@ +false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/null_config.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/null_config.yml new file mode 100644 index 0000000..e88e12e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/null_config.yml @@ -0,0 +1 @@ +project: ~ diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml new file mode 100644 index 0000000..071742f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services1.yml @@ -0,0 +1,11 @@ +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml new file mode 100644 index 0000000..993fc7c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services10.yml @@ -0,0 +1,10 @@ +parameters: + project.parameter.foo: BAR + +services: + project.service.foo: + class: BAR + public: true + +project: + test: '%project.parameter.foo%' diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml new file mode 100644 index 0000000..40126f0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services11.yml @@ -0,0 +1 @@ +foobarfoobar: {} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml new file mode 100644 index 0000000..d52d355 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services13.yml @@ -0,0 +1,3 @@ +# used to test imports in XML +parameters: + imported_from_yaml: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml new file mode 100644 index 0000000..4c188c5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services14.yml @@ -0,0 +1,3 @@ +services: + factory: { class: FooBarClass, factory: baz:getClass} + factory_with_static_call: { class: FooBarClass, factory: FooBacFactory::createFooBar} diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml new file mode 100644 index 0000000..e05d69d --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services2.yml @@ -0,0 +1,13 @@ +parameters: + foo: bar + values: + - true + - false + - 0 + - 1000.3 + - !php/const PHP_INT_MAX + bar: foo + escape: '@@escapeme' + foo_bar: '@foo_bar' + mixedcase: + MixedCaseKey: value diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml new file mode 100644 index 0000000..a2617c1 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services21.yml @@ -0,0 +1,15 @@ +services: + manager: + class: UserManager + arguments: + - true + calls: + - method: setLogger + arguments: + - '@logger' + - method: setClass + arguments: + - User + tags: + - name: manager + alias: user diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml new file mode 100644 index 0000000..1984c17 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services23.yml @@ -0,0 +1,4 @@ +services: + bar_service: + class: BarClass + autowire: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml new file mode 100644 index 0000000..f59354e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services24.yml @@ -0,0 +1,16 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + foo: + class: Foo + public: true + autowire: true + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml new file mode 100644 index 0000000..a1f235a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services26.yml @@ -0,0 +1,22 @@ +parameters: + project_dir: '/foo/bar' + env(FOO): foo + env(DB): 'sqlite://%%project_dir%%/var/data.db' + bar: '%env(FOO)%' + baz: '%env(int:Baz)%' + json: '%env(json:file:json_file)%' + db_dsn: '%env(resolve:DB)%' + +services: + test: + public: true + class: '%env(FOO)%' + arguments: + - '%env(Bar)%' + - 'foo%bar%baz' + - '%baz%' + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar + public: true + bind: + $quz: '%env(QUZ)%' diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services28.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services28.yml new file mode 100644 index 0000000..fd0ce4c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services28.yml @@ -0,0 +1,34 @@ +services: + _defaults: + public: false + autowire: true + tags: + - name: foo + + Acme\Foo: ~ + + with_defaults: + class: Foo + + with_null: + class: Foo + public: true + autowire: ~ + + no_defaults: + class: Foo + public: true + autowire: false + tags: [] + + with_defaults_aliased: + alias: with_defaults + + with_defaults_aliased_short: '@with_defaults' + + Acme\WithShortCutArgs: [foo] + + child_def: + parent: with_defaults + public: true + autowire: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml new file mode 100644 index 0000000..0e92cdf --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services3.yml @@ -0,0 +1,5 @@ +parameters: + foo: foo + values: + - true + - false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services31_invalid_tags.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services31_invalid_tags.yml new file mode 100644 index 0000000..a6061a9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services31_invalid_tags.yml @@ -0,0 +1,6 @@ +services: + _defaults: + tags: ['foo'] + + Foo\Bar: + tags: invalid diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml new file mode 100644 index 0000000..073f555 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4.yml @@ -0,0 +1,8 @@ +imports: + - services2.yml + - { resource: services3.yml } + - { resource: "../php/simple.php" } + - { resource: "../ini/parameters.ini", class: Symfony\Component\DependencyInjection\Loader\IniFileLoader } + - { resource: "../ini/parameters2.ini" } + - { resource: "../xml/services13.xml" } + - { resource: "../xml/xml_with_wrong_ext.php", type: xml } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml new file mode 100644 index 0000000..f7089fc --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services4_bad_import.yml @@ -0,0 +1,2 @@ +imports: + - { resource: foo_fake.yml, ignore_errors: true } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml new file mode 100644 index 0000000..1ee6c6e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services6.yml @@ -0,0 +1,43 @@ +services: + foo: { class: FooClass } + baz: { class: BazClass } + not_shared: { class: FooClass, shared: false } + file: { class: FooClass, file: '%path%/foo.php' } + arguments: { class: FooClass, arguments: [foo, '@foo', [true, false]] } + configurator1: { class: FooClass, configurator: sc_configure } + configurator2: { class: FooClass, configurator: ['@baz', configure] } + configurator3: { class: FooClass, configurator: [BazClass, configureStatic] } + method_call1: + class: FooClass + calls: + - [ setBar, [] ] + - [ setBar ] + - [ setBar, ['@=service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")'] ] + method_call2: + class: FooClass + calls: + - [ setBar, [ foo, '@foo', [true, false] ] ] + request: + class: Request + synthetic: true + lazy: true + decorator_service: + decorates: decorated + decorator_service_with_name: + decorates: decorated + decoration_inner_name: decorated.pif-pouf + decorator_service_with_name_and_priority: + decorates: decorated + decoration_inner_name: decorated.pif-pouf + decoration_priority: 5 + new_factory1: { class: FooBarClass, factory: factory} + new_factory2: { class: FooBarClass, factory: ['@baz', getClass]} + new_factory3: { class: FooBarClass, factory: [BazClass, getInstance]} + new_factory4: { class: BazClass, factory: [~, getInstance]} + Acme\WithShortCutArgs: [foo, '@baz'] + alias_for_foo: '@foo' + another_alias_for_foo: + alias: foo + public: false + another_third_alias_for_foo: + alias: foo diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml new file mode 100644 index 0000000..09064f2 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services7.yml @@ -0,0 +1,2 @@ +services: + foo: { class: BarClass } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml new file mode 100644 index 0000000..4e37bc9 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services8.yml @@ -0,0 +1,18 @@ +parameters: + foo: '%baz%' + baz: bar + bar: 'foo is %%foo bar' + escape: '@@escapeme' + values: [true, false, null, 0, 1000.3, 'true', 'false', 'null'] + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml new file mode 100644 index 0000000..dbe59ed --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services9.yml @@ -0,0 +1,173 @@ +parameters: + baz_class: BazClass + foo_class: Bar\FooClass + foo: bar + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + foo: + class: Bar\FooClass + tags: + - { name: foo, foo: foo } + - { name: foo, bar: bar, baz: baz } + arguments: [foo, '@foo.baz', { '%foo%': 'foo is %foo%', foobar: '%foo%' }, true, '@service_container'] + properties: { foo: bar, moo: '@foo.baz', qux: { '%foo%': 'foo is %foo%', foobar: '%foo%' } } + calls: + - [setBar, ['@bar']] + - [initialize, { }] + + factory: [Bar\FooClass, getInstance] + configurator: sc_configure + public: true + foo.baz: + class: '%baz_class%' + factory: ['%baz_class%', getInstance] + configurator: ['%baz_class%', configureStatic1] + public: true + bar: + class: Bar\FooClass + arguments: [foo, '@foo.baz', '%foo_bar%'] + configurator: ['@foo.baz', configure] + public: true + foo_bar: + class: '%foo_class%' + shared: false + arguments: ['@deprecated_service'] + public: true + method_call1: + class: Bar\FooClass + file: '%path%foo.php' + calls: + - [setBar, ['@foo']] + - [setBar, ['@?foo2']] + - [setBar, ['@?foo3']] + - [setBar, ['@?foobaz']] + - [setBar, ['@=service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")']] + public: true + + foo_with_inline: + class: Foo + calls: + - [setBar, ['@inlined']] + public: true + + inlined: + class: Bar + public: false + properties: { pub: pub } + calls: + - [setBaz, ['@baz']] + + baz: + class: Baz + calls: + - [setFoo, ['@foo_with_inline']] + public: true + + request: + class: Request + synthetic: true + public: true + configurator_service: + class: ConfClass + public: false + calls: + - [setFoo, ['@baz']] + + configured_service: + class: stdClass + configurator: ['@configurator_service', configureStdClass] + public: true + configurator_service_simple: + class: ConfClass + public: false + arguments: ['bar'] + configured_service_simple: + class: stdClass + configurator: ['@configurator_service_simple', configureStdClass] + public: true + decorated: + class: stdClass + public: true + decorator_service: + class: stdClass + decorates: decorated + public: true + decorator_service_with_name: + class: stdClass + decorates: decorated + decoration_inner_name: decorated.pif-pouf + public: true + deprecated_service: + class: stdClass + deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + public: true + new_factory: + class: FactoryClass + public: false + properties: { foo: bar } + factory_service: + class: Bar + factory: ['@foo.baz', getInstance] + public: true + new_factory_service: + class: FooBarBaz + properties: { foo: bar } + factory: ['@new_factory', getInstance] + public: true + service_from_static_method: + class: Bar\FooClass + factory: [Bar\FooClass, getInstance] + public: true + factory_simple: + class: SimpleFactoryClass + deprecated: The "%service_id%" service is deprecated. You should stop using it, as it will soon be removed. + public: false + arguments: ['foo'] + factory_service_simple: + class: Bar + factory: ['@factory_simple', getInstance] + public: true + lazy_context: + class: LazyContext + arguments: [!iterator {'k1': '@foo.baz', 'k2': '@service_container'}, !iterator []] + public: true + lazy_context_ignore_invalid_ref: + class: LazyContext + arguments: [!iterator ['@foo.baz', '@?invalid'], !iterator []] + public: true + BAR: + class: stdClass + properties: { bar: '@bar' } + public: true + bar2: + class: stdClass + public: true + BAR2: + class: stdClass + public: true + tagged_iterator_foo: + class: Bar + tags: + - { name: foo } + public: false + tagged_iterator: + class: Bar + arguments: + - !tagged foo + public: true + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false + alias_for_foo: + alias: 'foo' + public: true + alias_for_alias: + alias: 'foo' + public: true diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure.yml new file mode 100644 index 0000000..809c9f4 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure.yml @@ -0,0 +1,9 @@ + +services: + _defaults: + autoconfigure: true + + use_defaults_settings: ~ + + override_defaults_settings_to_false: + autoconfigure: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml new file mode 100644 index 0000000..a6b973b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_autoconfigure_with_parent.yml @@ -0,0 +1,9 @@ +services: + parent_service: + class: stdClass + public: true + + child_service: + class: stdClass + autoconfigure: true + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_bindings.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_bindings.yml new file mode 100644 index 0000000..0eba120 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_bindings.yml @@ -0,0 +1,17 @@ +services: + _defaults: + bind: + NonExistent: ~ + $quz: quz + $factory: factory + public: true + + bar: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar + autowire: true + bind: + Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface: '@Symfony\Component\DependencyInjection\Tests\Fixtures\Bar' + $foo: [ ~ ] + + Symfony\Component\DependencyInjection\Tests\Fixtures\Bar: + factory: [ ~, 'create' ] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_case.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_case.yml new file mode 100644 index 0000000..38e477b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_case.yml @@ -0,0 +1,11 @@ +services: + _defaults: {public: true} + bar: + class: stdClass + Bar: + class: stdClass + properties: { bar: '@bar' } + BAR: + class: Bar\FooClass + arguments: ['@Bar'] + calls: [[setBar, ['@bar']]] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml new file mode 100644 index 0000000..cc5c2be --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_configurator_short_syntax.yml @@ -0,0 +1,8 @@ +services: + foo_bar: + class: FooBarClass + configurator: foo_bar_configurator:configure + + foo_bar_with_static_call: + class: FooBarClass + configurator: FooBarConfigurator::configureFooBar diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_defaults_with_parent.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_defaults_with_parent.yml new file mode 100644 index 0000000..fb07a5c --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_defaults_with_parent.yml @@ -0,0 +1,11 @@ +services: + _defaults: + autowire: true + + parent_service: + class: stdClass + public: true + + child_service: + class: stdClass + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_dump_load.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_dump_load.yml new file mode 100644 index 0000000..9c25cbc --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_dump_load.yml @@ -0,0 +1,16 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + foo: + autoconfigure: true + abstract: true + arguments: ['@!bar'] + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_inline.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_inline.yml new file mode 100644 index 0000000..b985cab --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_inline.yml @@ -0,0 +1,16 @@ + +services: + service_container: + class: Symfony\Component\DependencyInjection\ContainerInterface + public: true + synthetic: true + foo: + class: Class1 + public: true + arguments: [!service { class: Class2, arguments: [!service { class: Class2 }] }] + Psr\Container\ContainerInterface: + alias: service_container + public: false + Symfony\Component\DependencyInjection\ContainerInterface: + alias: service_container + public: false diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof.yml new file mode 100644 index 0000000..a58cc07 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof.yml @@ -0,0 +1,11 @@ +services: + _instanceof: + Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface: + autowire: true + lazy: true + tags: + - { name: foo } + - { name: bar } + + Symfony\Component\DependencyInjection\Tests\Fixtures\Bar: ~ + Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface: '@Symfony\Component\DependencyInjection\Tests\Fixtures\Bar' diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml new file mode 100644 index 0000000..dfdb6cd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_instanceof_with_parent.yml @@ -0,0 +1,12 @@ +services: + _instanceof: + FooInterface: + autowire: true + + parent_service: + class: stdClass + public: true + + child_service: + class: stdClass + parent: parent_service diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_named_args.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_named_args.yml new file mode 100644 index 0000000..84242e3 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_named_args.yml @@ -0,0 +1,12 @@ +services: + _defaults: {public: true} + + Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy: { 0: ~, $apiKey: ABCD } + + another_one: + class: Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy + arguments: + $apiKey: ABCD + Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass: ~ + calls: + - ['setApiKey', { $apiKey: '123' }] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype.yml new file mode 100644 index 0000000..8c0b202 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype.yml @@ -0,0 +1,4 @@ +services: + Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\: + resource: ../Prototype + exclude: '../Prototype/{OtherDir,BadClasses}' diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace.yml new file mode 100644 index 0000000..5c30d01 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace.yml @@ -0,0 +1,10 @@ +services: + dir1: + namespace: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\ + resource: ../Prototype/OtherDir/*/Dir1 + tags: [foo] + + dir2: + namespace: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\ + resource: ../Prototype/OtherDir/*/Dir2 + tags: [bar] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace_without_resource.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace_without_resource.yml new file mode 100644 index 0000000..abd370e --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_prototype_namespace_without_resource.yml @@ -0,0 +1,4 @@ +services: + dir1: + namespace: Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\ + tags: [foo] diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_underscore.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_underscore.yml new file mode 100644 index 0000000..0cdbf90 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/services_underscore.yml @@ -0,0 +1,3 @@ +services: + _foo: + class: Foo diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml new file mode 100644 index 0000000..0ea5f53 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_empty_string.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag name is an empty string + - { name: '', foo: bar } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml new file mode 100644 index 0000000..f24cafd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_no_string.yml @@ -0,0 +1,6 @@ +services: + foo_service: + class: FooClass + tags: + # tag name is not a string + - { name: [], foo: bar } diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_only.yml b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_only.yml new file mode 100644 index 0000000..90180b0 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/tag_name_only.yml @@ -0,0 +1,5 @@ +services: + foo_service: + class: FooClass + tags: + - foo diff --git a/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/yaml_with_wrong_ext.ini b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/yaml_with_wrong_ext.ini new file mode 100644 index 0000000..1395458 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Fixtures/yaml/yaml_with_wrong_ext.ini @@ -0,0 +1,2 @@ +parameters: + with_wrong_ext: from yaml diff --git a/vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php b/vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.php new file mode 100644 index 0000000..f93965f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/LazyProxy/Instantiator/RealServiceInstantiatorTest.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\DependencyInjection\Tests\LazyProxy\Instantiator; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator; + +/** + * Tests for {@see \Symfony\Component\DependencyInjection\Instantiator\RealServiceInstantiator}. + * + * @author Marco Pivetta + */ +class RealServiceInstantiatorTest extends TestCase +{ + public function testInstantiateProxy() + { + $instantiator = new RealServiceInstantiator(); + $instance = new \stdClass(); + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $callback = function () use ($instance) { + return $instance; + }; + + $this->assertSame($instance, $instantiator->instantiateProxy($container, new Definition(), 'foo', $callback)); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.php b/vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.php new file mode 100644 index 0000000..b1b9b39 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/LazyProxy/PhpDumper/NullDumperTest.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\DependencyInjection\Tests\LazyProxy\PhpDumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; + +/** + * Tests for {@see \Symfony\Component\DependencyInjection\PhpDumper\NullDumper}. + * + * @author Marco Pivetta + */ +class NullDumperTest extends TestCase +{ + public function testNullDumper() + { + $dumper = new NullDumper(); + $definition = new Definition('stdClass'); + + $this->assertFalse($dumper->isProxyCandidate($definition)); + $this->assertSame('', $dumper->getProxyFactoryCode($definition, 'foo', '(false)')); + $this->assertSame('', $dumper->getProxyCode($definition)); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.php new file mode 100644 index 0000000..125e09b --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/ClosureLoaderTest.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\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; + +class ClosureLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new ClosureLoader(new ContainerBuilder()); + + $this->assertTrue($loader->supports(function ($container) {}), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); + } + + public function testLoad() + { + $loader = new ClosureLoader($container = new ContainerBuilder()); + + $loader->load(function ($container) { + $container->setParameter('foo', 'foo'); + }); + + $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a \Closure resource'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php new file mode 100644 index 0000000..559abbe --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/DirectoryLoaderTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + +class DirectoryLoaderTest extends TestCase +{ + private static $fixturesPath; + + private $container; + private $loader; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + } + + protected function setUp() + { + $locator = new FileLocator(self::$fixturesPath); + $this->container = new ContainerBuilder(); + $this->loader = new DirectoryLoader($this->container, $locator); + $resolver = new LoaderResolver(array( + new PhpFileLoader($this->container, $locator), + new IniFileLoader($this->container, $locator), + new YamlFileLoader($this->container, $locator), + $this->loader, + )); + $this->loader->setResolver($resolver); + } + + public function testDirectoryCanBeLoadedRecursively() + { + $this->loader->load('directory/'); + $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml', 'php' => 'php'), $this->container->getParameterBag()->all(), '->load() takes a single directory'); + } + + public function testImports() + { + $this->loader->resolve('directory/import/import.yml')->load('directory/import/import.yml'); + $this->assertEquals(array('ini' => 'ini', 'yaml' => 'yaml'), $this->container->getParameterBag()->all(), '->load() takes a single file that imports a directory'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The file "foo" does not exist (in: + */ + public function testExceptionIsRaisedWhenDirectoryDoesNotExist() + { + $this->loader->load('foo/'); + } + + public function testSupports() + { + $loader = new DirectoryLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('directory/'), '->supports("directory/") returns true'); + $this->assertTrue($loader->supports('directory/', 'directory'), '->supports("directory/", "directory") returns true'); + $this->assertFalse($loader->supports('directory'), '->supports("directory") returns false'); + $this->assertTrue($loader->supports('directory', 'directory'), '->supports("directory", "directory") returns true'); + $this->assertFalse($loader->supports('directory', 'foo'), '->supports("directory", "foo") returns false'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/FileLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/FileLoaderTest.php new file mode 100644 index 0000000..7301508 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/FileLoaderTest.php @@ -0,0 +1,241 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface as PsrContainerInterface; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Loader\FileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingParent; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\FooInterface; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\AnotherSub\DeeperBaz; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\OtherDir\Baz; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\BarInterface; + +class FileLoaderTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../'); + } + + public function testImportWithGlobPattern() + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath)); + + $resolver = new LoaderResolver(array( + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), + new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')), + new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + )); + + $loader->setResolver($resolver); + $loader->import('{F}ixtures/{xml,yaml}/services2.{yml,xml}'); + + $actual = $container->getParameterBag()->all(); + $expected = array( + 'a string', + 'foo' => 'bar', + 'values' => array( + 0, + 'integer' => 4, + 100 => null, + 'true', + true, + false, + 'on', + 'off', + 'float' => 1.3, + 1000.3, + 'a string', + array('foo', 'bar'), + ), + 'mixedcase' => array('MixedCaseKey' => 'value'), + 'constant' => PHP_EOL, + 'bar' => '%foo%', + 'escape' => '@escapeme', + 'foo_bar' => new Reference('foo_bar'), + ); + + $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); + } + + public function testRegisterClasses() + { + $container = new ContainerBuilder(); + $container->setParameter('sub_dir', 'Sub'); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + $loader->registerClasses(new Definition(), 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\\', 'Prototype/%sub_dir%/*'); + + $this->assertEquals( + array('service_container', Bar::class), + array_keys($container->getDefinitions()) + ); + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + BarInterface::class, + ), + array_keys($container->getAliases()) + ); + } + + public function testRegisterClassesWithExclude() + { + $container = new ContainerBuilder(); + $container->setParameter('other_dir', 'OtherDir'); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + $loader->registerClasses( + new Definition(), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', + 'Prototype/*', + // load everything, except OtherDir/AnotherSub & Foo.php + 'Prototype/{%other_dir%/AnotherSub,Foo.php}' + ); + + $this->assertTrue($container->has(Bar::class)); + $this->assertTrue($container->has(Baz::class)); + $this->assertFalse($container->has(Foo::class)); + $this->assertFalse($container->has(DeeperBaz::class)); + + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + BarInterface::class, + ), + array_keys($container->getAliases()) + ); + } + + public function testNestedRegisterClasses() + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + $prototype = new Definition(); + $prototype->setPublic(true)->setPrivate(true); + $loader->registerClasses($prototype, 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', 'Prototype/*'); + + $this->assertTrue($container->has(Bar::class)); + $this->assertTrue($container->has(Baz::class)); + $this->assertTrue($container->has(Foo::class)); + + $this->assertEquals( + array( + PsrContainerInterface::class, + ContainerInterface::class, + FooInterface::class, + ), + array_keys($container->getAliases()) + ); + + $alias = $container->getAlias(FooInterface::class); + $this->assertSame(Foo::class, (string) $alias); + $this->assertFalse($alias->isPublic()); + $this->assertFalse($alias->isPrivate()); + } + + public function testMissingParentClass() + { + $container = new ContainerBuilder(); + $container->setParameter('bad_classes_dir', 'BadClasses'); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + $loader->registerClasses( + (new Definition())->setPublic(false), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\\', + 'Prototype/%bad_classes_dir%/*' + ); + + $this->assertTrue($container->has(MissingParent::class)); + + $this->assertSame( + array('While discovering services from namespace "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\", an error was thrown when processing the class "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingParent": "Class Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\BadClasses\MissingClass not found".'), + $container->getDefinition(MissingParent::class)->getErrors() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /Expected to find class "Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\Prototype\\Bar" in file ".+" while importing services from resource "Prototype\/Sub\/\*", but it was not found\! Check the namespace prefix used with the resource/ + */ + public function testRegisterClassesWithBadPrefix() + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + // the Sub is missing from namespace prefix + $loader->registerClasses(new Definition(), 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', 'Prototype/Sub/*'); + } + + /** + * @dataProvider getIncompatibleExcludeTests + */ + public function testRegisterClassesWithIncompatibleExclude($resourcePattern, $excludePattern) + { + $container = new ContainerBuilder(); + $loader = new TestFileLoader($container, new FileLocator(self::$fixturesPath.'/Fixtures')); + + try { + $loader->registerClasses( + new Definition(), + 'Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\\', + $resourcePattern, + $excludePattern + ); + } catch (InvalidArgumentException $e) { + $this->assertEquals( + sprintf('Invalid "exclude" pattern when importing classes for "Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\": make sure your "exclude" pattern (%s) is a subset of the "resource" pattern (%s)', $excludePattern, $resourcePattern), + $e->getMessage() + ); + } + } + + public function getIncompatibleExcludeTests() + { + yield array('Prototype/*', 'yaml/*', false); + yield array('Prototype/OtherDir/*', 'Prototype/*', false); + } +} + +class TestFileLoader extends FileLoader +{ + public function load($resource, $type = null) + { + return $resource; + } + + public function supports($resource, $type = null) + { + return false; + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/GlobFileLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/GlobFileLoaderTest.php new file mode 100644 index 0000000..74822f5 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/GlobFileLoaderTest.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\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; + +class GlobFileLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new GlobFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('any-path', 'glob'), '->supports() returns true if the resource has the glob type'); + $this->assertFalse($loader->supports('any-path'), '->supports() returns false if the resource is not of glob type'); + } + + public function testLoadAddsTheGlobResourceToTheContainer() + { + $loader = new GlobFileLoaderWithoutImport($container = new ContainerBuilder(), new FileLocator()); + $loader->load(__DIR__.'/../Fixtures/config/*'); + + $this->assertEquals(new GlobResource(__DIR__.'/../Fixtures/config', '/*', false), $container->getResources()[1]); + } +} + +class GlobFileLoaderWithoutImport extends GlobFileLoader +{ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.php new file mode 100644 index 0000000..cb124bc --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/IniFileLoaderTest.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\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; + +class IniFileLoaderTest extends TestCase +{ + protected $container; + protected $loader; + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->loader = new IniFileLoader($this->container, new FileLocator(realpath(__DIR__.'/../Fixtures/').'/ini')); + } + + public function testIniFileCanBeLoaded() + { + $this->loader->load('parameters.ini'); + $this->assertEquals(array('foo' => 'bar', 'bar' => '%foo%'), $this->container->getParameterBag()->all(), '->load() takes a single file name as its first argument'); + } + + /** + * @dataProvider getTypeConversions + */ + public function testTypeConversions($key, $value, $supported) + { + $this->loader->load('types.ini'); + $parameters = $this->container->getParameterBag()->all(); + $this->assertSame($value, $parameters[$key], '->load() converts values to PHP types'); + } + + /** + * @dataProvider getTypeConversions + * This test illustrates where our conversions differs from INI_SCANNER_TYPED introduced in PHP 5.6.1 + */ + public function testTypeConversionsWithNativePhp($key, $value, $supported) + { + if (!$supported) { + $this->markTestSkipped(sprintf('Converting the value "%s" to "%s" is not supported by the IniFileLoader.', $key, $value)); + } + + $this->loader->load('types.ini'); + $expected = parse_ini_file(__DIR__.'/../Fixtures/ini/types.ini', true, INI_SCANNER_TYPED); + $this->assertSame($value, $expected['parameters'][$key], '->load() converts values to PHP types'); + } + + public function getTypeConversions() + { + return array( + array('true_comment', true, true), + array('true', true, true), + array('false', false, true), + array('on', true, true), + array('off', false, true), + array('yes', true, true), + array('no', false, true), + array('none', false, true), + array('null', null, true), + array('constant', PHP_VERSION, true), + array('12', 12, true), + array('12_string', '12', true), + array('12_comment', 12, true), + array('12_string_comment', '12', true), + array('12_string_comment_again', '12', true), + array('-12', -12, true), + array('1', 1, true), + array('0', 0, true), + array('0b0110', bindec('0b0110'), false), // not supported by INI_SCANNER_TYPED + array('11112222333344445555', '1111,2222,3333,4444,5555', true), + array('0777', 0777, false), // not supported by INI_SCANNER_TYPED + array('255', 0xFF, false), // not supported by INI_SCANNER_TYPED + array('100.0', 1e2, false), // not supported by INI_SCANNER_TYPED + array('-120.0', -1.2E2, false), // not supported by INI_SCANNER_TYPED + array('-10100.1', -10100.1, false), // not supported by INI_SCANNER_TYPED + array('-10,100.1', '-10,100.1', true), + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The file "foo.ini" does not exist (in: + */ + public function testExceptionIsRaisedWhenIniFileDoesNotExist() + { + $this->loader->load('foo.ini'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "nonvalid.ini" file is not valid. + */ + public function testExceptionIsRaisedWhenIniFileCannotBeParsed() + { + @$this->loader->load('nonvalid.ini'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The "almostvalid.ini" file is not valid. + */ + public function testExceptionIsRaisedWhenIniFileIsAlmostValid() + { + @$this->loader->load('almostvalid.ini'); + } + + public function testSupports() + { + $loader = new IniFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.ini'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); + $this->assertTrue($loader->supports('with_wrong_ext.yml', 'ini'), '->supports() returns true if the resource with forced type is loadable'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/LoaderResolverTest.php b/vendor/symfony/dependency-injection/Tests/Loader/LoaderResolverTest.php new file mode 100644 index 0000000..cb2d6dd --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/LoaderResolverTest.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + +class LoaderResolverTest extends TestCase +{ + private static $fixturesPath; + + /** @var LoaderResolver */ + private $resolver; + + protected function setUp() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + + $container = new ContainerBuilder(); + $this->resolver = new LoaderResolver(array( + new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), + new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')), + new ClosureLoader($container), + )); + } + + public function provideResourcesToLoad() + { + return array( + array('ini_with_wrong_ext.xml', 'ini', IniFileLoader::class), + array('xml_with_wrong_ext.php', 'xml', XmlFileLoader::class), + array('php_with_wrong_ext.yml', 'php', PhpFileLoader::class), + array('yaml_with_wrong_ext.ini', 'yaml', YamlFileLoader::class), + ); + } + + /** + * @dataProvider provideResourcesToLoad + */ + public function testResolvesForcedType($resource, $type, $expectedClass) + { + $this->assertInstanceOf($expectedClass, $this->resolver->resolve($resource, $type)); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.php new file mode 100644 index 0000000..e0b7a26 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/PhpFileLoaderTest.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\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\Dumper\YamlDumper; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +class PhpFileLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new PhpFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); + $this->assertTrue($loader->supports('with_wrong_ext.yml', 'php'), '->supports() returns true if the resource with forced type is loadable'); + } + + public function testLoad() + { + $loader = new PhpFileLoader($container = new ContainerBuilder(), new FileLocator()); + + $loader->load(__DIR__.'/../Fixtures/php/simple.php'); + + $this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a PHP file resource'); + } + + public function testConfigServices() + { + $fixtures = realpath(__DIR__.'/../Fixtures'); + $loader = new PhpFileLoader($container = new ContainerBuilder(), new FileLocator()); + $loader->load($fixtures.'/config/services9.php'); + + $container->compile(); + $dumper = new PhpDumper($container); + $this->assertStringEqualsFile($fixtures.'/php/services9_compiled.php', str_replace(str_replace('\\', '\\\\', $fixtures.\DIRECTORY_SEPARATOR.'includes'.\DIRECTORY_SEPARATOR), '%path%', $dumper->dump())); + } + + /** + * @dataProvider provideConfig + */ + public function testConfig($file) + { + $fixtures = realpath(__DIR__.'/../Fixtures'); + $loader = new PhpFileLoader($container = new ContainerBuilder(), new FileLocator()); + $loader->load($fixtures.'/config/'.$file.'.php'); + + $container->compile(); + + $dumper = new YamlDumper($container); + $this->assertStringEqualsFile($fixtures.'/config/'.$file.'.expected.yml', $dumper->dump()); + } + + public function provideConfig() + { + yield array('basic'); + yield array('defaults'); + yield array('instanceof'); + yield array('prototype'); + yield array('child'); + yield array('php7'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot have a "parent" and also have "autoconfigure". Try disabling autoconfiguration for the service. + */ + public function testAutoConfigureAndChildDefinitionNotAllowed() + { + $fixtures = realpath(__DIR__.'/../Fixtures'); + $container = new ContainerBuilder(); + $loader = new PhpFileLoader($container, new FileLocator()); + $loader->load($fixtures.'/config/services_autoconfigure_with_parent.php'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid factory "factory:method": the `service:method` notation is not available when using PHP-based DI configuration. Use "[ref('factory'), 'method']" instead. + */ + public function testFactoryShortNotationNotAllowed() + { + $fixtures = realpath(__DIR__.'/../Fixtures'); + $container = new ContainerBuilder(); + $loader = new PhpFileLoader($container, new FileLocator()); + $loader->load($fixtures.'/config/factory_short_notation.php'); + $container->compile(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php new file mode 100644 index 0000000..bd3fc51 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/XmlFileLoaderTest.php @@ -0,0 +1,798 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar; +use Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; +use Symfony\Component\ExpressionLanguage\Expression; + +class XmlFileLoaderTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + require_once self::$fixturesPath.'/includes/foo.php'; + require_once self::$fixturesPath.'/includes/ProjectExtension.php'; + require_once self::$fixturesPath.'/includes/ProjectWithXsdExtension.php'; + } + + public function testLoad() + { + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); + + try { + $loader->load('foo.xml'); + $this->fail('->load() throws an InvalidArgumentException if the loaded file does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the loaded file does not exist'); + $this->assertStringStartsWith('The file "foo.xml" does not exist (in:', $e->getMessage(), '->load() throws an InvalidArgumentException if the loaded file does not exist'); + } + } + + public function testParseFile() + { + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('parseFileToDOM'); + $m->setAccessible(true); + + try { + $m->invoke($loader, self::$fixturesPath.'/ini/parameters.ini'); + $this->fail('->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'parameters.ini'), $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + $this->assertStringStartsWith('[ERROR 4] Start tag expected, \'<\' not found (in', $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + } + + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/xml')); + + try { + $m->invoke($loader, self::$fixturesPath.'/xml/nonvalid.xml'); + $this->fail('->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'nonvalid.xml'), $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file is not a valid XML file'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + $this->assertStringStartsWith('[ERROR 1845] Element \'nonvalid\': No matching global declaration available for the validation root. (in', $e->getMessage(), '->parseFileToDOM() throws an InvalidArgumentException if the loaded file does not validate the XSD'); + } + + $xml = $m->invoke($loader, self::$fixturesPath.'/xml/services1.xml'); + $this->assertInstanceOf('DOMDocument', $xml, '->parseFileToDOM() returns an SimpleXMLElement object'); + } + + public function testLoadWithExternalEntitiesDisabled() + { + $disableEntities = libxml_disable_entity_loader(true); + + $containerBuilder = new ContainerBuilder(); + $loader = new XmlFileLoader($containerBuilder, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services2.xml'); + + libxml_disable_entity_loader($disableEntities); + + $this->assertGreaterThan(0, $containerBuilder->getParameterBag()->all(), 'Parameters can be read from the config file.'); + } + + public function testLoadParameters() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services2.xml'); + + $actual = $container->getParameterBag()->all(); + $expected = array( + 'a string', + 'foo' => 'bar', + 'values' => array( + 0, + 'integer' => 4, + 100 => null, + 'true', + true, + false, + 'on', + 'off', + 'float' => 1.3, + 1000.3, + 'a string', + array('foo', 'bar'), + ), + 'mixedcase' => array('MixedCaseKey' => 'value'), + 'constant' => PHP_EOL, + ); + + $this->assertEquals($expected, $actual, '->load() converts XML values to PHP ones'); + } + + public function testLoadImports() + { + $container = new ContainerBuilder(); + $resolver = new LoaderResolver(array( + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), + new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yml')), + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + )); + $loader->setResolver($resolver); + $loader->load('services4.xml'); + + $actual = $container->getParameterBag()->all(); + $expected = array( + 'a string', + 'foo' => 'bar', + 'values' => array( + 0, + 'integer' => 4, + 100 => null, + 'true', + true, + false, + 'on', + 'off', + 'float' => 1.3, + 1000.3, + 'a string', + array('foo', 'bar'), + ), + 'mixedcase' => array('MixedCaseKey' => 'value'), + 'constant' => PHP_EOL, + 'bar' => '%foo%', + 'imported_from_ini' => true, + 'imported_from_yaml' => true, + 'with_wrong_ext' => 'from yaml', + ); + + $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); + $this->assertTrue($actual['imported_from_ini']); + + // Bad import throws no exception due to ignore_errors value. + $loader->load('services4_bad_import.xml'); + } + + public function testLoadAnonymousServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services5.xml'); + $services = $container->getDefinitions(); + $this->assertCount(6, $services, '->load() attributes unique ids to anonymous services'); + + // anonymous service as an argument + $args = $services['foo']->getArguments(); + $this->assertCount(1, $args, '->load() references anonymous services as "normal" ones'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $args[0], '->load() converts anonymous services to references to "normal" services'); + $this->assertArrayHasKey((string) $args[0], $services, '->load() makes a reference to the created ones'); + $inner = $services[(string) $args[0]]; + $this->assertEquals('BarClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); + + // inner anonymous services + $args = $inner->getArguments(); + $this->assertCount(1, $args, '->load() references anonymous services as "normal" ones'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $args[0], '->load() converts anonymous services to references to "normal" services'); + $this->assertArrayHasKey((string) $args[0], $services, '->load() makes a reference to the created ones'); + $inner = $services[(string) $args[0]]; + $this->assertEquals('BazClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); + + // anonymous service as a property + $properties = $services['foo']->getProperties(); + $property = $properties['p']; + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Reference', $property, '->load() converts anonymous services to references to "normal" services'); + $this->assertArrayHasKey((string) $property, $services, '->load() makes a reference to the created ones'); + $inner = $services[(string) $property]; + $this->assertEquals('BuzClass', $inner->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $this->assertFalse($inner->isPublic()); + + // anonymous services are shared when using decoration definitions + $container->compile(); + $services = $container->getDefinitions(); + $fooArgs = $services['foo']->getArguments(); + $barArgs = $services['bar']->getArguments(); + $this->assertSame($fooArgs[0], $barArgs[0]); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Top-level services must have "id" attribute, none found in + */ + public function testLoadAnonymousServicesWithoutId() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_without_id.xml'); + } + + public function testLoadAnonymousNestedServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('nested_service_without_id.xml'); + + $this->assertTrue($container->hasDefinition('FooClass')); + $arguments = $container->getDefinition('FooClass')->getArguments(); + $this->assertInstanceOf(Reference::class, array_shift($arguments)); + } + + public function testLoadServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services6.xml'); + $services = $container->getDefinitions(); + $this->assertArrayHasKey('foo', $services, '->load() parses elements'); + $this->assertFalse($services['not_shared']->isShared(), '->load() parses shared flag'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts element to Definition instances'); + $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); + $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); + $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); + $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(new Reference('baz'), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag'); + $this->assertSame(array(null, 'getInstance'), $services['new_factory4']->getFactory(), '->load() accepts factory tag without class'); + + $aliases = $container->getAliases(); + $this->assertArrayHasKey('alias_for_foo', $aliases, '->load() parses elements'); + $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); + $this->assertTrue($aliases['alias_for_foo']->isPublic()); + $this->assertArrayHasKey('another_alias_for_foo', $aliases); + $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); + $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); + + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); + } + + public function testParsesIteratorArgument() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services9.xml'); + + $lazyDefinition = $container->getDefinition('lazy_context'); + + $this->assertEquals(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))), new IteratorArgument(array())), $lazyDefinition->getArguments(), '->load() parses lazy arguments'); + } + + public function testParsesTags() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services10.xml'); + + $services = $container->findTaggedServiceIds('foo_tag'); + $this->assertCount(1, $services); + + foreach ($services as $id => $tagAttributes) { + foreach ($tagAttributes as $attributes) { + $this->assertArrayHasKey('other_option', $attributes); + $this->assertEquals('lorem', $attributes['other_option']); + $this->assertArrayHasKey('other-option', $attributes, 'unnormalized tag attributes should not be removed'); + + $this->assertEquals('ciz', $attributes['some_option'], 'no overriding should be done when normalizing'); + $this->assertEquals('cat', $attributes['some-option']); + + $this->assertArrayNotHasKey('an_other_option', $attributes, 'normalization should not be done when an underscore is already found'); + } + } + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testParseTagsWithoutNameThrowsException() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('tag_without_name.xml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /The tag name for service ".+" in .* must be a non-empty string/ + */ + public function testParseTagWithEmptyNameThrowsException() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('tag_with_empty_name.xml'); + } + + public function testDeprecated() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_deprecated.xml'); + + $this->assertTrue($container->getDefinition('foo')->isDeprecated()); + $message = 'The "foo" service is deprecated. You should stop using it, as it will soon be removed.'; + $this->assertSame($message, $container->getDefinition('foo')->getDeprecationMessage('foo')); + + $this->assertTrue($container->getDefinition('bar')->isDeprecated()); + $message = 'The "bar" service is deprecated.'; + $this->assertSame($message, $container->getDefinition('bar')->getDeprecationMessage('bar')); + } + + public function testConvertDomElementToArray() + { + $doc = new \DOMDocument('1.0'); + $doc->loadXML('bar'); + $this->assertEquals('bar', XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => 'bar'), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML('bar'); + $this->assertEquals(array('foo' => 'bar'), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML('barbar'); + $this->assertEquals(array('foo' => array('value' => 'bar', 'foo' => 'bar')), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => null), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => null), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + + $doc = new \DOMDocument('1.0'); + $doc->loadXML(''); + $this->assertEquals(array('foo' => array(array('foo' => 'bar'), array('foo' => 'bar'))), XmlFileLoader::convertDomElementToArray($doc->documentElement), '::convertDomElementToArray() converts a \DomElement to an array'); + } + + public function testExtensions() + { + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $container->registerExtension(new \ProjectWithXsdExtension()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + // extension without an XSD + $loader->load('extensions/services1.xml'); + $container->compile(); + $services = $container->getDefinitions(); + $parameters = $container->getParameterBag()->all(); + + $this->assertArrayHasKey('project.service.bar', $services, '->load() parses extension elements'); + $this->assertArrayHasKey('project.parameter.bar', $parameters, '->load() parses extension elements'); + + $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); + $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); + + // extension with an XSD + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $container->registerExtension(new \ProjectWithXsdExtension()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('extensions/services2.xml'); + $container->compile(); + $services = $container->getDefinitions(); + $parameters = $container->getParameterBag()->all(); + + $this->assertArrayHasKey('project.service.bar', $services, '->load() parses extension elements'); + $this->assertArrayHasKey('project.parameter.bar', $parameters, '->load() parses extension elements'); + + $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); + $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); + + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $container->registerExtension(new \ProjectWithXsdExtension()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + // extension with an XSD (does not validate) + try { + $loader->load('extensions/services3.xml'); + $this->fail('->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'services3.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertContains('The attribute \'bar\' is not allowed', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } + + // non-registered extension + try { + $loader->load('extensions/services4.xml'); + $this->fail('->load() throws an InvalidArgumentException if the tag is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid'); + $this->assertStringStartsWith('There is no extension able to load the configuration for "project:bar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid'); + } + } + + public function testExtensionInPhar() + { + if (\extension_loaded('suhosin') && false === strpos(ini_get('suhosin.executor.include.whitelist'), 'phar')) { + $this->markTestSkipped('To run this test, add "phar" to the "suhosin.executor.include.whitelist" settings in your php.ini file.'); + } + + require_once self::$fixturesPath.'/includes/ProjectWithXsdExtensionInPhar.phar'; + + // extension with an XSD in PHAR archive + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectWithXsdExtensionInPhar()); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('extensions/services6.xml'); + + // extension with an XSD in PHAR archive (does not validate) + try { + $loader->load('extensions/services7.xml'); + $this->fail('->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'services7.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + $this->assertContains('The attribute \'bar\' is not allowed', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration does not validate the XSD'); + } + } + + public function testSupports() + { + $loader = new XmlFileLoader(new ContainerBuilder(), new FileLocator()); + + $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); + $this->assertFalse($loader->supports('foo.foo'), '->supports() returns false if the resource is not loadable'); + $this->assertTrue($loader->supports('with_wrong_ext.yml', 'xml'), '->supports() returns true if the resource with forced type is loadable'); + } + + public function testNoNamingConflictsForAnonymousServices() + { + $container = new ContainerBuilder(); + + $loader1 = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml/extension1')); + $loader1->load('services.xml'); + $services = $container->getDefinitions(); + $this->assertCount(3, $services, '->load() attributes unique ids to anonymous services'); + $loader2 = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml/extension2')); + $loader2->load('services.xml'); + $services = $container->getDefinitions(); + $this->assertCount(5, $services, '->load() attributes unique ids to anonymous services'); + + $services = $container->getDefinitions(); + $args1 = $services['extension1.foo']->getArguments(); + $inner1 = $services[(string) $args1[0]]; + $this->assertEquals('BarClass1', $inner1->getClass(), '->load() uses the same configuration as for the anonymous ones'); + $args2 = $services['extension2.foo']->getArguments(); + $inner2 = $services[(string) $args2[0]]; + $this->assertEquals('BarClass2', $inner2->getClass(), '->load() uses the same configuration as for the anonymous ones'); + } + + public function testDocTypeIsNotAllowed() + { + $container = new ContainerBuilder(); + + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + // document types are not allowed. + try { + $loader->load('withdoctype.xml'); + $this->fail('->load() throws an InvalidArgumentException if the configuration contains a document type'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Exception\\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration contains a document type'); + $this->assertRegExp(sprintf('#^Unable to parse file ".+%s".$#', 'withdoctype.xml'), $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration contains a document type'); + + $e = $e->getPrevious(); + $this->assertInstanceOf('InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the configuration contains a document type'); + $this->assertSame('Document types are not allowed.', $e->getMessage(), '->load() throws an InvalidArgumentException if the configuration contains a document type'); + } + } + + public function testXmlNamespaces() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('namespaces.xml'); + $services = $container->getDefinitions(); + + $this->assertArrayHasKey('foo', $services, '->load() parses elements'); + $this->assertCount(1, $services['foo']->getTag('foo.tag'), '->load parses elements'); + $this->assertEquals(array(array('setBar', array('foo'))), $services['foo']->getMethodCalls(), '->load() parses the tag'); + } + + public function testLoadIndexedArguments() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services14.xml'); + + $this->assertEquals(array('index_0' => 'app'), $container->findDefinition('logger')->getArguments()); + } + + public function testLoadInlinedServices() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services21.xml'); + + $foo = $container->getDefinition('foo'); + + $fooFactory = $foo->getFactory(); + $this->assertInstanceOf(Reference::class, $fooFactory[0]); + $this->assertTrue($container->has((string) $fooFactory[0])); + $fooFactoryDefinition = $container->getDefinition((string) $fooFactory[0]); + $this->assertSame('FooFactory', $fooFactoryDefinition->getClass()); + $this->assertSame('createFoo', $fooFactory[1]); + + $fooFactoryFactory = $fooFactoryDefinition->getFactory(); + $this->assertInstanceOf(Reference::class, $fooFactoryFactory[0]); + $this->assertTrue($container->has((string) $fooFactoryFactory[0])); + $this->assertSame('Foobar', $container->getDefinition((string) $fooFactoryFactory[0])->getClass()); + $this->assertSame('createFooFactory', $fooFactoryFactory[1]); + + $fooConfigurator = $foo->getConfigurator(); + $this->assertInstanceOf(Reference::class, $fooConfigurator[0]); + $this->assertTrue($container->has((string) $fooConfigurator[0])); + $fooConfiguratorDefinition = $container->getDefinition((string) $fooConfigurator[0]); + $this->assertSame('Bar', $fooConfiguratorDefinition->getClass()); + $this->assertSame('configureFoo', $fooConfigurator[1]); + + $barConfigurator = $fooConfiguratorDefinition->getConfigurator(); + $this->assertInstanceOf(Reference::class, $barConfigurator[0]); + $this->assertSame('Baz', $container->getDefinition((string) $barConfigurator[0])->getClass()); + $this->assertSame('configureBar', $barConfigurator[1]); + } + + public function testAutowire() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services23.xml'); + + $this->assertTrue($container->getDefinition('bar')->isAutowired()); + } + + public function testClassFromId() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('class_from_id.xml'); + $container->compile(); + + $this->assertEquals(CaseSensitiveClass::class, $container->getDefinition(CaseSensitiveClass::class)->getClass()); + } + + public function testPrototype() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_prototype.xml'); + + $ids = array_keys($container->getDefinitions()); + sort($ids); + $this->assertSame(array(Prototype\Foo::class, Prototype\Sub\Bar::class, 'service_container'), $ids); + + $resources = $container->getResources(); + + $fixturesDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR; + $this->assertTrue(false !== array_search(new FileResource($fixturesDir.'xml'.\DIRECTORY_SEPARATOR.'services_prototype.xml'), $resources)); + $this->assertTrue(false !== array_search(new GlobResource($fixturesDir.'Prototype', '/*', true), $resources)); + $resources = array_map('strval', $resources); + $this->assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo', $resources); + $this->assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar', $resources); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Invalid attribute "class" defined for alias "bar" in + */ + public function testAliasDefinitionContainsUnsupportedElements() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + + $loader->load('invalid_alias_definition.xml'); + + $this->assertTrue($container->has('bar')); + } + + public function testArgumentWithKeyOutsideCollection() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('with_key_outside_collection.xml'); + + $this->assertSame(array('type' => 'foo', 'bar'), $container->getDefinition('foo')->getArguments()); + } + + public function testDefaults() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services28.xml'); + + $this->assertFalse($container->getDefinition('with_defaults')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); + $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); + $this->assertArrayNotHasKey('public', $container->getDefinition('with_defaults')->getChanges()); + $this->assertArrayNotHasKey('autowire', $container->getDefinition('with_defaults')->getChanges()); + + $container->compile(); + + $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); + + $this->assertSame(array('foo' => array(array())), $container->getDefinition('no_defaults')->getTags()); + + $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); + + $this->assertTrue($container->getDefinition('child_def')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('child_def')->getTags()); + $this->assertFalse($container->getDefinition('child_def')->isAutowired()); + + $definitions = $container->getDefinitions(); + $this->assertSame('service_container', key($definitions)); + + array_shift($definitions); + $anonymous = current($definitions); + $this->assertSame('bar', key($definitions)); + $this->assertTrue($anonymous->isPublic()); + $this->assertTrue($anonymous->isAutowired()); + $this->assertSame(array('foo' => array(array())), $anonymous->getTags()); + } + + public function testNamedArguments() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_named_args.xml'); + + $this->assertEquals(array('$apiKey' => 'ABCD', CaseSensitiveClass::class => null), $container->getDefinition(NamedArgumentsDummy::class)->getArguments()); + + $container->compile(); + + $this->assertEquals(array(null, 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments()); + $this->assertEquals(array(array('setApiKey', array('123'))), $container->getDefinition(NamedArgumentsDummy::class)->getMethodCalls()); + } + + public function testInstanceof() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_instanceof.xml'); + $container->compile(); + + $definition = $container->getDefinition(Bar::class); + $this->assertTrue($definition->isAutowired()); + $this->assertTrue($definition->isLazy()); + $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot use the "parent" option in the same file where "instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file. + */ + public function testInstanceOfAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_instanceof_with_parent.xml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot have a "parent" and also have "autoconfigure". Try setting autoconfigure="false" for the service. + */ + public function testAutoConfigureAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_autoconfigure_with_parent.xml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Attribute "autowire" on service "child_service" cannot be inherited from "defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly. + */ + public function testDefaultsAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_defaults_with_parent.xml'); + $container->compile(); + } + + public function testAutoConfigureInstanceof() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_autoconfigure.xml'); + + $this->assertTrue($container->getDefinition('use_defaults_settings')->isAutoconfigured()); + $this->assertFalse($container->getDefinition('override_defaults_settings_to_false')->isAutoconfigured()); + } + + public function testCaseSensitivity() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_case.xml'); + $container->compile(); + + $this->assertTrue($container->has('bar')); + $this->assertTrue($container->has('BAR')); + $this->assertFalse($container->has('baR')); + $this->assertNotSame($container->get('BAR'), $container->get('bar')); + $this->assertSame($container->get('BAR')->arguments->bar, $container->get('bar')); + $this->assertSame($container->get('BAR')->bar, $container->get('bar')); + } + + public function testBindings() + { + $container = new ContainerBuilder(); + $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')); + $loader->load('services_bindings.xml'); + $container->compile(); + + $definition = $container->getDefinition('bar'); + $this->assertEquals(array( + 'NonExistent' => null, + BarInterface::class => new Reference(Bar::class), + '$foo' => array(null), + '$quz' => 'quz', + '$factory' => 'factory', + ), array_map(function ($v) { return $v->getValues()[0]; }, $definition->getBindings())); + $this->assertEquals(array( + 'quz', + null, + new Reference(Bar::class), + array(null), + ), $definition->getArguments()); + + $definition = $container->getDefinition(Bar::class); + $this->assertEquals(array( + null, + 'factory', + ), $definition->getArguments()); + $this->assertEquals(array( + 'NonExistent' => null, + '$quz' => 'quz', + '$factory' => 'factory', + ), array_map(function ($v) { return $v->getValues()[0]; }, $definition->getBindings())); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php new file mode 100644 index 0000000..62cf3a6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/Loader/YamlFileLoaderTest.php @@ -0,0 +1,741 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar; +use Symfony\Component\DependencyInjection\Tests\Fixtures\BarInterface; +use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; +use Symfony\Component\DependencyInjection\Tests\Fixtures\NamedArgumentsDummy; +use Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype; +use Symfony\Component\ExpressionLanguage\Expression; + +class YamlFileLoaderTest extends TestCase +{ + protected static $fixturesPath; + + public static function setUpBeforeClass() + { + self::$fixturesPath = realpath(__DIR__.'/../Fixtures/'); + require_once self::$fixturesPath.'/includes/foo.php'; + require_once self::$fixturesPath.'/includes/ProjectExtension.php'; + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /The file ".+" does not exist./ + */ + public function testLoadUnExistFile() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/ini')); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('loadFile'); + $m->setAccessible(true); + + $m->invoke($loader, 'foo.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /The file ".+" does not contain valid YAML./ + */ + public function testLoadInvalidYamlFile() + { + $path = self::$fixturesPath.'/ini'; + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator($path)); + $r = new \ReflectionObject($loader); + $m = $r->getMethod('loadFile'); + $m->setAccessible(true); + + $m->invoke($loader, $path.'/parameters.ini'); + } + + /** + * @dataProvider provideInvalidFiles + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testLoadInvalidFile($file) + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + + $loader->load($file.'.yml'); + } + + public function provideInvalidFiles() + { + return array( + array('bad_parameters'), + array('bad_imports'), + array('bad_import'), + array('bad_services'), + array('bad_service'), + array('bad_calls'), + array('bad_format'), + array('nonvalid1'), + array('nonvalid2'), + ); + } + + public function testLoadParameters() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services2.yml'); + $this->assertEquals(array('foo' => 'bar', 'mixedcase' => array('MixedCaseKey' => 'value'), 'values' => array(true, false, 0, 1000.3, PHP_INT_MAX), 'bar' => 'foo', 'escape' => '@escapeme', 'foo_bar' => new Reference('foo_bar')), $container->getParameterBag()->all(), '->load() converts YAML keys to lowercase'); + } + + public function testLoadImports() + { + $container = new ContainerBuilder(); + $resolver = new LoaderResolver(array( + new IniFileLoader($container, new FileLocator(self::$fixturesPath.'/ini')), + new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml')), + new PhpFileLoader($container, new FileLocator(self::$fixturesPath.'/php')), + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')), + )); + $loader->setResolver($resolver); + $loader->load('services4.yml'); + + $actual = $container->getParameterBag()->all(); + $expected = array( + 'foo' => 'bar', + 'values' => array(true, false, PHP_INT_MAX), + 'bar' => '%foo%', + 'escape' => '@escapeme', + 'foo_bar' => new Reference('foo_bar'), + 'mixedcase' => array('MixedCaseKey' => 'value'), + 'imported_from_ini' => true, + 'imported_from_xml' => true, + 'with_wrong_ext' => 'from yaml', + ); + $this->assertEquals(array_keys($expected), array_keys($actual), '->load() imports and merges imported files'); + $this->assertTrue($actual['imported_from_ini']); + + // Bad import throws no exception due to ignore_errors value. + $loader->load('services4_bad_import.yml'); + } + + public function testLoadServices() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services6.yml'); + $services = $container->getDefinitions(); + $this->assertArrayHasKey('foo', $services, '->load() parses service elements'); + $this->assertFalse($services['not_shared']->isShared(), '->load() parses the shared flag'); + $this->assertInstanceOf('Symfony\\Component\\DependencyInjection\\Definition', $services['foo'], '->load() converts service element to Definition instances'); + $this->assertEquals('FooClass', $services['foo']->getClass(), '->load() parses the class attribute'); + $this->assertEquals('%path%/foo.php', $services['file']->getFile(), '->load() parses the file tag'); + $this->assertEquals(array('foo', new Reference('foo'), array(true, false)), $services['arguments']->getArguments(), '->load() parses the argument tags'); + $this->assertEquals('sc_configure', $services['configurator1']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(new Reference('baz'), 'configure'), $services['configurator2']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array('BazClass', 'configureStatic'), $services['configurator3']->getConfigurator(), '->load() parses the configurator tag'); + $this->assertEquals(array(array('setBar', array()), array('setBar', array()), array('setBar', array(new Expression('service("foo").foo() ~ (container.hasParameter("foo") ? parameter("foo") : "default")')))), $services['method_call1']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals(array(array('setBar', array('foo', new Reference('foo'), array(true, false)))), $services['method_call2']->getMethodCalls(), '->load() parses the method_call tag'); + $this->assertEquals('factory', $services['new_factory1']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['new_factory2']->getFactory(), '->load() parses the factory tag'); + $this->assertEquals(array('BazClass', 'getInstance'), $services['new_factory3']->getFactory(), '->load() parses the factory tag'); + $this->assertSame(array(null, 'getInstance'), $services['new_factory4']->getFactory(), '->load() accepts factory tag without class'); + $this->assertEquals(array('foo', new Reference('baz')), $services['Acme\WithShortCutArgs']->getArguments(), '->load() parses short service definition'); + + $aliases = $container->getAliases(); + $this->assertArrayHasKey('alias_for_foo', $aliases, '->load() parses aliases'); + $this->assertEquals('foo', (string) $aliases['alias_for_foo'], '->load() parses aliases'); + $this->assertTrue($aliases['alias_for_foo']->isPublic()); + $this->assertArrayHasKey('another_alias_for_foo', $aliases); + $this->assertEquals('foo', (string) $aliases['another_alias_for_foo']); + $this->assertFalse($aliases['another_alias_for_foo']->isPublic()); + $this->assertTrue(isset($aliases['another_third_alias_for_foo'])); + $this->assertEquals('foo', (string) $aliases['another_third_alias_for_foo']); + $this->assertTrue($aliases['another_third_alias_for_foo']->isPublic()); + + $this->assertEquals(array('decorated', null, 0), $services['decorator_service']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 0), $services['decorator_service_with_name']->getDecoratedService()); + $this->assertEquals(array('decorated', 'decorated.pif-pouf', 5), $services['decorator_service_with_name_and_priority']->getDecoratedService()); + } + + public function testLoadFactoryShortSyntax() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services14.yml'); + $services = $container->getDefinitions(); + + $this->assertEquals(array(new Reference('baz'), 'getClass'), $services['factory']->getFactory(), '->load() parses the factory tag with service:method'); + $this->assertEquals(array('FooBacFactory', 'createFooBar'), $services['factory_with_static_call']->getFactory(), '->load() parses the factory tag with Class::method'); + } + + public function testLoadConfiguratorShortSyntax() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_configurator_short_syntax.yml'); + $services = $container->getDefinitions(); + + $this->assertEquals(array(new Reference('foo_bar_configurator'), 'configure'), $services['foo_bar']->getConfigurator(), '->load() parses the configurator tag with service:method'); + $this->assertEquals(array('FooBarConfigurator', 'configureFooBar'), $services['foo_bar_with_static_call']->getConfigurator(), '->load() parses the configurator tag with Class::method'); + } + + public function testExtensions() + { + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services10.yml'); + $container->compile(); + $services = $container->getDefinitions(); + $parameters = $container->getParameterBag()->all(); + + $this->assertArrayHasKey('project.service.bar', $services, '->load() parses extension elements'); + $this->assertArrayHasKey('project.parameter.bar', $parameters, '->load() parses extension elements'); + + $this->assertEquals('BAR', $services['project.service.foo']->getClass(), '->load() parses extension elements'); + $this->assertEquals('BAR', $parameters['project.parameter.foo'], '->load() parses extension elements'); + + try { + $loader->load('services11.yml'); + $this->fail('->load() throws an InvalidArgumentException if the tag is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tag is not valid'); + $this->assertStringStartsWith('There is no extension able to load the configuration for "foobarfoobar" (in', $e->getMessage(), '->load() throws an InvalidArgumentException if the tag is not valid'); + } + } + + public function testExtensionWithNullConfig() + { + $container = new ContainerBuilder(); + $container->registerExtension(new \ProjectExtension()); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('null_config.yml'); + $container->compile(); + + $this->assertSame(array(null), $container->getParameter('project.configs')); + } + + public function testSupports() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator()); + + $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 false if the resource is not loadable'); + $this->assertTrue($loader->supports('with_wrong_ext.xml', 'yml'), '->supports() returns true if the resource with forced type is loadable'); + $this->assertTrue($loader->supports('with_wrong_ext.xml', 'yaml'), '->supports() returns true if the resource with forced type is loadable'); + } + + public function testNonArrayTagsThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + try { + $loader->load('badtag1.yml'); + $this->fail('->load() should throw an exception when the tags key of a service is not an array'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if the tags key is not an array'); + $this->assertStringStartsWith('Parameter "tags" must be an array for service', $e->getMessage(), '->load() throws an InvalidArgumentException if the tags key is not an array'); + } + } + + public function testTagWithoutNameThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + try { + $loader->load('badtag2.yml'); + $this->fail('->load() should throw an exception when a tag is missing the name key'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag is missing the name key'); + $this->assertStringStartsWith('A "tags" entry is missing a "name" key for service ', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag is missing the name key'); + } + } + + public function testNameOnlyTagsAreAllowedAsString() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('tag_name_only.yml'); + + $this->assertCount(1, $container->getDefinition('foo_service')->getTag('foo')); + } + + public function testTagWithAttributeArrayThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + try { + $loader->load('badtag3.yml'); + $this->fail('->load() should throw an exception when a tag-attribute is not a scalar'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\InvalidArgumentException', $e, '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); + $this->assertStringStartsWith('A "tags" attribute must be of a scalar-type for service "foo_service", tag "foo", attribute "bar"', $e->getMessage(), '->load() throws an InvalidArgumentException if a tag-attribute is not a scalar'); + } + } + + public function testLoadYamlOnlyWithKeys() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services21.yml'); + + $definition = $container->getDefinition('manager'); + $this->assertEquals(array(array('setLogger', array(new Reference('logger'))), array('setClass', array('User'))), $definition->getMethodCalls()); + $this->assertEquals(array(true), $definition->getArguments()); + $this->assertEquals(array('manager' => array(array('alias' => 'user'))), $definition->getTags()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /The tag name for service ".+" in .+ must be a non-empty string/ + */ + public function testTagWithEmptyNameThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('tag_name_empty_string.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageREgExp /The tag name for service "\.+" must be a non-empty string/ + */ + public function testTagWithNonStringNameThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('tag_name_no_string.yml'); + } + + public function testParsesIteratorArgument() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services9.yml'); + + $lazyDefinition = $container->getDefinition('lazy_context'); + + $this->assertEquals(array(new IteratorArgument(array('k1' => new Reference('foo.baz'), 'k2' => new Reference('service_container'))), new IteratorArgument(array())), $lazyDefinition->getArguments(), '->load() parses lazy arguments'); + } + + public function testAutowire() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services23.yml'); + + $this->assertTrue($container->getDefinition('bar_service')->isAutowired()); + } + + public function testClassFromId() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('class_from_id.yml'); + $container->compile(); + + $this->assertEquals(CaseSensitiveClass::class, $container->getDefinition(CaseSensitiveClass::class)->getClass()); + } + + public function testPrototype() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_prototype.yml'); + + $ids = array_keys($container->getDefinitions()); + sort($ids); + $this->assertSame(array(Prototype\Foo::class, Prototype\Sub\Bar::class, 'service_container'), $ids); + + $resources = $container->getResources(); + + $fixturesDir = \dirname(__DIR__).\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR; + $this->assertTrue(false !== array_search(new FileResource($fixturesDir.'yaml'.\DIRECTORY_SEPARATOR.'services_prototype.yml'), $resources)); + $this->assertTrue(false !== array_search(new GlobResource($fixturesDir.'Prototype', '', true), $resources)); + $resources = array_map('strval', $resources); + $this->assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Foo', $resources); + $this->assertContains('reflection.Symfony\Component\DependencyInjection\Tests\Fixtures\Prototype\Sub\Bar', $resources); + } + + public function testPrototypeWithNamespace() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_prototype_namespace.yml'); + + $ids = array_keys($container->getDefinitions()); + sort($ids); + + $this->assertSame(array( + Prototype\OtherDir\Component1\Dir1\Service1::class, + Prototype\OtherDir\Component1\Dir2\Service2::class, + Prototype\OtherDir\Component2\Dir1\Service4::class, + Prototype\OtherDir\Component2\Dir2\Service5::class, + 'service_container', + ), $ids); + + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component1\Dir1\Service1::class)->hasTag('foo')); + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component2\Dir1\Service4::class)->hasTag('foo')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component1\Dir1\Service1::class)->hasTag('bar')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component2\Dir1\Service4::class)->hasTag('bar')); + + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component1\Dir2\Service2::class)->hasTag('bar')); + $this->assertTrue($container->getDefinition(Prototype\OtherDir\Component2\Dir2\Service5::class)->hasTag('bar')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component1\Dir2\Service2::class)->hasTag('foo')); + $this->assertFalse($container->getDefinition(Prototype\OtherDir\Component2\Dir2\Service5::class)->hasTag('foo')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /A "resource" attribute must be set when the "namespace" attribute is set for service ".+" in .+/ + */ + public function testPrototypeWithNamespaceAndNoResource() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_prototype_namespace_without_resource.yml'); + } + + public function testDefaults() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services28.yml'); + + $this->assertFalse($container->getDefinition('with_defaults')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags()); + $this->assertTrue($container->getDefinition('with_defaults')->isAutowired()); + $this->assertArrayNotHasKey('public', $container->getDefinition('with_defaults')->getChanges()); + $this->assertArrayNotHasKey('autowire', $container->getDefinition('with_defaults')->getChanges()); + + $this->assertFalse($container->getAlias('with_defaults_aliased')->isPublic()); + $this->assertFalse($container->getAlias('with_defaults_aliased_short')->isPublic()); + + $this->assertFalse($container->getDefinition('Acme\WithShortCutArgs')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('Acme\WithShortCutArgs')->getTags()); + $this->assertTrue($container->getDefinition('Acme\WithShortCutArgs')->isAutowired()); + + $container->compile(); + + $this->assertTrue($container->getDefinition('with_null')->isPublic()); + $this->assertTrue($container->getDefinition('no_defaults')->isPublic()); + + // foo tag is inherited from defaults + $this->assertSame(array('foo' => array(array())), $container->getDefinition('with_null')->getTags()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('no_defaults')->getTags()); + + $this->assertTrue($container->getDefinition('with_null')->isAutowired()); + $this->assertFalse($container->getDefinition('no_defaults')->isAutowired()); + + $this->assertTrue($container->getDefinition('child_def')->isPublic()); + $this->assertSame(array('foo' => array(array())), $container->getDefinition('child_def')->getTags()); + $this->assertFalse($container->getDefinition('child_def')->isAutowired()); + } + + public function testNamedArguments() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_named_args.yml'); + + $this->assertEquals(array(null, '$apiKey' => 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments()); + $this->assertEquals(array('$apiKey' => 'ABCD', CaseSensitiveClass::class => null), $container->getDefinition('another_one')->getArguments()); + + $container->compile(); + + $this->assertEquals(array(null, 'ABCD'), $container->getDefinition(NamedArgumentsDummy::class)->getArguments()); + $this->assertEquals(array(null, 'ABCD'), $container->getDefinition('another_one')->getArguments()); + $this->assertEquals(array(array('setApiKey', array('123'))), $container->getDefinition('another_one')->getMethodCalls()); + } + + public function testInstanceof() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_instanceof.yml'); + $container->compile(); + + $definition = $container->getDefinition(Bar::class); + $this->assertTrue($definition->isAutowired()); + $this->assertTrue($definition->isLazy()); + $this->assertSame(array('foo' => array(array()), 'bar' => array(array())), $definition->getTags()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot use the "parent" option in the same file where "_instanceof" configuration is defined as using both is not supported. Move your child definitions to a separate file. + */ + public function testInstanceOfAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_instanceof_with_parent.yml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The service "child_service" cannot have a "parent" and also have "autoconfigure". Try setting "autoconfigure: false" for the service. + */ + public function testAutoConfigureAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_autoconfigure_with_parent.yml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Attribute "autowire" on service "child_service" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly. + */ + public function testDefaultsAndChildDefinitionNotAllowed() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_defaults_with_parent.yml'); + $container->compile(); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo"). + */ + public function testDecoratedServicesWithWrongSyntaxThrowsException() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_decorates.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /Parameter "tags" must be an array for service "Foo\\Bar" in .+services31_invalid_tags\.yml\. Check your YAML syntax./ + */ + public function testInvalidTagsWithDefaults() + { + $loader = new YamlFileLoader(new ContainerBuilder(), new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services31_invalid_tags.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Service names that start with an underscore are reserved. Rename the "_foo" service or define it in XML instead. + */ + public function testUnderscoreServiceId() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_underscore.yml'); + } + + public function testAnonymousServices() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('anonymous_services.yml'); + + $definition = $container->getDefinition('Foo'); + $this->assertTrue($definition->isAutowired()); + + // Anonymous service in an argument + $args = $definition->getArguments(); + $this->assertCount(1, $args); + $this->assertInstanceOf(Reference::class, $args[0]); + $this->assertTrue($container->has((string) $args[0])); + $this->assertRegExp('/^\d+_Bar[._A-Za-z0-9]{7}$/', (string) $args[0]); + + $anonymous = $container->getDefinition((string) $args[0]); + $this->assertEquals('Bar', $anonymous->getClass()); + $this->assertFalse($anonymous->isPublic()); + $this->assertTrue($anonymous->isAutowired()); + + // Anonymous service in a callable + $factory = $definition->getFactory(); + $this->assertInternalType('array', $factory); + $this->assertInstanceOf(Reference::class, $factory[0]); + $this->assertTrue($container->has((string) $factory[0])); + $this->assertRegExp('/^\d+_Quz[._A-Za-z0-9]{7}$/', (string) $factory[0]); + $this->assertEquals('constructFoo', $factory[1]); + + $anonymous = $container->getDefinition((string) $factory[0]); + $this->assertEquals('Quz', $anonymous->getClass()); + $this->assertFalse($anonymous->isPublic()); + $this->assertFalse($anonymous->isAutowired()); + } + + public function testAnonymousServicesInDifferentFilesWithSameNameDoNotConflict() + { + $container = new ContainerBuilder(); + + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml/foo')); + $loader->load('services.yml'); + + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml/bar')); + $loader->load('services.yml'); + + $this->assertCount(5, $container->getDefinitions()); + } + + public function testAnonymousServicesInInstanceof() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('anonymous_services_in_instanceof.yml'); + + $definition = $container->getDefinition('Dummy'); + + $instanceof = $definition->getInstanceofConditionals(); + $this->assertCount(3, $instanceof); + $this->assertArrayHasKey('DummyInterface', $instanceof); + + $args = $instanceof['DummyInterface']->getProperties(); + $this->assertCount(1, $args); + $this->assertInstanceOf(Reference::class, $args['foo']); + $this->assertTrue($container->has((string) $args['foo'])); + + $anonymous = $container->getDefinition((string) $args['foo']); + $this->assertEquals('Anonymous', $anonymous->getClass()); + $this->assertFalse($anonymous->isPublic()); + $this->assertEmpty($anonymous->getInstanceofConditionals()); + + $this->assertFalse($container->has('Bar')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /Creating an alias using the tag "!service" is not allowed in ".+anonymous_services_alias\.yml"\./ + */ + public function testAnonymousServicesWithAliases() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('anonymous_services_alias.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /Using an anonymous service in a parameter is not allowed in ".+anonymous_services_in_parameters\.yml"\./ + */ + public function testAnonymousServicesInParameters() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('anonymous_services_in_parameters.yml'); + } + + public function testAutoConfigureInstanceof() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_autoconfigure.yml'); + + $this->assertTrue($container->getDefinition('use_defaults_settings')->isAutoconfigured()); + $this->assertFalse($container->getDefinition('override_defaults_settings_to_false')->isAutoconfigured()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /Service "_defaults" key must be an array, "NULL" given in ".+bad_empty_defaults\.yml"\./ + */ + public function testEmptyDefaultsThrowsClearException() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_empty_defaults.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /Service "_instanceof" key must be an array, "NULL" given in ".+bad_empty_instanceof\.yml"\./ + */ + public function testEmptyInstanceofThrowsClearException() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_empty_instanceof.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /^The configuration key "private" is unsupported for definition "bar"/ + */ + public function testUnsupportedKeywordThrowsException() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_keyword.yml'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessageRegExp /^The configuration key "calls" is unsupported for the service "bar" which is defined as an alias/ + */ + public function testUnsupportedKeywordInServiceAliasThrowsException() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('bad_alias.yml'); + } + + public function testCaseSensitivity() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_case.yml'); + $container->compile(); + + $this->assertTrue($container->has('bar')); + $this->assertTrue($container->has('BAR')); + $this->assertFalse($container->has('baR')); + $this->assertNotSame($container->get('BAR'), $container->get('bar')); + $this->assertSame($container->get('BAR')->arguments->bar, $container->get('bar')); + $this->assertSame($container->get('BAR')->bar, $container->get('bar')); + } + + public function testBindings() + { + $container = new ContainerBuilder(); + $loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml')); + $loader->load('services_bindings.yml'); + $container->compile(); + + $definition = $container->getDefinition('bar'); + $this->assertEquals(array( + 'NonExistent' => null, + BarInterface::class => new Reference(Bar::class), + '$foo' => array(null), + '$quz' => 'quz', + '$factory' => 'factory', + ), array_map(function ($v) { return $v->getValues()[0]; }, $definition->getBindings())); + $this->assertEquals(array( + 'quz', + null, + new Reference(Bar::class), + array(null), + ), $definition->getArguments()); + + $definition = $container->getDefinition(Bar::class); + $this->assertEquals(array( + null, + 'factory', + ), $definition->getArguments()); + $this->assertEquals(array( + 'NonExistent' => null, + '$quz' => 'quz', + '$factory' => 'factory', + ), array_map(function ($v) { return $v->getValues()[0]; }, $definition->getBindings())); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php b/vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.php new file mode 100644 index 0000000..9abfb45 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ParameterBag/EnvPlaceholderParameterBagTest.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\DependencyInjection\Tests\ParameterBag; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; + +class EnvPlaceholderParameterBagTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + */ + public function testGetThrowsInvalidArgumentExceptionIfEnvNameContainsNonWordCharacters() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->get('env(%foo%)'); + } + + public function testMergeWillNotDuplicateIdenticalParameters() + { + $envVariableName = 'DB_HOST'; + $parameter = sprintf('env(%s)', $envVariableName); + $firstBag = new EnvPlaceholderParameterBag(); + + // initialize placeholders + $firstBag->get($parameter); + $secondBag = clone $firstBag; + + $firstBag->mergeEnvPlaceholders($secondBag); + $mergedPlaceholders = $firstBag->getEnvPlaceholders(); + + $placeholderForVariable = $mergedPlaceholders[$envVariableName]; + $placeholder = array_values($placeholderForVariable)[0]; + + $this->assertCount(1, $placeholderForVariable); + $this->assertInternalType('string', $placeholder); + $this->assertContains($envVariableName, $placeholder); + } + + public function testMergeWhereFirstBagIsEmptyWillWork() + { + $envVariableName = 'DB_HOST'; + $parameter = sprintf('env(%s)', $envVariableName); + $firstBag = new EnvPlaceholderParameterBag(); + $secondBag = new EnvPlaceholderParameterBag(); + + // initialize placeholder only in second bag + $secondBag->get($parameter); + + $this->assertEmpty($firstBag->getEnvPlaceholders()); + + $firstBag->mergeEnvPlaceholders($secondBag); + $mergedPlaceholders = $firstBag->getEnvPlaceholders(); + + $placeholderForVariable = $mergedPlaceholders[$envVariableName]; + $placeholder = array_values($placeholderForVariable)[0]; + + $this->assertCount(1, $placeholderForVariable); + $this->assertInternalType('string', $placeholder); + $this->assertContains($envVariableName, $placeholder); + } + + public function testMergeWherePlaceholderOnlyExistsInSecond() + { + $uniqueEnvName = 'DB_HOST'; + $commonEnvName = 'DB_USER'; + + $uniqueParamName = sprintf('env(%s)', $uniqueEnvName); + $commonParamName = sprintf('env(%s)', $commonEnvName); + + $firstBag = new EnvPlaceholderParameterBag(); + // initialize common placeholder + $firstBag->get($commonParamName); + $secondBag = clone $firstBag; + + // initialize unique placeholder + $secondBag->get($uniqueParamName); + + $firstBag->mergeEnvPlaceholders($secondBag); + $merged = $firstBag->getEnvPlaceholders(); + + $this->assertCount(1, $merged[$uniqueEnvName]); + // second bag has same placeholder for commonEnvName + $this->assertCount(1, $merged[$commonEnvName]); + } + + public function testMergeWithDifferentIdentifiersForPlaceholders() + { + $envName = 'DB_USER'; + $paramName = sprintf('env(%s)', $envName); + + $firstBag = new EnvPlaceholderParameterBag(); + $secondBag = new EnvPlaceholderParameterBag(); + // initialize placeholders + $firstPlaceholder = $firstBag->get($paramName); + $secondPlaceholder = $secondBag->get($paramName); + + $firstBag->mergeEnvPlaceholders($secondBag); + $merged = $firstBag->getEnvPlaceholders(); + + $this->assertNotEquals($firstPlaceholder, $secondPlaceholder); + $this->assertCount(2, $merged[$envName]); + } + + public function testResolveEnvCastsIntToString() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->get('env(INT_VAR)'); + $bag->set('env(INT_VAR)', 2); + $bag->resolve(); + $this->assertSame('2', $bag->all()['env(INT_VAR)']); + } + + public function testResolveEnvAllowsNull() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->get('env(NULL_VAR)'); + $bag->set('env(NULL_VAR)', null); + $bag->resolve(); + $this->assertNull($bag->all()['env(NULL_VAR)']); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The default value of env parameter "ARRAY_VAR" must be scalar or null, array given. + */ + public function testResolveThrowsOnBadDefaultValue() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->get('env(ARRAY_VAR)'); + $bag->set('env(ARRAY_VAR)', array()); + $bag->resolve(); + } + + public function testGetEnvAllowsNull() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->set('env(NULL_VAR)', null); + $bag->get('env(NULL_VAR)'); + $bag->resolve(); + + $this->assertNull($bag->all()['env(NULL_VAR)']); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The default value of an env() parameter must be scalar or null, but "array" given to "env(ARRAY_VAR)". + */ + public function testGetThrowsOnBadDefaultValue() + { + $bag = new EnvPlaceholderParameterBag(); + $bag->set('env(ARRAY_VAR)', array()); + $bag->get('env(ARRAY_VAR)'); + $bag->resolve(); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.php b/vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.php new file mode 100644 index 0000000..ef9a66f --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ParameterBag/FrozenParameterBagTest.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\DependencyInjection\Tests\ParameterBag; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; + +class FrozenParameterBagTest extends TestCase +{ + public function testConstructor() + { + $parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + ); + $bag = new FrozenParameterBag($parameters); + $this->assertEquals($parameters, $bag->all(), '__construct() takes an array of parameters as its first argument'); + } + + /** + * @expectedException \LogicException + */ + public function testClear() + { + $bag = new FrozenParameterBag(array()); + $bag->clear(); + } + + /** + * @expectedException \LogicException + */ + public function testSet() + { + $bag = new FrozenParameterBag(array()); + $bag->set('foo', 'bar'); + } + + /** + * @expectedException \LogicException + */ + public function testAdd() + { + $bag = new FrozenParameterBag(array()); + $bag->add(array()); + } + + /** + * @expectedException \LogicException + */ + public function testRemove() + { + $bag = new FrozenParameterBag(array('foo' => 'bar')); + $bag->remove('foo'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php b/vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php new file mode 100644 index 0000000..3d79742 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ParameterBag/ParameterBagTest.php @@ -0,0 +1,260 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\ParameterBag; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +class ParameterBagTest extends TestCase +{ + public function testConstructor() + { + $bag = new ParameterBag($parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $this->assertEquals($parameters, $bag->all(), '__construct() takes an array of parameters as its first argument'); + } + + public function testClear() + { + $bag = new ParameterBag($parameters = array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $bag->clear(); + $this->assertEquals(array(), $bag->all(), '->clear() removes all parameters'); + } + + public function testRemove() + { + $bag = new ParameterBag(array( + 'foo' => 'foo', + 'bar' => 'bar', + )); + $bag->remove('foo'); + $this->assertEquals(array('bar' => 'bar'), $bag->all(), '->remove() removes a parameter'); + } + + public function testGetSet() + { + $bag = new ParameterBag(array('foo' => 'bar')); + $bag->set('bar', 'foo'); + $this->assertEquals('foo', $bag->get('bar'), '->set() sets the value of a new parameter'); + + $bag->set('foo', 'baz'); + $this->assertEquals('baz', $bag->get('foo'), '->set() overrides previously set parameter'); + + try { + $bag->get('baba'); + $this->fail('->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException', $e, '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + $this->assertEquals('You have requested a non-existent parameter "baba".', $e->getMessage(), '->get() throws an Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException if the key does not exist'); + } + } + + /** + * @dataProvider provideGetThrowParameterNotFoundExceptionData + */ + public function testGetThrowParameterNotFoundException($parameterKey, $exceptionMessage) + { + $bag = new ParameterBag(array( + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => 'baz', + 'fiz' => array('bar' => array('boo' => 12)), + )); + + if (method_exists($this, 'expectException')) { + $this->expectException(ParameterNotFoundException::class); + $this->expectExceptionMessage($exceptionMessage); + } else { + $this->setExpectedException(ParameterNotFoundException::class, $exceptionMessage); + } + + $bag->get($parameterKey); + } + + public function provideGetThrowParameterNotFoundExceptionData() + { + return array( + array('foo1', 'You have requested a non-existent parameter "foo1". Did you mean this: "foo"?'), + array('bag', 'You have requested a non-existent parameter "bag". Did you mean one of these: "bar", "baz"?'), + array('', 'You have requested a non-existent parameter "".'), + + array('fiz.bar.boo', 'You have requested a non-existent parameter "fiz.bar.boo". You cannot access nested array items, do you want to inject "fiz" instead?'), + ); + } + + 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('bar'), '->has() returns false if a parameter is not defined'); + } + + public function testMixedCase() + { + $bag = new ParameterBag(array( + 'foo' => 'foo', + 'bar' => 'bar', + 'BAR' => 'baz', + )); + + $bag->remove('BAR'); + $this->assertEquals(array('foo' => 'foo', 'bar' => 'bar'), $bag->all()); + + $bag->set('Foo', 'baz1'); + $this->assertEquals('foo', $bag->get('foo')); + $this->assertEquals('baz1', $bag->get('Foo')); + } + + public function testResolveValue() + { + $bag = new ParameterBag(array()); + $this->assertEquals('foo', $bag->resolveValue('foo'), '->resolveValue() returns its argument unmodified if no placeholders are found'); + + $bag = new ParameterBag(array('foo' => 'bar')); + $this->assertEquals('I\'m a bar', $bag->resolveValue('I\'m a %foo%'), '->resolveValue() replaces placeholders by their values'); + $this->assertEquals(array('bar' => 'bar'), $bag->resolveValue(array('%foo%' => '%foo%')), '->resolveValue() replaces placeholders in keys and values of arrays'); + $this->assertEquals(array('bar' => array('bar' => array('bar' => 'bar'))), $bag->resolveValue(array('%foo%' => array('%foo%' => array('%foo%' => '%foo%')))), '->resolveValue() replaces placeholders in nested arrays'); + $this->assertEquals('I\'m a %%foo%%', $bag->resolveValue('I\'m a %%foo%%'), '->resolveValue() supports % escaping by doubling it'); + $this->assertEquals('I\'m a bar %%foo bar', $bag->resolveValue('I\'m a %foo% %%foo %foo%'), '->resolveValue() supports % escaping by doubling it'); + $this->assertEquals(array('foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar'))), $bag->resolveValue(array('foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar')))), '->resolveValue() supports % escaping by doubling it'); + + $bag = new ParameterBag(array('foo' => true)); + $this->assertTrue($bag->resolveValue('%foo%'), '->resolveValue() replaces arguments that are just a placeholder by their value without casting them to strings'); + $bag = new ParameterBag(array('foo' => null)); + $this->assertNull($bag->resolveValue('%foo%'), '->resolveValue() replaces arguments that are just a placeholder by their value without casting them to strings'); + + $bag = new ParameterBag(array('foo' => 'bar', 'baz' => '%%%foo% %foo%%% %%foo%% %%%foo%%%')); + $this->assertEquals('%%bar bar%% %%foo%% %%bar%%', $bag->resolveValue('%baz%'), '->resolveValue() replaces params placed besides escaped %'); + + $bag = new ParameterBag(array('baz' => '%%s?%%s')); + $this->assertEquals('%%s?%%s', $bag->resolveValue('%baz%'), '->resolveValue() is not replacing greedily'); + + $bag = new ParameterBag(array()); + try { + $bag->resolveValue('%foobar%'); + $this->fail('->resolveValue() throws an InvalidArgumentException if a placeholder references a non-existent parameter'); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('You have requested a non-existent parameter "foobar".', $e->getMessage(), '->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); + } + + try { + $bag->resolveValue('foo %foobar% bar'); + $this->fail('->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('You have requested a non-existent parameter "foobar".', $e->getMessage(), '->resolveValue() throws a ParameterNotFoundException if a placeholder references a non-existent parameter'); + } + + $bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => array())); + try { + $bag->resolveValue('%foo%'); + $this->fail('->resolveValue() throws a RuntimeException when a parameter embeds another non-string parameter'); + } catch (RuntimeException $e) { + $this->assertEquals('A string value must be composed of strings and/or numbers, but found parameter "bar" of type array inside string value "a %bar%".', $e->getMessage(), '->resolveValue() throws a RuntimeException when a parameter embeds another non-string parameter'); + } + + $bag = new ParameterBag(array('foo' => '%bar%', 'bar' => '%foobar%', 'foobar' => '%foo%')); + try { + $bag->resolveValue('%foo%'); + $this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } catch (ParameterCircularReferenceException $e) { + $this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } + + $bag = new ParameterBag(array('foo' => 'a %bar%', 'bar' => 'a %foobar%', 'foobar' => 'a %foo%')); + try { + $bag->resolveValue('%foo%'); + $this->fail('->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } catch (ParameterCircularReferenceException $e) { + $this->assertEquals('Circular reference detected for parameter "foo" ("foo" > "bar" > "foobar" > "foo").', $e->getMessage(), '->resolveValue() throws a ParameterCircularReferenceException when a parameter has a circular reference'); + } + + $bag = new ParameterBag(array('host' => 'foo.bar', 'port' => 1337)); + $this->assertEquals('foo.bar:1337', $bag->resolveValue('%host%:%port%')); + } + + public function testResolveIndicatesWhyAParameterIsNeeded() + { + $bag = new ParameterBag(array('foo' => '%bar%')); + + try { + $bag->resolve(); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('The parameter "foo" has a dependency on a non-existent parameter "bar".', $e->getMessage()); + } + + $bag = new ParameterBag(array('foo' => '%bar%')); + + try { + $bag->resolve(); + } catch (ParameterNotFoundException $e) { + $this->assertEquals('The parameter "foo" has a dependency on a non-existent parameter "bar".', $e->getMessage()); + } + } + + public function testResolveUnescapesValue() + { + $bag = new ParameterBag(array( + 'foo' => array('bar' => array('ding' => 'I\'m a bar %%foo %%bar')), + 'bar' => 'I\'m a %%foo%%', + )); + + $bag->resolve(); + + $this->assertEquals('I\'m a %foo%', $bag->get('bar'), '->resolveValue() supports % escaping by doubling it'); + $this->assertEquals(array('bar' => array('ding' => 'I\'m a bar %foo %bar')), $bag->get('foo'), '->resolveValue() supports % escaping by doubling it'); + } + + public function testEscapeValue() + { + $bag = new ParameterBag(); + + $bag->add(array( + 'foo' => $bag->escapeValue(array('bar' => array('ding' => 'I\'m a bar %foo %bar', 'zero' => null))), + 'bar' => $bag->escapeValue('I\'m a %foo%'), + )); + + $this->assertEquals('I\'m a %%foo%%', $bag->get('bar'), '->escapeValue() escapes % by doubling it'); + $this->assertEquals(array('bar' => array('ding' => 'I\'m a bar %%foo %%bar', 'zero' => null)), $bag->get('foo'), '->escapeValue() escapes % by doubling it'); + } + + /** + * @dataProvider stringsWithSpacesProvider + */ + public function testResolveStringWithSpacesReturnsString($expected, $test, $description) + { + $bag = new ParameterBag(array('foo' => 'bar')); + + try { + $this->assertEquals($expected, $bag->resolveString($test), $description); + } catch (ParameterNotFoundException $e) { + $this->fail(sprintf('%s - "%s"', $description, $expected)); + } + } + + public function stringsWithSpacesProvider() + { + return array( + array('bar', '%foo%', 'Parameters must be wrapped by %.'), + array('% foo %', '% foo %', 'Parameters should not have spaces.'), + array('{% set my_template = "foo" %}', '{% set my_template = "foo" %}', 'Twig-like strings are not parameters.'), + array('50% is less than 100%', '50% is less than 100%', 'Text between % signs is allowed, if there are spaces.'), + ); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ParameterTest.php b/vendor/symfony/dependency-injection/Tests/ParameterTest.php new file mode 100644 index 0000000..975e558 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ParameterTest.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\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Parameter; + +class ParameterTest extends TestCase +{ + public function testConstructor() + { + $ref = new Parameter('foo'); + $this->assertEquals('foo', (string) $ref, '__construct() sets the id of the parameter, which is used for the __toString() method'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ReferenceTest.php b/vendor/symfony/dependency-injection/Tests/ReferenceTest.php new file mode 100644 index 0000000..1fc274a --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ReferenceTest.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\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Reference; + +class ReferenceTest extends TestCase +{ + public function testConstructor() + { + $ref = new Reference('foo'); + $this->assertEquals('foo', (string) $ref, '__construct() sets the id of the reference, which is used for the __toString() method'); + } +} diff --git a/vendor/symfony/dependency-injection/Tests/ServiceLocatorTest.php b/vendor/symfony/dependency-injection/Tests/ServiceLocatorTest.php new file mode 100644 index 0000000..56fac64 --- /dev/null +++ b/vendor/symfony/dependency-injection/Tests/ServiceLocatorTest.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\DependencyInjection\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; + +class ServiceLocatorTest extends TestCase +{ + public function testHas() + { + $locator = new ServiceLocator(array( + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + function () { return 'dummy'; }, + )); + + $this->assertTrue($locator->has('foo')); + $this->assertTrue($locator->has('bar')); + $this->assertFalse($locator->has('dummy')); + } + + public function testGet() + { + $locator = new ServiceLocator(array( + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + )); + + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('baz', $locator->get('bar')); + } + + public function testGetDoesNotMemoize() + { + $i = 0; + $locator = new ServiceLocator(array( + 'foo' => function () use (&$i) { + ++$i; + + return 'bar'; + }, + )); + + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame('bar', $locator->get('foo')); + $this->assertSame(2, $i); + } + + /** + * @expectedException \Psr\Container\NotFoundExceptionInterface + * @expectedExceptionMessage Service "dummy" not found: the container inside "Symfony\Component\DependencyInjection\Tests\ServiceLocatorTest" is a smaller service locator that only knows about the "foo" and "bar" services. + */ + public function testGetThrowsOnUndefinedService() + { + $locator = new ServiceLocator(array( + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + )); + + $locator->get('dummy'); + } + + /** + * @expectedException \Psr\Container\NotFoundExceptionInterface + * @expectedExceptionMessage The service "foo" has a dependency on a non-existent service "bar". This locator only knows about the "foo" service. + */ + public function testThrowsOnUndefinedInternalService() + { + $locator = new ServiceLocator(array( + 'foo' => function () use (&$locator) { return $locator->get('bar'); }, + )); + + $locator->get('foo'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException + * @expectedExceptionMessage Circular reference detected for service "bar", path: "bar -> baz -> bar". + */ + public function testThrowsOnCircularReference() + { + $locator = new ServiceLocator(array( + 'foo' => function () use (&$locator) { return $locator->get('bar'); }, + 'bar' => function () use (&$locator) { return $locator->get('baz'); }, + 'baz' => function () use (&$locator) { return $locator->get('bar'); }, + )); + + $locator->get('foo'); + } + + /** + * @expectedException \Psr\Container\NotFoundExceptionInterface + * @expectedExceptionMessage Service "foo" not found: even though it exists in the app's container, the container inside "caller" is a smaller service locator that only knows about the "bar" service. Unless you need extra laziness, try using dependency injection instead. Otherwise, you need to declare it using "SomeServiceSubscriber::getSubscribedServices()". + */ + public function testThrowsInServiceSubscriber() + { + $container = new Container(); + $container->set('foo', new \stdClass()); + $subscriber = new SomeServiceSubscriber(); + $subscriber->container = new ServiceLocator(array('bar' => function () {})); + $subscriber->container = $subscriber->container->withContext('caller', $container); + + $subscriber->getFoo(); + } + + public function testInvoke() + { + $locator = new ServiceLocator(array( + 'foo' => function () { return 'bar'; }, + 'bar' => function () { return 'baz'; }, + )); + + $this->assertSame('bar', $locator('foo')); + $this->assertSame('baz', $locator('bar')); + $this->assertNull($locator('dummy'), '->__invoke() should return null on invalid service'); + } +} + +class SomeServiceSubscriber implements ServiceSubscriberinterface +{ + public $container; + + public function getFoo() + { + return $this->container->get('foo'); + } + + public static function getSubscribedServices() + { + return array('bar' => 'stdClass'); + } +} diff --git a/vendor/symfony/dependency-injection/TypedReference.php b/vendor/symfony/dependency-injection/TypedReference.php new file mode 100644 index 0000000..dc869ec --- /dev/null +++ b/vendor/symfony/dependency-injection/TypedReference.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\DependencyInjection; + +/** + * Represents a PHP type-hinted service reference. + * + * @author Nicolas Grekas + */ +class TypedReference extends Reference +{ + private $type; + private $requiringClass; + + /** + * @param string $id The service identifier + * @param string $type The PHP type of the identified service + * @param string $requiringClass The class of the service that requires the referenced type + * @param int $invalidBehavior The behavior when the service does not exist + */ + public function __construct(string $id, string $type, string $requiringClass = '', int $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) + { + parent::__construct($id, $invalidBehavior); + $this->type = $type; + $this->requiringClass = $requiringClass; + } + + public function getType() + { + return $this->type; + } + + public function getRequiringClass() + { + return $this->requiringClass; + } + + public function canBeAutoregistered() + { + return $this->requiringClass && (false !== $i = strpos($this->type, '\\')) && 0 === strncasecmp($this->type, $this->requiringClass, 1 + $i); + } +} diff --git a/vendor/symfony/dependency-injection/Variable.php b/vendor/symfony/dependency-injection/Variable.php new file mode 100644 index 0000000..95e28d6 --- /dev/null +++ b/vendor/symfony/dependency-injection/Variable.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\DependencyInjection; + +/** + * Represents a variable. + * + * $var = new Variable('a'); + * + * will be dumped as + * + * $a + * + * by the PHP dumper. + * + * @author Johannes M. Schmitt + */ +class Variable +{ + private $name; + + public function __construct(string $name) + { + $this->name = $name; + } + + public function __toString() + { + return $this->name; + } +} diff --git a/vendor/symfony/dependency-injection/composer.json b/vendor/symfony/dependency-injection/composer.json new file mode 100644 index 0000000..1a2fef0 --- /dev/null +++ b/vendor/symfony/dependency-injection/composer.json @@ -0,0 +1,55 @@ +{ + "name": "symfony/dependency-injection", + "type": "library", + "description": "Symfony DependencyInjection 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": "^7.1.3", + "psr/container": "^1.0" + }, + "require-dev": { + "symfony/yaml": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0" + }, + "suggest": { + "symfony/yaml": "", + "symfony/config": "", + "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", + "symfony/expression-language": "For using expressions in service container configuration", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/finder": "<3.4", + "symfony/proxy-manager-bridge": "<3.4", + "symfony/yaml": "<3.4" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\DependencyInjection\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + } +} diff --git a/vendor/symfony/dependency-injection/phpunit.xml.dist b/vendor/symfony/dependency-injection/phpunit.xml.dist new file mode 100644 index 0000000..781f767 --- /dev/null +++ b/vendor/symfony/dependency-injection/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/event-dispatcher/.gitignore b/vendor/symfony/event-dispatcher/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/event-dispatcher/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/event-dispatcher/CHANGELOG.md b/vendor/symfony/event-dispatcher/CHANGELOG.md new file mode 100644 index 0000000..b581d31 --- /dev/null +++ b/vendor/symfony/event-dispatcher/CHANGELOG.md @@ -0,0 +1,55 @@ +CHANGELOG +========= + +4.1.0 +----- + + * added support for invokable event listeners tagged with `kernel.event_listener` by default + * The `TraceableEventDispatcher::getOrphanedEvents()` method has been added. + * The `TraceableEventDispatcherInterface` has been deprecated. + +4.0.0 +----- + + * removed the `ContainerAwareEventDispatcher` class + * added the `reset()` method to the `TraceableEventDispatcherInterface` + +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/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php new file mode 100644 index 0000000..aae3275 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.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\EventDispatcher\Debug; + +use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * 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; + private $orphanedEvents; + + 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(); + $this->orphanedEvents = 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 getOrphanedEvents(): array + { + return $this->orphanedEvents; + } + + public function reset() + { + $this->called = array(); + $this->orphanedEvents = 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) + { + if (!$this->dispatcher->hasListeners($eventName)) { + $this->orphanedEvents[] = $eventName; + + return; + } + + 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/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php new file mode 100644 index 0000000..d716f19 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.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\EventDispatcher\Debug; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * @deprecated since Symfony 4.1 + * + * @author Fabien Potencier + */ +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(); + + /** + * Resets the trace. + */ + public function reset(); +} diff --git a/vendor/symfony/event-dispatcher/Debug/WrappedListener.php b/vendor/symfony/event-dispatcher/Debug/WrappedListener.php new file mode 100644 index 0000000..2d8126a --- /dev/null +++ b/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\EventDispatcher\Event; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Stopwatch\Stopwatch; +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/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php new file mode 100644 index 0000000..4c8db4c --- /dev/null +++ b/vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.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\EventDispatcher\DependencyInjection; + +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +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; + + public function __construct(string $dispatcherService = 'event_dispatcher', string $listenerTag = 'kernel.event_listener', string $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']); + + if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) { + $event['method'] = '__invoke'; + } + } + + $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 = $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(EventSubscriberInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class)); + } + $class = $r->name; + + 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/vendor/symfony/event-dispatcher/Event.php b/vendor/symfony/event-dispatcher/Event.php new file mode 100644 index 0000000..9c56b2f --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/EventDispatcher.php new file mode 100644 index 0000000..4e75c63 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/EventDispatcherInterface.php b/vendor/symfony/event-dispatcher/EventDispatcherInterface.php new file mode 100644 index 0000000..d3d0cb8 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/EventSubscriberInterface.php b/vendor/symfony/event-dispatcher/EventSubscriberInterface.php new file mode 100644 index 0000000..8af7789 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/GenericEvent.php b/vendor/symfony/event-dispatcher/GenericEvent.php new file mode 100644 index 0000000..95c9940 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php b/vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php new file mode 100644 index 0000000..b3cf56c --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/LICENSE b/vendor/symfony/event-dispatcher/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/README.md b/vendor/symfony/event-dispatcher/README.md new file mode 100644 index 0000000..185c3fe --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/AbstractEventDispatcherTest.php new file mode 100644 index 0000000..9997a7b --- /dev/null +++ b/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, 'postFoo')); + $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/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 0000000..b5c5352 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -0,0 +1,293 @@ + + * + * 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\Event; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +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 testItReturnsNoOrphanedEventsWhenCreated() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertEmpty($events); + } + + public function testItReturnsOrphanedEventsAfterDispatch() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->dispatch('foo'); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertCount(1, $events); + $this->assertEquals(array('foo'), $events); + } + + public function testItDoesNotReturnHandledEvents() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->addListener('foo', function () {}); + $tdispatcher->dispatch('foo'); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertEmpty($events); + } + + 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'); + } + + public function testClearOrphanedEvents() + { + $tdispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $tdispatcher->dispatch('foo'); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertCount(1, $events); + $tdispatcher->reset(); + $events = $tdispatcher->getOrphanedEvents(); + $this->assertCount(0, $events); + } +} + +class EventSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array('foo' => 'call'); + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php new file mode 100644 index 0000000..a359bd7 --- /dev/null +++ b/vendor/symfony/event-dispatcher/Tests/DependencyInjection/RegisterListenersPassTest.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\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() + { + $builder = new ContainerBuilder(); + $builder->register('event_dispatcher'); + $builder->register('my_event_subscriber', 'stdClass') + ->addTag('kernel.event_subscriber'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + } + + public function testValidEventSubscriber() + { + $services = array( + 'my_event_subscriber' => array(0 => array()), + ); + + $builder = new ContainerBuilder(); + $eventDispatcherDefinition = $builder->register('event_dispatcher'); + $builder->register('my_event_subscriber', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService') + ->addTag('kernel.event_subscriber'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($builder); + + $expectedCalls = array( + array( + 'addListener', + array( + 'event', + array(new ServiceClosureArgument(new Reference('my_event_subscriber')), 'onEvent'), + 0, + ), + ), + ); + $this->assertEquals($expectedCalls, $eventDispatcherDefinition->getMethodCalls()); + } + + /** + * @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); + } + + public function testInvokableEventListener() + { + $container = new ContainerBuilder(); + $container->register('foo', \stdClass::class)->addTag('kernel.event_listener', array('event' => 'foo.bar')); + $container->register('bar', InvokableListenerService::class)->addTag('kernel.event_listener', array('event' => 'foo.bar')); + $container->register('baz', InvokableListenerService::class)->addTag('kernel.event_listener', array('event' => 'event')); + $container->register('event_dispatcher', \stdClass::class); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expectedCalls = array( + array( + 'addListener', + array( + 'foo.bar', + array(new ServiceClosureArgument(new Reference('foo')), 'onFooBar'), + 0, + ), + ), + array( + 'addListener', + array( + 'foo.bar', + array(new ServiceClosureArgument(new Reference('bar')), '__invoke'), + 0, + ), + ), + array( + 'addListener', + array( + 'event', + array(new ServiceClosureArgument(new Reference('baz')), 'onEvent'), + 0, + ), + ), + ); + $this->assertEquals($expectedCalls, $definition->getMethodCalls()); + } +} + +class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'event' => 'onEvent', + ); + } +} + +class InvokableListenerService +{ + public function __invoke() + { + } + + public function onEvent() + { + } +} diff --git a/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/EventDispatcherTest.php new file mode 100644 index 0000000..5faa5c8 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/Tests/EventTest.php b/vendor/symfony/event-dispatcher/Tests/EventTest.php new file mode 100644 index 0000000..5be2ea0 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php b/vendor/symfony/event-dispatcher/Tests/GenericEventTest.php new file mode 100644 index 0000000..9cf68c9 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php b/vendor/symfony/event-dispatcher/Tests/ImmutableEventDispatcherTest.php new file mode 100644 index 0000000..04f2861 --- /dev/null +++ b/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/vendor/symfony/event-dispatcher/composer.json b/vendor/symfony/event-dispatcher/composer.json new file mode 100644 index 0000000..12ee532 --- /dev/null +++ b/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": "^7.1.3" + }, + "require-dev": { + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4" + }, + "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": "4.1-dev" + } + } +} diff --git a/vendor/symfony/event-dispatcher/phpunit.xml.dist b/vendor/symfony/event-dispatcher/phpunit.xml.dist new file mode 100644 index 0000000..b3ad1bd --- /dev/null +++ b/vendor/symfony/event-dispatcher/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/expression-language/.gitignore b/vendor/symfony/expression-language/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/expression-language/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/expression-language/CHANGELOG.md b/vendor/symfony/expression-language/CHANGELOG.md new file mode 100644 index 0000000..6c50b2e --- /dev/null +++ b/vendor/symfony/expression-language/CHANGELOG.md @@ -0,0 +1,20 @@ +CHANGELOG +========= + +4.0.0 +----- + + * the first argument of the `ExpressionLanguage` constructor must be an instance + of `CacheItemPoolInterface` + * removed the `ArrayParserCache` and `ParserCacheAdapter` classes + * removed the `ParserCacheInterface` + +2.6.0 +----- + + * Added ExpressionFunction and ExpressionFunctionProviderInterface + +2.4.0 +----- + + * added the component diff --git a/vendor/symfony/expression-language/Compiler.php b/vendor/symfony/expression-language/Compiler.php new file mode 100644 index 0000000..acfb618 --- /dev/null +++ b/vendor/symfony/expression-language/Compiler.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage; + +/** + * Compiles a node to PHP code. + * + * @author Fabien Potencier + */ +class Compiler +{ + private $source; + private $functions; + + public function __construct(array $functions) + { + $this->functions = $functions; + } + + public function getFunction($name) + { + return $this->functions[$name]; + } + + /** + * Gets the current PHP code after compilation. + * + * @return string The PHP code + */ + public function getSource() + { + return $this->source; + } + + public function reset() + { + $this->source = ''; + + return $this; + } + + /** + * Compiles a node. + * + * @return $this + */ + public function compile(Node\Node $node) + { + $node->compile($this); + + return $this; + } + + public function subcompile(Node\Node $node) + { + $current = $this->source; + $this->source = ''; + + $node->compile($this); + + $source = $this->source; + $this->source = $current; + + return $source; + } + + /** + * Adds a raw string to the compiled code. + * + * @param string $string The string + * + * @return $this + */ + public function raw($string) + { + $this->source .= $string; + + return $this; + } + + /** + * Adds a quoted string to the compiled code. + * + * @param string $value The string + * + * @return $this + */ + public function string($value) + { + $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); + + return $this; + } + + /** + * Returns a PHP representation of a given value. + * + * @param mixed $value The value to convert + * + * @return $this + */ + public function repr($value) + { + if (\is_int($value) || \is_float($value)) { + if (false !== $locale = setlocale(LC_NUMERIC, 0)) { + setlocale(LC_NUMERIC, 'C'); + } + + $this->raw($value); + + if (false !== $locale) { + setlocale(LC_NUMERIC, $locale); + } + } elseif (null === $value) { + $this->raw('null'); + } elseif (\is_bool($value)) { + $this->raw($value ? 'true' : 'false'); + } elseif (\is_array($value)) { + $this->raw('array('); + $first = true; + foreach ($value as $key => $value) { + if (!$first) { + $this->raw(', '); + } + $first = false; + $this->repr($key); + $this->raw(' => '); + $this->repr($value); + } + $this->raw(')'); + } else { + $this->string($value); + } + + return $this; + } +} diff --git a/vendor/symfony/expression-language/Expression.php b/vendor/symfony/expression-language/Expression.php new file mode 100644 index 0000000..59d0e2a --- /dev/null +++ b/vendor/symfony/expression-language/Expression.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\ExpressionLanguage; + +/** + * Represents an expression. + * + * @author Fabien Potencier + */ +class Expression +{ + protected $expression; + + public function __construct(string $expression) + { + $this->expression = $expression; + } + + /** + * Gets the expression. + * + * @return string The expression + */ + public function __toString() + { + return $this->expression; + } +} diff --git a/vendor/symfony/expression-language/ExpressionFunction.php b/vendor/symfony/expression-language/ExpressionFunction.php new file mode 100644 index 0000000..968d80f --- /dev/null +++ b/vendor/symfony/expression-language/ExpressionFunction.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage; + +/** + * Represents a function that can be used in an expression. + * + * A function is defined by two PHP callables. The callables are used + * by the language to compile and/or evaluate the function. + * + * The "compiler" function is used at compilation time and must return a + * PHP representation of the function call (it receives the function + * arguments as arguments). + * + * The "evaluator" function is used for expression evaluation and must return + * the value of the function call based on the values defined for the + * expression (it receives the values as a first argument and the function + * arguments as remaining arguments). + * + * @author Fabien Potencier + */ +class ExpressionFunction +{ + private $name; + private $compiler; + private $evaluator; + + /** + * @param string $name The function name + * @param callable $compiler A callable able to compile the function + * @param callable $evaluator A callable able to evaluate the function + */ + public function __construct(string $name, callable $compiler, callable $evaluator) + { + $this->name = $name; + $this->compiler = $compiler; + $this->evaluator = $evaluator; + } + + public function getName() + { + return $this->name; + } + + public function getCompiler() + { + return $this->compiler; + } + + public function getEvaluator() + { + return $this->evaluator; + } + + /** + * Creates an ExpressionFunction from a PHP function name. + * + * @param string $phpFunctionName The PHP function name + * @param string|null $expressionFunctionName The expression function name (default: same than the PHP function name) + * + * @return self + * + * @throws \InvalidArgumentException if given PHP function name does not exist + * @throws \InvalidArgumentException if given PHP function name is in namespace + * and expression function name is not defined + */ + public static function fromPhp($phpFunctionName, $expressionFunctionName = null) + { + $phpFunctionName = ltrim($phpFunctionName, '\\'); + if (!\function_exists($phpFunctionName)) { + throw new \InvalidArgumentException(sprintf('PHP function "%s" does not exist.', $phpFunctionName)); + } + + $parts = explode('\\', $phpFunctionName); + if (!$expressionFunctionName && \count($parts) > 1) { + throw new \InvalidArgumentException(sprintf('An expression function name must be defined when PHP function "%s" is namespaced.', $phpFunctionName)); + } + + $compiler = function () use ($phpFunctionName) { + return sprintf('\%s(%s)', $phpFunctionName, implode(', ', \func_get_args())); + }; + + $evaluator = function () use ($phpFunctionName) { + $args = \func_get_args(); + + return \call_user_func_array($phpFunctionName, array_splice($args, 1)); + }; + + return new self($expressionFunctionName ?: end($parts), $compiler, $evaluator); + } +} diff --git a/vendor/symfony/expression-language/ExpressionFunctionProviderInterface.php b/vendor/symfony/expression-language/ExpressionFunctionProviderInterface.php new file mode 100644 index 0000000..414b013 --- /dev/null +++ b/vendor/symfony/expression-language/ExpressionFunctionProviderInterface.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\ExpressionLanguage; + +/** + * @author Fabien Potencier + */ +interface ExpressionFunctionProviderInterface +{ + /** + * @return ExpressionFunction[] An array of Function instances + */ + public function getFunctions(); +} diff --git a/vendor/symfony/expression-language/ExpressionLanguage.php b/vendor/symfony/expression-language/ExpressionLanguage.php new file mode 100644 index 0000000..851fe00 --- /dev/null +++ b/vendor/symfony/expression-language/ExpressionLanguage.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; + +/** + * Allows to compile and evaluate expressions written in your own DSL. + * + * @author Fabien Potencier + */ +class ExpressionLanguage +{ + private $cache; + private $lexer; + private $parser; + private $compiler; + + protected $functions = array(); + + /** + * @param CacheItemPoolInterface $cache + * @param ExpressionFunctionProviderInterface[] $providers + */ + public function __construct(CacheItemPoolInterface $cache = null, array $providers = array()) + { + $this->cache = $cache ?: new ArrayAdapter(); + $this->registerFunctions(); + foreach ($providers as $provider) { + $this->registerProvider($provider); + } + } + + /** + * Compiles an expression source code. + * + * @param Expression|string $expression The expression to compile + * @param array $names An array of valid names + * + * @return string The compiled PHP source code + */ + public function compile($expression, $names = array()) + { + return $this->getCompiler()->compile($this->parse($expression, $names)->getNodes())->getSource(); + } + + /** + * Evaluate an expression. + * + * @param Expression|string $expression The expression to compile + * @param array $values An array of values + * + * @return mixed The result of the evaluation of the expression + */ + public function evaluate($expression, $values = array()) + { + return $this->parse($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values); + } + + /** + * Parses an expression. + * + * @param Expression|string $expression The expression to parse + * @param array $names An array of valid names + * + * @return ParsedExpression A ParsedExpression instance + */ + public function parse($expression, $names) + { + if ($expression instanceof ParsedExpression) { + return $expression; + } + + asort($names); + $cacheKeyItems = array(); + + foreach ($names as $nameKey => $name) { + $cacheKeyItems[] = \is_int($nameKey) ? $name : $nameKey.':'.$name; + } + + $cacheItem = $this->cache->getItem(rawurlencode($expression.'//'.implode('|', $cacheKeyItems))); + + if (null === $parsedExpression = $cacheItem->get()) { + $nodes = $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names); + $parsedExpression = new ParsedExpression((string) $expression, $nodes); + + $cacheItem->set($parsedExpression); + $this->cache->save($cacheItem); + } + + return $parsedExpression; + } + + /** + * Registers a function. + * + * @param string $name The function name + * @param callable $compiler A callable able to compile the function + * @param callable $evaluator A callable able to evaluate the function + * + * @throws \LogicException when registering a function after calling evaluate(), compile() or parse() + * + * @see ExpressionFunction + */ + public function register($name, callable $compiler, callable $evaluator) + { + if (null !== $this->parser) { + throw new \LogicException('Registering functions after calling evaluate(), compile() or parse() is not supported.'); + } + + $this->functions[$name] = array('compiler' => $compiler, 'evaluator' => $evaluator); + } + + public function addFunction(ExpressionFunction $function) + { + $this->register($function->getName(), $function->getCompiler(), $function->getEvaluator()); + } + + public function registerProvider(ExpressionFunctionProviderInterface $provider) + { + foreach ($provider->getFunctions() as $function) { + $this->addFunction($function); + } + } + + protected function registerFunctions() + { + $this->addFunction(ExpressionFunction::fromPhp('constant')); + } + + private function getLexer(): Lexer + { + if (null === $this->lexer) { + $this->lexer = new Lexer(); + } + + return $this->lexer; + } + + private function getParser(): Parser + { + if (null === $this->parser) { + $this->parser = new Parser($this->functions); + } + + return $this->parser; + } + + private function getCompiler(): Compiler + { + if (null === $this->compiler) { + $this->compiler = new Compiler($this->functions); + } + + return $this->compiler->reset(); + } +} diff --git a/vendor/symfony/expression-language/LICENSE b/vendor/symfony/expression-language/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/expression-language/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/vendor/symfony/expression-language/Lexer.php b/vendor/symfony/expression-language/Lexer.php new file mode 100644 index 0000000..bd54e89 --- /dev/null +++ b/vendor/symfony/expression-language/Lexer.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\ExpressionLanguage; + +/** + * Lexes an expression. + * + * @author Fabien Potencier + */ +class Lexer +{ + /** + * Tokenizes an expression. + * + * @param string $expression The expression to tokenize + * + * @return TokenStream A token stream instance + * + * @throws SyntaxError + */ + public function tokenize($expression) + { + $expression = str_replace(array("\r", "\n", "\t", "\v", "\f"), ' ', $expression); + $cursor = 0; + $tokens = array(); + $brackets = array(); + $end = \strlen($expression); + + while ($cursor < $end) { + if (' ' == $expression[$cursor]) { + ++$cursor; + + continue; + } + + if (preg_match('/[0-9]+(?:\.[0-9]+)?/A', $expression, $match, 0, $cursor)) { + // numbers + $number = (float) $match[0]; // floats + if (preg_match('/^[0-9]+$/', $match[0]) && $number <= PHP_INT_MAX) { + $number = (int) $match[0]; // integers lower than the maximum + } + $tokens[] = new Token(Token::NUMBER_TYPE, $number, $cursor + 1); + $cursor += \strlen($match[0]); + } elseif (false !== strpos('([{', $expression[$cursor])) { + // opening bracket + $brackets[] = array($expression[$cursor], $cursor); + + $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); + ++$cursor; + } elseif (false !== strpos(')]}', $expression[$cursor])) { + // closing bracket + if (empty($brackets)) { + throw new SyntaxError(sprintf('Unexpected "%s"', $expression[$cursor]), $cursor, $expression); + } + + list($expect, $cur) = array_pop($brackets); + if ($expression[$cursor] != strtr($expect, '([{', ')]}')) { + throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression); + } + + $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); + ++$cursor; + } elseif (preg_match('/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As', $expression, $match, 0, $cursor)) { + // strings + $tokens[] = new Token(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)), $cursor + 1); + $cursor += \strlen($match[0]); + } elseif (preg_match('/not in(?=[\s(])|\!\=\=|not(?=[\s(])|and(?=[\s(])|\=\=\=|\>\=|or(?=[\s(])|\<\=|\*\*|\.\.|in(?=[\s(])|&&|\|\||matches|\=\=|\!\=|\*|~|%|\/|\>|\||\!|\^|&|\+|\<|\-/A', $expression, $match, 0, $cursor)) { + // operators + $tokens[] = new Token(Token::OPERATOR_TYPE, $match[0], $cursor + 1); + $cursor += \strlen($match[0]); + } elseif (false !== strpos('.,?:', $expression[$cursor])) { + // punctuation + $tokens[] = new Token(Token::PUNCTUATION_TYPE, $expression[$cursor], $cursor + 1); + ++$cursor; + } elseif (preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $expression, $match, 0, $cursor)) { + // names + $tokens[] = new Token(Token::NAME_TYPE, $match[0], $cursor + 1); + $cursor += \strlen($match[0]); + } else { + // unlexable + throw new SyntaxError(sprintf('Unexpected character "%s"', $expression[$cursor]), $cursor, $expression); + } + } + + $tokens[] = new Token(Token::EOF_TYPE, null, $cursor + 1); + + if (!empty($brackets)) { + list($expect, $cur) = array_pop($brackets); + throw new SyntaxError(sprintf('Unclosed "%s"', $expect), $cur, $expression); + } + + return new TokenStream($tokens, $expression); + } +} diff --git a/vendor/symfony/expression-language/Node/ArgumentsNode.php b/vendor/symfony/expression-language/Node/ArgumentsNode.php new file mode 100644 index 0000000..1c78d80 --- /dev/null +++ b/vendor/symfony/expression-language/Node/ArgumentsNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class ArgumentsNode extends ArrayNode +{ + public function compile(Compiler $compiler) + { + $this->compileArguments($compiler, false); + } + + public function toArray() + { + $array = array(); + + foreach ($this->getKeyValuePairs() as $pair) { + $array[] = $pair['value']; + $array[] = ', '; + } + array_pop($array); + + return $array; + } +} diff --git a/vendor/symfony/expression-language/Node/ArrayNode.php b/vendor/symfony/expression-language/Node/ArrayNode.php new file mode 100644 index 0000000..e1a2f2e --- /dev/null +++ b/vendor/symfony/expression-language/Node/ArrayNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class ArrayNode extends Node +{ + protected $index; + + public function __construct() + { + $this->index = -1; + } + + public function addElement(Node $value, Node $key = null) + { + if (null === $key) { + $key = new ConstantNode(++$this->index); + } + + array_push($this->nodes, $key, $value); + } + + /** + * Compiles the node to PHP. + */ + public function compile(Compiler $compiler) + { + $compiler->raw('array('); + $this->compileArguments($compiler); + $compiler->raw(')'); + } + + public function evaluate($functions, $values) + { + $result = array(); + foreach ($this->getKeyValuePairs() as $pair) { + $result[$pair['key']->evaluate($functions, $values)] = $pair['value']->evaluate($functions, $values); + } + + return $result; + } + + public function toArray() + { + $value = array(); + foreach ($this->getKeyValuePairs() as $pair) { + $value[$pair['key']->attributes['value']] = $pair['value']; + } + + $array = array(); + + if ($this->isHash($value)) { + foreach ($value as $k => $v) { + $array[] = ', '; + $array[] = new ConstantNode($k); + $array[] = ': '; + $array[] = $v; + } + $array[0] = '{'; + $array[] = '}'; + } else { + foreach ($value as $v) { + $array[] = ', '; + $array[] = $v; + } + $array[0] = '['; + $array[] = ']'; + } + + return $array; + } + + protected function getKeyValuePairs() + { + $pairs = array(); + foreach (array_chunk($this->nodes, 2) as $pair) { + $pairs[] = array('key' => $pair[0], 'value' => $pair[1]); + } + + return $pairs; + } + + protected function compileArguments(Compiler $compiler, $withKeys = true) + { + $first = true; + foreach ($this->getKeyValuePairs() as $pair) { + if (!$first) { + $compiler->raw(', '); + } + $first = false; + + if ($withKeys) { + $compiler + ->compile($pair['key']) + ->raw(' => ') + ; + } + + $compiler->compile($pair['value']); + } + } +} diff --git a/vendor/symfony/expression-language/Node/BinaryNode.php b/vendor/symfony/expression-language/Node/BinaryNode.php new file mode 100644 index 0000000..48c6548 --- /dev/null +++ b/vendor/symfony/expression-language/Node/BinaryNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class BinaryNode extends Node +{ + private static $operators = array( + '~' => '.', + 'and' => '&&', + 'or' => '||', + ); + + private static $functions = array( + '**' => 'pow', + '..' => 'range', + 'in' => 'in_array', + 'not in' => '!in_array', + ); + + public function __construct(string $operator, Node $left, Node $right) + { + parent::__construct( + array('left' => $left, 'right' => $right), + array('operator' => $operator) + ); + } + + public function compile(Compiler $compiler) + { + $operator = $this->attributes['operator']; + + if ('matches' == $operator) { + $compiler + ->raw('preg_match(') + ->compile($this->nodes['right']) + ->raw(', ') + ->compile($this->nodes['left']) + ->raw(')') + ; + + return; + } + + if (isset(self::$functions[$operator])) { + $compiler + ->raw(sprintf('%s(', self::$functions[$operator])) + ->compile($this->nodes['left']) + ->raw(', ') + ->compile($this->nodes['right']) + ->raw(')') + ; + + return; + } + + if (isset(self::$operators[$operator])) { + $operator = self::$operators[$operator]; + } + + $compiler + ->raw('(') + ->compile($this->nodes['left']) + ->raw(' ') + ->raw($operator) + ->raw(' ') + ->compile($this->nodes['right']) + ->raw(')') + ; + } + + public function evaluate($functions, $values) + { + $operator = $this->attributes['operator']; + $left = $this->nodes['left']->evaluate($functions, $values); + + if (isset(self::$functions[$operator])) { + $right = $this->nodes['right']->evaluate($functions, $values); + + if ('not in' === $operator) { + return !\in_array($left, $right); + } + $f = self::$functions[$operator]; + + return $f($left, $right); + } + + switch ($operator) { + case 'or': + case '||': + return $left || $this->nodes['right']->evaluate($functions, $values); + case 'and': + case '&&': + return $left && $this->nodes['right']->evaluate($functions, $values); + } + + $right = $this->nodes['right']->evaluate($functions, $values); + + switch ($operator) { + case '|': + return $left | $right; + case '^': + return $left ^ $right; + case '&': + return $left & $right; + case '==': + return $left == $right; + case '===': + return $left === $right; + case '!=': + return $left != $right; + case '!==': + return $left !== $right; + case '<': + return $left < $right; + case '>': + return $left > $right; + case '>=': + return $left >= $right; + case '<=': + return $left <= $right; + case 'not in': + return !\in_array($left, $right); + case 'in': + return \in_array($left, $right); + case '+': + return $left + $right; + case '-': + return $left - $right; + case '~': + return $left.$right; + case '*': + return $left * $right; + case '/': + return $left / $right; + case '%': + return $left % $right; + case 'matches': + return preg_match($right, $left); + } + } + + public function toArray() + { + return array('(', $this->nodes['left'], ' '.$this->attributes['operator'].' ', $this->nodes['right'], ')'); + } +} diff --git a/vendor/symfony/expression-language/Node/ConditionalNode.php b/vendor/symfony/expression-language/Node/ConditionalNode.php new file mode 100644 index 0000000..9db0f93 --- /dev/null +++ b/vendor/symfony/expression-language/Node/ConditionalNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class ConditionalNode extends Node +{ + public function __construct(Node $expr1, Node $expr2, Node $expr3) + { + parent::__construct( + array('expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3) + ); + } + + public function compile(Compiler $compiler) + { + $compiler + ->raw('((') + ->compile($this->nodes['expr1']) + ->raw(') ? (') + ->compile($this->nodes['expr2']) + ->raw(') : (') + ->compile($this->nodes['expr3']) + ->raw('))') + ; + } + + public function evaluate($functions, $values) + { + if ($this->nodes['expr1']->evaluate($functions, $values)) { + return $this->nodes['expr2']->evaluate($functions, $values); + } + + return $this->nodes['expr3']->evaluate($functions, $values); + } + + public function toArray() + { + return array('(', $this->nodes['expr1'], ' ? ', $this->nodes['expr2'], ' : ', $this->nodes['expr3'], ')'); + } +} diff --git a/vendor/symfony/expression-language/Node/ConstantNode.php b/vendor/symfony/expression-language/Node/ConstantNode.php new file mode 100644 index 0000000..de2c48f --- /dev/null +++ b/vendor/symfony/expression-language/Node/ConstantNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class ConstantNode extends Node +{ + private $isIdentifier; + + public function __construct($value, bool $isIdentifier = false) + { + $this->isIdentifier = $isIdentifier; + parent::__construct( + array(), + array('value' => $value) + ); + } + + public function compile(Compiler $compiler) + { + $compiler->repr($this->attributes['value']); + } + + public function evaluate($functions, $values) + { + return $this->attributes['value']; + } + + public function toArray() + { + $array = array(); + $value = $this->attributes['value']; + + if ($this->isIdentifier) { + $array[] = $value; + } elseif (true === $value) { + $array[] = 'true'; + } elseif (false === $value) { + $array[] = 'false'; + } elseif (null === $value) { + $array[] = 'null'; + } elseif (is_numeric($value)) { + $array[] = $value; + } elseif (!\is_array($value)) { + $array[] = $this->dumpString($value); + } elseif ($this->isHash($value)) { + foreach ($value as $k => $v) { + $array[] = ', '; + $array[] = new self($k); + $array[] = ': '; + $array[] = new self($v); + } + $array[0] = '{'; + $array[] = '}'; + } else { + foreach ($value as $v) { + $array[] = ', '; + $array[] = new self($v); + } + $array[0] = '['; + $array[] = ']'; + } + + return $array; + } +} diff --git a/vendor/symfony/expression-language/Node/FunctionNode.php b/vendor/symfony/expression-language/Node/FunctionNode.php new file mode 100644 index 0000000..1a1ad43 --- /dev/null +++ b/vendor/symfony/expression-language/Node/FunctionNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class FunctionNode extends Node +{ + public function __construct(string $name, Node $arguments) + { + parent::__construct( + array('arguments' => $arguments), + array('name' => $name) + ); + } + + public function compile(Compiler $compiler) + { + $arguments = array(); + foreach ($this->nodes['arguments']->nodes as $node) { + $arguments[] = $compiler->subcompile($node); + } + + $function = $compiler->getFunction($this->attributes['name']); + + $compiler->raw(\call_user_func_array($function['compiler'], $arguments)); + } + + public function evaluate($functions, $values) + { + $arguments = array($values); + foreach ($this->nodes['arguments']->nodes as $node) { + $arguments[] = $node->evaluate($functions, $values); + } + + return \call_user_func_array($functions[$this->attributes['name']]['evaluator'], $arguments); + } + + public function toArray() + { + $array = array(); + $array[] = $this->attributes['name']; + + foreach ($this->nodes['arguments']->nodes as $node) { + $array[] = ', '; + $array[] = $node; + } + $array[1] = '('; + $array[] = ')'; + + return $array; + } +} diff --git a/vendor/symfony/expression-language/Node/GetAttrNode.php b/vendor/symfony/expression-language/Node/GetAttrNode.php new file mode 100644 index 0000000..6a8b47c --- /dev/null +++ b/vendor/symfony/expression-language/Node/GetAttrNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class GetAttrNode extends Node +{ + const PROPERTY_CALL = 1; + const METHOD_CALL = 2; + const ARRAY_CALL = 3; + + public function __construct(Node $node, Node $attribute, ArrayNode $arguments, int $type) + { + parent::__construct( + array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), + array('type' => $type) + ); + } + + public function compile(Compiler $compiler) + { + switch ($this->attributes['type']) { + case self::PROPERTY_CALL: + $compiler + ->compile($this->nodes['node']) + ->raw('->') + ->raw($this->nodes['attribute']->attributes['value']) + ; + break; + + case self::METHOD_CALL: + $compiler + ->compile($this->nodes['node']) + ->raw('->') + ->raw($this->nodes['attribute']->attributes['value']) + ->raw('(') + ->compile($this->nodes['arguments']) + ->raw(')') + ; + break; + + case self::ARRAY_CALL: + $compiler + ->compile($this->nodes['node']) + ->raw('[') + ->compile($this->nodes['attribute'])->raw(']') + ; + break; + } + } + + public function evaluate($functions, $values) + { + switch ($this->attributes['type']) { + case self::PROPERTY_CALL: + $obj = $this->nodes['node']->evaluate($functions, $values); + if (!\is_object($obj)) { + throw new \RuntimeException('Unable to get a property on a non-object.'); + } + + $property = $this->nodes['attribute']->attributes['value']; + + return $obj->$property; + + case self::METHOD_CALL: + $obj = $this->nodes['node']->evaluate($functions, $values); + if (!\is_object($obj)) { + throw new \RuntimeException('Unable to get a property on a non-object.'); + } + if (!\is_callable($toCall = array($obj, $this->nodes['attribute']->attributes['value']))) { + throw new \RuntimeException(sprintf('Unable to call method "%s" of object "%s".', $this->nodes['attribute']->attributes['value'], \get_class($obj))); + } + + return \call_user_func_array($toCall, $this->nodes['arguments']->evaluate($functions, $values)); + + case self::ARRAY_CALL: + $array = $this->nodes['node']->evaluate($functions, $values); + if (!\is_array($array) && !$array instanceof \ArrayAccess) { + throw new \RuntimeException('Unable to get an item on a non-array.'); + } + + return $array[$this->nodes['attribute']->evaluate($functions, $values)]; + } + } + + public function toArray() + { + switch ($this->attributes['type']) { + case self::PROPERTY_CALL: + return array($this->nodes['node'], '.', $this->nodes['attribute']); + + case self::METHOD_CALL: + return array($this->nodes['node'], '.', $this->nodes['attribute'], '(', $this->nodes['arguments'], ')'); + + case self::ARRAY_CALL: + return array($this->nodes['node'], '[', $this->nodes['attribute'], ']'); + } + } +} diff --git a/vendor/symfony/expression-language/Node/NameNode.php b/vendor/symfony/expression-language/Node/NameNode.php new file mode 100644 index 0000000..c084bd4 --- /dev/null +++ b/vendor/symfony/expression-language/Node/NameNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class NameNode extends Node +{ + public function __construct(string $name) + { + parent::__construct( + array(), + array('name' => $name) + ); + } + + public function compile(Compiler $compiler) + { + $compiler->raw('$'.$this->attributes['name']); + } + + public function evaluate($functions, $values) + { + return $values[$this->attributes['name']]; + } + + public function toArray() + { + return array($this->attributes['name']); + } +} diff --git a/vendor/symfony/expression-language/Node/Node.php b/vendor/symfony/expression-language/Node/Node.php new file mode 100644 index 0000000..c6eb9f0 --- /dev/null +++ b/vendor/symfony/expression-language/Node/Node.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * Represents a node in the AST. + * + * @author Fabien Potencier + */ +class Node +{ + public $nodes = array(); + public $attributes = array(); + + /** + * @param array $nodes An array of nodes + * @param array $attributes An array of attributes + */ + public function __construct(array $nodes = array(), array $attributes = array()) + { + $this->nodes = $nodes; + $this->attributes = $attributes; + } + + public function __toString() + { + $attributes = array(); + foreach ($this->attributes as $name => $value) { + $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); + } + + $repr = array(str_replace('Symfony\Component\ExpressionLanguage\Node\\', '', \get_class($this)).'('.implode(', ', $attributes)); + + if (\count($this->nodes)) { + foreach ($this->nodes as $node) { + foreach (explode("\n", (string) $node) as $line) { + $repr[] = ' '.$line; + } + } + + $repr[] = ')'; + } else { + $repr[0] .= ')'; + } + + return implode("\n", $repr); + } + + public function compile(Compiler $compiler) + { + foreach ($this->nodes as $node) { + $node->compile($compiler); + } + } + + public function evaluate($functions, $values) + { + $results = array(); + foreach ($this->nodes as $node) { + $results[] = $node->evaluate($functions, $values); + } + + return $results; + } + + public function toArray() + { + throw new \BadMethodCallException(sprintf('Dumping a "%s" instance is not supported yet.', \get_class($this))); + } + + public function dump() + { + $dump = ''; + + foreach ($this->toArray() as $v) { + $dump .= is_scalar($v) ? $v : $v->dump(); + } + + return $dump; + } + + protected function dumpString($value) + { + return sprintf('"%s"', addcslashes($value, "\0\t\"\\")); + } + + protected function isHash(array $value) + { + $expectedKey = 0; + + foreach ($value as $key => $val) { + if ($key !== $expectedKey++) { + return true; + } + } + + return false; + } +} diff --git a/vendor/symfony/expression-language/Node/UnaryNode.php b/vendor/symfony/expression-language/Node/UnaryNode.php new file mode 100644 index 0000000..4a18e83 --- /dev/null +++ b/vendor/symfony/expression-language/Node/UnaryNode.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\ExpressionLanguage\Node; + +use Symfony\Component\ExpressionLanguage\Compiler; + +/** + * @author Fabien Potencier + * + * @internal + */ +class UnaryNode extends Node +{ + private static $operators = array( + '!' => '!', + 'not' => '!', + '+' => '+', + '-' => '-', + ); + + public function __construct(string $operator, Node $node) + { + parent::__construct( + array('node' => $node), + array('operator' => $operator) + ); + } + + public function compile(Compiler $compiler) + { + $compiler + ->raw('(') + ->raw(self::$operators[$this->attributes['operator']]) + ->compile($this->nodes['node']) + ->raw(')') + ; + } + + public function evaluate($functions, $values) + { + $value = $this->nodes['node']->evaluate($functions, $values); + switch ($this->attributes['operator']) { + case 'not': + case '!': + return !$value; + case '-': + return -$value; + } + + return $value; + } + + public function toArray(): array + { + return array('(', $this->attributes['operator'].' ', $this->nodes['node'], ')'); + } +} diff --git a/vendor/symfony/expression-language/ParsedExpression.php b/vendor/symfony/expression-language/ParsedExpression.php new file mode 100644 index 0000000..2e2bf37 --- /dev/null +++ b/vendor/symfony/expression-language/ParsedExpression.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\ExpressionLanguage; + +use Symfony\Component\ExpressionLanguage\Node\Node; + +/** + * Represents an already parsed expression. + * + * @author Fabien Potencier + */ +class ParsedExpression extends Expression +{ + private $nodes; + + /** + * @param string $expression An expression + * @param Node $nodes A Node representing the expression + */ + public function __construct(string $expression, Node $nodes) + { + parent::__construct($expression); + + $this->nodes = $nodes; + } + + public function getNodes() + { + return $this->nodes; + } +} diff --git a/vendor/symfony/expression-language/Parser.php b/vendor/symfony/expression-language/Parser.php new file mode 100644 index 0000000..a3b31ce --- /dev/null +++ b/vendor/symfony/expression-language/Parser.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\ExpressionLanguage; + +/** + * Parsers a token stream. + * + * This parser implements a "Precedence climbing" algorithm. + * + * @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm + * @see http://en.wikipedia.org/wiki/Operator-precedence_parser + * + * @author Fabien Potencier + */ +class Parser +{ + const OPERATOR_LEFT = 1; + const OPERATOR_RIGHT = 2; + + private $stream; + private $unaryOperators; + private $binaryOperators; + private $functions; + private $names; + + public function __construct(array $functions) + { + $this->functions = $functions; + + $this->unaryOperators = array( + 'not' => array('precedence' => 50), + '!' => array('precedence' => 50), + '-' => array('precedence' => 500), + '+' => array('precedence' => 500), + ); + $this->binaryOperators = array( + 'or' => array('precedence' => 10, 'associativity' => self::OPERATOR_LEFT), + '||' => array('precedence' => 10, 'associativity' => self::OPERATOR_LEFT), + 'and' => array('precedence' => 15, 'associativity' => self::OPERATOR_LEFT), + '&&' => array('precedence' => 15, 'associativity' => self::OPERATOR_LEFT), + '|' => array('precedence' => 16, 'associativity' => self::OPERATOR_LEFT), + '^' => array('precedence' => 17, 'associativity' => self::OPERATOR_LEFT), + '&' => array('precedence' => 18, 'associativity' => self::OPERATOR_LEFT), + '==' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '===' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '!=' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '!==' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '<' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '>' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '>=' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '<=' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + 'not in' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + 'in' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + 'matches' => array('precedence' => 20, 'associativity' => self::OPERATOR_LEFT), + '..' => array('precedence' => 25, 'associativity' => self::OPERATOR_LEFT), + '+' => array('precedence' => 30, 'associativity' => self::OPERATOR_LEFT), + '-' => array('precedence' => 30, 'associativity' => self::OPERATOR_LEFT), + '~' => array('precedence' => 40, 'associativity' => self::OPERATOR_LEFT), + '*' => array('precedence' => 60, 'associativity' => self::OPERATOR_LEFT), + '/' => array('precedence' => 60, 'associativity' => self::OPERATOR_LEFT), + '%' => array('precedence' => 60, 'associativity' => self::OPERATOR_LEFT), + '**' => array('precedence' => 200, 'associativity' => self::OPERATOR_RIGHT), + ); + } + + /** + * Converts a token stream to a node tree. + * + * The valid names is an array where the values + * are the names that the user can use in an expression. + * + * If the variable name in the compiled PHP code must be + * different, define it as the key. + * + * For instance, ['this' => 'container'] means that the + * variable 'container' can be used in the expression + * but the compiled code will use 'this'. + * + * @param TokenStream $stream A token stream instance + * @param array $names An array of valid names + * + * @return Node\Node A node tree + * + * @throws SyntaxError + */ + public function parse(TokenStream $stream, $names = array()) + { + $this->stream = $stream; + $this->names = $names; + + $node = $this->parseExpression(); + if (!$stream->isEOF()) { + throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $stream->current->type, $stream->current->value), $stream->current->cursor, $stream->getExpression()); + } + + return $node; + } + + public function parseExpression($precedence = 0) + { + $expr = $this->getPrimary(); + $token = $this->stream->current; + while ($token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->value]) && $this->binaryOperators[$token->value]['precedence'] >= $precedence) { + $op = $this->binaryOperators[$token->value]; + $this->stream->next(); + + $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); + $expr = new Node\BinaryNode($token->value, $expr, $expr1); + + $token = $this->stream->current; + } + + if (0 === $precedence) { + return $this->parseConditionalExpression($expr); + } + + return $expr; + } + + protected function getPrimary() + { + $token = $this->stream->current; + + if ($token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->value])) { + $operator = $this->unaryOperators[$token->value]; + $this->stream->next(); + $expr = $this->parseExpression($operator['precedence']); + + return $this->parsePostfixExpression(new Node\UnaryNode($token->value, $expr)); + } + + if ($token->test(Token::PUNCTUATION_TYPE, '(')) { + $this->stream->next(); + $expr = $this->parseExpression(); + $this->stream->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); + + return $this->parsePostfixExpression($expr); + } + + return $this->parsePrimaryExpression(); + } + + protected function parseConditionalExpression($expr) + { + while ($this->stream->current->test(Token::PUNCTUATION_TYPE, '?')) { + $this->stream->next(); + if (!$this->stream->current->test(Token::PUNCTUATION_TYPE, ':')) { + $expr2 = $this->parseExpression(); + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, ':')) { + $this->stream->next(); + $expr3 = $this->parseExpression(); + } else { + $expr3 = new Node\ConstantNode(null); + } + } else { + $this->stream->next(); + $expr2 = $expr; + $expr3 = $this->parseExpression(); + } + + $expr = new Node\ConditionalNode($expr, $expr2, $expr3); + } + + return $expr; + } + + public function parsePrimaryExpression() + { + $token = $this->stream->current; + switch ($token->type) { + case Token::NAME_TYPE: + $this->stream->next(); + switch ($token->value) { + case 'true': + case 'TRUE': + return new Node\ConstantNode(true); + + case 'false': + case 'FALSE': + return new Node\ConstantNode(false); + + case 'null': + case 'NULL': + return new Node\ConstantNode(null); + + default: + if ('(' === $this->stream->current->value) { + if (false === isset($this->functions[$token->value])) { + throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, array_keys($this->functions)); + } + + $node = new Node\FunctionNode($token->value, $this->parseArguments()); + } else { + if (!\in_array($token->value, $this->names, true)) { + throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, $this->names); + } + + // is the name used in the compiled code different + // from the name used in the expression? + if (\is_int($name = array_search($token->value, $this->names))) { + $name = $token->value; + } + + $node = new Node\NameNode($name); + } + } + break; + + case Token::NUMBER_TYPE: + case Token::STRING_TYPE: + $this->stream->next(); + + return new Node\ConstantNode($token->value); + + default: + if ($token->test(Token::PUNCTUATION_TYPE, '[')) { + $node = $this->parseArrayExpression(); + } elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) { + $node = $this->parseHashExpression(); + } else { + throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s"', $token->type, $token->value), $token->cursor, $this->stream->getExpression()); + } + } + + return $this->parsePostfixExpression($node); + } + + public function parseArrayExpression() + { + $this->stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected'); + + $node = new Node\ArrayNode(); + $first = true; + while (!$this->stream->current->test(Token::PUNCTUATION_TYPE, ']')) { + if (!$first) { + $this->stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma'); + + // trailing ,? + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, ']')) { + break; + } + } + $first = false; + + $node->addElement($this->parseExpression()); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed'); + + return $node; + } + + public function parseHashExpression() + { + $this->stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected'); + + $node = new Node\ArrayNode(); + $first = true; + while (!$this->stream->current->test(Token::PUNCTUATION_TYPE, '}')) { + if (!$first) { + $this->stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma'); + + // trailing ,? + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '}')) { + break; + } + } + $first = false; + + // a hash key can be: + // + // * a number -- 12 + // * a string -- 'a' + // * a name, which is equivalent to a string -- a + // * an expression, which must be enclosed in parentheses -- (1 + 2) + if ($this->stream->current->test(Token::STRING_TYPE) || $this->stream->current->test(Token::NAME_TYPE) || $this->stream->current->test(Token::NUMBER_TYPE)) { + $key = new Node\ConstantNode($this->stream->current->value); + $this->stream->next(); + } elseif ($this->stream->current->test(Token::PUNCTUATION_TYPE, '(')) { + $key = $this->parseExpression(); + } else { + $current = $this->stream->current; + + throw new SyntaxError(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"', $current->type, $current->value), $current->cursor, $this->stream->getExpression()); + } + + $this->stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)'); + $value = $this->parseExpression(); + + $node->addElement($value, $key); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed'); + + return $node; + } + + public function parsePostfixExpression($node) + { + $token = $this->stream->current; + while (Token::PUNCTUATION_TYPE == $token->type) { + if ('.' === $token->value) { + $this->stream->next(); + $token = $this->stream->current; + $this->stream->next(); + + if ( + Token::NAME_TYPE !== $token->type + && + // Operators like "not" and "matches" are valid method or property names, + // + // In other words, besides NAME_TYPE, OPERATOR_TYPE could also be parsed as a property or method. + // This is because operators are processed by the lexer prior to names. So "not" in "foo.not()" or "matches" in "foo.matches" will be recognized as an operator first. + // But in fact, "not" and "matches" in such expressions shall be parsed as method or property names. + // + // And this ONLY works if the operator consists of valid characters for a property or method name. + // + // Other types, such as STRING_TYPE and NUMBER_TYPE, can't be parsed as property nor method names. + // + // As a result, if $token is NOT an operator OR $token->value is NOT a valid property or method name, an exception shall be thrown. + (Token::OPERATOR_TYPE !== $token->type || !preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A', $token->value)) + ) { + throw new SyntaxError('Expected name', $token->cursor, $this->stream->getExpression()); + } + + $arg = new Node\ConstantNode($token->value, true); + + $arguments = new Node\ArgumentsNode(); + if ($this->stream->current->test(Token::PUNCTUATION_TYPE, '(')) { + $type = Node\GetAttrNode::METHOD_CALL; + foreach ($this->parseArguments()->nodes as $n) { + $arguments->addElement($n); + } + } else { + $type = Node\GetAttrNode::PROPERTY_CALL; + } + + $node = new Node\GetAttrNode($node, $arg, $arguments, $type); + } elseif ('[' === $token->value) { + $this->stream->next(); + $arg = $this->parseExpression(); + $this->stream->expect(Token::PUNCTUATION_TYPE, ']'); + + $node = new Node\GetAttrNode($node, $arg, new Node\ArgumentsNode(), Node\GetAttrNode::ARRAY_CALL); + } else { + break; + } + + $token = $this->stream->current; + } + + return $node; + } + + /** + * Parses arguments. + */ + public function parseArguments() + { + $args = array(); + $this->stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + while (!$this->stream->current->test(Token::PUNCTUATION_TYPE, ')')) { + if (!empty($args)) { + $this->stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + } + + $args[] = $this->parseExpression(); + } + $this->stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); + + return new Node\Node($args); + } +} diff --git a/vendor/symfony/expression-language/README.md b/vendor/symfony/expression-language/README.md new file mode 100644 index 0000000..08b310d --- /dev/null +++ b/vendor/symfony/expression-language/README.md @@ -0,0 +1,15 @@ +ExpressionLanguage Component +============================ + +The ExpressionLanguage component provides an engine that can compile and +evaluate expressions. An expression is a one-liner that returns a value +(mostly, but not limited to, Booleans). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/expression_language/introduction.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php b/vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php new file mode 100644 index 0000000..74a1008 --- /dev/null +++ b/vendor/symfony/expression-language/Resources/bin/generate_operator_regex.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +$operators = array('not', '!', 'or', '||', '&&', 'and', '|', '^', '&', '==', '===', '!=', '!==', '<', '>', '>=', '<=', 'not in', 'in', '..', '+', '-', '~', '*', '/', '%', 'matches', '**'); +$operators = array_combine($operators, array_map('strlen', $operators)); +arsort($operators); + +$regex = array(); +foreach ($operators as $operator => $length) { + // an operator that ends with a character must be followed by + // a whitespace or a parenthesis + $regex[] = preg_quote($operator, '/').(ctype_alpha($operator[$length - 1]) ? '(?=[\s(])' : ''); +} + +echo '/'.implode('|', $regex).'/A'; diff --git a/vendor/symfony/expression-language/SerializedParsedExpression.php b/vendor/symfony/expression-language/SerializedParsedExpression.php new file mode 100644 index 0000000..a1f838c --- /dev/null +++ b/vendor/symfony/expression-language/SerializedParsedExpression.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\ExpressionLanguage; + +/** + * Represents an already parsed expression. + * + * @author Fabien Potencier + */ +class SerializedParsedExpression extends ParsedExpression +{ + private $nodes; + + /** + * @param string $expression An expression + * @param string $nodes The serialized nodes for the expression + */ + public function __construct(string $expression, string $nodes) + { + $this->expression = $expression; + $this->nodes = $nodes; + } + + public function getNodes() + { + return unserialize($this->nodes); + } +} diff --git a/vendor/symfony/expression-language/SyntaxError.php b/vendor/symfony/expression-language/SyntaxError.php new file mode 100644 index 0000000..d3313b8 --- /dev/null +++ b/vendor/symfony/expression-language/SyntaxError.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\ExpressionLanguage; + +class SyntaxError extends \LogicException +{ + public function __construct(string $message, int $cursor = 0, string $expression = '', string $subject = null, array $proposals = null) + { + $message = sprintf('%s around position %d', $message, $cursor); + if ($expression) { + $message = sprintf('%s for expression `%s`', $message, $expression); + } + $message .= '.'; + + if (null !== $subject && null !== $proposals) { + $minScore = INF; + foreach ($proposals as $proposal) { + $distance = levenshtein($subject, $proposal); + if ($distance < $minScore) { + $guess = $proposal; + $minScore = $distance; + } + } + + if (isset($guess) && $minScore < 3) { + $message .= sprintf(' Did you mean "%s"?', $guess); + } + } + + parent::__construct($message); + } +} diff --git a/vendor/symfony/expression-language/Tests/ExpressionFunctionTest.php b/vendor/symfony/expression-language/Tests/ExpressionFunctionTest.php new file mode 100644 index 0000000..f2710fb --- /dev/null +++ b/vendor/symfony/expression-language/Tests/ExpressionFunctionTest.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\ExpressionLanguage\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\ExpressionFunction; + +/** + * Tests ExpressionFunction. + * + * @author Dany Maillard + */ +class ExpressionFunctionTest extends TestCase +{ + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage PHP function "fn_does_not_exist" does not exist. + */ + public function testFunctionDoesNotExist() + { + ExpressionFunction::fromPhp('fn_does_not_exist'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage An expression function name must be defined when PHP function "Symfony\Component\ExpressionLanguage\Tests\fn_namespaced" is namespaced. + */ + public function testFunctionNamespaced() + { + ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\fn_namespaced'); + } +} + +function fn_namespaced() +{ +} diff --git a/vendor/symfony/expression-language/Tests/ExpressionLanguageTest.php b/vendor/symfony/expression-language/Tests/ExpressionLanguageTest.php new file mode 100644 index 0000000..15a26d5 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/ExpressionLanguageTest.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\ExpressionLanguage\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\ExpressionLanguage\ParsedExpression; +use Symfony\Component\ExpressionLanguage\Tests\Fixtures\TestProvider; + +class ExpressionLanguageTest extends TestCase +{ + public function testCachedParse() + { + $cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemPoolInterface')->getMock(); + $cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock(); + $savedParsedExpression = null; + $expressionLanguage = new ExpressionLanguage($cacheMock); + + $cacheMock + ->expects($this->exactly(2)) + ->method('getItem') + ->with('1%20%2B%201%2F%2F') + ->willReturn($cacheItemMock) + ; + + $cacheItemMock + ->expects($this->exactly(2)) + ->method('get') + ->will($this->returnCallback(function () use (&$savedParsedExpression) { + return $savedParsedExpression; + })) + ; + + $cacheItemMock + ->expects($this->exactly(1)) + ->method('set') + ->with($this->isInstanceOf(ParsedExpression::class)) + ->will($this->returnCallback(function ($parsedExpression) use (&$savedParsedExpression) { + $savedParsedExpression = $parsedExpression; + })) + ; + + $cacheMock + ->expects($this->exactly(1)) + ->method('save') + ->with($cacheItemMock) + ; + + $parsedExpression = $expressionLanguage->parse('1 + 1', array()); + $this->assertSame($savedParsedExpression, $parsedExpression); + + $parsedExpression = $expressionLanguage->parse('1 + 1', array()); + $this->assertSame($savedParsedExpression, $parsedExpression); + } + + public function testConstantFunction() + { + $expressionLanguage = new ExpressionLanguage(); + $this->assertEquals(PHP_VERSION, $expressionLanguage->evaluate('constant("PHP_VERSION")')); + + $expressionLanguage = new ExpressionLanguage(); + $this->assertEquals('\constant("PHP_VERSION")', $expressionLanguage->compile('constant("PHP_VERSION")')); + } + + public function testProviders() + { + $expressionLanguage = new ExpressionLanguage(null, array(new TestProvider())); + $this->assertEquals('foo', $expressionLanguage->evaluate('identity("foo")')); + $this->assertEquals('"foo"', $expressionLanguage->compile('identity("foo")')); + $this->assertEquals('FOO', $expressionLanguage->evaluate('strtoupper("foo")')); + $this->assertEquals('\strtoupper("foo")', $expressionLanguage->compile('strtoupper("foo")')); + $this->assertEquals('foo', $expressionLanguage->evaluate('strtolower("FOO")')); + $this->assertEquals('\strtolower("FOO")', $expressionLanguage->compile('strtolower("FOO")')); + $this->assertTrue($expressionLanguage->evaluate('fn_namespaced()')); + $this->assertEquals('\Symfony\Component\ExpressionLanguage\Tests\Fixtures\fn_namespaced()', $expressionLanguage->compile('fn_namespaced()')); + } + + /** + * @dataProvider shortCircuitProviderEvaluate + */ + public function testShortCircuitOperatorsEvaluate($expression, array $values, $expected) + { + $expressionLanguage = new ExpressionLanguage(); + $this->assertEquals($expected, $expressionLanguage->evaluate($expression, $values)); + } + + /** + * @dataProvider shortCircuitProviderCompile + */ + public function testShortCircuitOperatorsCompile($expression, array $names, $expected) + { + $result = null; + $expressionLanguage = new ExpressionLanguage(); + eval(sprintf('$result = %s;', $expressionLanguage->compile($expression, $names))); + $this->assertSame($expected, $result); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Unexpected end of expression around position 6 for expression `node.`. + */ + public function testParseThrowsInsteadOfNotice() + { + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->parse('node.', array('node')); + } + + public function shortCircuitProviderEvaluate() + { + $object = $this->getMockBuilder('stdClass')->setMethods(array('foo'))->getMock(); + $object->expects($this->never())->method('foo'); + + return array( + array('false and object.foo()', array('object' => $object), false), + array('false && object.foo()', array('object' => $object), false), + array('true || object.foo()', array('object' => $object), true), + array('true or object.foo()', array('object' => $object), true), + ); + } + + public function shortCircuitProviderCompile() + { + return array( + array('false and foo', array('foo' => 'foo'), false), + array('false && foo', array('foo' => 'foo'), false), + array('true || foo', array('foo' => 'foo'), true), + array('true or foo', array('foo' => 'foo'), true), + ); + } + + public function testCachingForOverriddenVariableNames() + { + $expressionLanguage = new ExpressionLanguage(); + $expression = 'a + b'; + $expressionLanguage->evaluate($expression, array('a' => 1, 'b' => 1)); + $result = $expressionLanguage->compile($expression, array('a', 'B' => 'b')); + $this->assertSame('($a + $B)', $result); + } + + public function testStrictEquality() + { + $expressionLanguage = new ExpressionLanguage(); + $expression = '123 === a'; + $result = $expressionLanguage->compile($expression, array('a')); + $this->assertSame('(123 === $a)', $result); + } + + public function testCachingWithDifferentNamesOrder() + { + $cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemPoolInterface')->getMock(); + $cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock(); + $expressionLanguage = new ExpressionLanguage($cacheMock); + $savedParsedExpressions = array(); + + $cacheMock + ->expects($this->exactly(2)) + ->method('getItem') + ->with('a%20%2B%20b%2F%2Fa%7CB%3Ab') + ->willReturn($cacheItemMock) + ; + + $cacheItemMock + ->expects($this->exactly(2)) + ->method('get') + ->will($this->returnCallback(function () use (&$savedParsedExpression) { + return $savedParsedExpression; + })) + ; + + $cacheItemMock + ->expects($this->exactly(1)) + ->method('set') + ->with($this->isInstanceOf(ParsedExpression::class)) + ->will($this->returnCallback(function ($parsedExpression) use (&$savedParsedExpression) { + $savedParsedExpression = $parsedExpression; + })) + ; + + $cacheMock + ->expects($this->exactly(1)) + ->method('save') + ->with($cacheItemMock) + ; + + $expression = 'a + b'; + $expressionLanguage->compile($expression, array('a', 'B' => 'b')); + $expressionLanguage->compile($expression, array('B' => 'b', 'a')); + } + + /** + * @dataProvider getRegisterCallbacks + * @expectedException \LogicException + */ + public function testRegisterAfterParse($registerCallback) + { + $el = new ExpressionLanguage(); + $el->parse('1 + 1', array()); + $registerCallback($el); + } + + /** + * @dataProvider getRegisterCallbacks + * @expectedException \LogicException + */ + public function testRegisterAfterEval($registerCallback) + { + $el = new ExpressionLanguage(); + $el->evaluate('1 + 1'); + $registerCallback($el); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessageRegExp /Unable to call method "\w+" of object "\w+"./ + */ + public function testCallBadCallable() + { + $el = new ExpressionLanguage(); + $el->evaluate('foo.myfunction()', array('foo' => new \stdClass())); + } + + /** + * @dataProvider getRegisterCallbacks + * @expectedException \LogicException + */ + public function testRegisterAfterCompile($registerCallback) + { + $el = new ExpressionLanguage(); + $el->compile('1 + 1'); + $registerCallback($el); + } + + public function getRegisterCallbacks() + { + return array( + array( + function (ExpressionLanguage $el) { + $el->register('fn', function () {}, function () {}); + }, + ), + array( + function (ExpressionLanguage $el) { + $el->addFunction(new ExpressionFunction('fn', function () {}, function () {})); + }, + ), + array( + function (ExpressionLanguage $el) { + $el->registerProvider(new TestProvider()); + }, + ), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/ExpressionTest.php b/vendor/symfony/expression-language/Tests/ExpressionTest.php new file mode 100644 index 0000000..052ef22 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/ExpressionTest.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\ExpressionLanguage\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Expression; + +class ExpressionTest extends TestCase +{ + public function testSerialization() + { + $expression = new Expression('kernel.boot()'); + + $serializedExpression = serialize($expression); + $unserializedExpression = unserialize($serializedExpression); + + $this->assertEquals($expression, $unserializedExpression); + } +} diff --git a/vendor/symfony/expression-language/Tests/Fixtures/TestProvider.php b/vendor/symfony/expression-language/Tests/Fixtures/TestProvider.php new file mode 100644 index 0000000..20c5182 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Fixtures/TestProvider.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\ExpressionLanguage\Tests\Fixtures; + +use Symfony\Component\ExpressionLanguage\ExpressionFunction; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; +use Symfony\Component\ExpressionLanguage\ExpressionPhpFunction; + +class TestProvider implements ExpressionFunctionProviderInterface +{ + public function getFunctions() + { + return array( + new ExpressionFunction('identity', function ($input) { + return $input; + }, function (array $values, $input) { + return $input; + }), + + ExpressionFunction::fromPhp('strtoupper'), + + ExpressionFunction::fromPhp('\strtolower'), + + ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\Fixtures\fn_namespaced', 'fn_namespaced'), + ); + } +} + +function fn_namespaced() +{ + return true; +} diff --git a/vendor/symfony/expression-language/Tests/LexerTest.php b/vendor/symfony/expression-language/Tests/LexerTest.php new file mode 100644 index 0000000..16bf450 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/LexerTest.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\ExpressionLanguage\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Lexer; +use Symfony\Component\ExpressionLanguage\Token; +use Symfony\Component\ExpressionLanguage\TokenStream; + +class LexerTest extends TestCase +{ + /** + * @var Lexer + */ + private $lexer; + + protected function setUp() + { + $this->lexer = new Lexer(); + } + + /** + * @dataProvider getTokenizeData + */ + public function testTokenize($tokens, $expression) + { + $tokens[] = new Token('end of expression', null, \strlen($expression) + 1); + $this->assertEquals(new TokenStream($tokens, $expression), $this->lexer->tokenize($expression)); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Unexpected character "'" around position 33 for expression `service(faulty.expression.example').dummyMethod()`. + */ + public function testTokenizeThrowsErrorWithMessage() + { + $expression = "service(faulty.expression.example').dummyMethod()"; + $this->lexer->tokenize($expression); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Unclosed "(" around position 7 for expression `service(unclosed.expression.dummyMethod()`. + */ + public function testTokenizeThrowsErrorOnUnclosedBrace() + { + $expression = 'service(unclosed.expression.dummyMethod()'; + $this->lexer->tokenize($expression); + } + + public function getTokenizeData() + { + return array( + array( + array(new Token('name', 'a', 3)), + ' a ', + ), + array( + array(new Token('name', 'a', 1)), + 'a', + ), + array( + array(new Token('string', 'foo', 1)), + '"foo"', + ), + array( + array(new Token('number', '3', 1)), + '3', + ), + array( + array(new Token('operator', '+', 1)), + '+', + ), + array( + array(new Token('punctuation', '.', 1)), + '.', + ), + array( + array( + new Token('punctuation', '(', 1), + new Token('number', '3', 2), + new Token('operator', '+', 4), + new Token('number', '5', 6), + new Token('punctuation', ')', 7), + new Token('operator', '~', 9), + new Token('name', 'foo', 11), + new Token('punctuation', '(', 14), + new Token('string', 'bar', 15), + new Token('punctuation', ')', 20), + new Token('punctuation', '.', 21), + new Token('name', 'baz', 22), + new Token('punctuation', '[', 25), + new Token('number', '4', 26), + new Token('punctuation', ']', 27), + ), + '(3 + 5) ~ foo("bar").baz[4]', + ), + array( + array(new Token('operator', '..', 1)), + '..', + ), + array( + array(new Token('string', '#foo', 1)), + "'#foo'", + ), + array( + array(new Token('string', '#foo', 1)), + '"#foo"', + ), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/AbstractNodeTest.php b/vendor/symfony/expression-language/Tests/Node/AbstractNodeTest.php new file mode 100644 index 0000000..a6f80c2 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/AbstractNodeTest.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\ExpressionLanguage\Tests\Node; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Compiler; + +abstract class AbstractNodeTest extends TestCase +{ + /** + * @dataProvider getEvaluateData + */ + public function testEvaluate($expected, $node, $variables = array(), $functions = array()) + { + $this->assertSame($expected, $node->evaluate($functions, $variables)); + } + + abstract public function getEvaluateData(); + + /** + * @dataProvider getCompileData + */ + public function testCompile($expected, $node, $functions = array()) + { + $compiler = new Compiler($functions); + $node->compile($compiler); + $this->assertSame($expected, $compiler->getSource()); + } + + abstract public function getCompileData(); + + /** + * @dataProvider getDumpData + */ + public function testDump($expected, $node) + { + $this->assertSame($expected, $node->dump()); + } + + abstract public function getDumpData(); +} diff --git a/vendor/symfony/expression-language/Tests/Node/ArgumentsNodeTest.php b/vendor/symfony/expression-language/Tests/Node/ArgumentsNodeTest.php new file mode 100644 index 0000000..60a6d1c --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/ArgumentsNodeTest.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\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ArgumentsNode; + +class ArgumentsNodeTest extends ArrayNodeTest +{ + public function getCompileData() + { + return array( + array('"a", "b"', $this->getArrayNode()), + ); + } + + public function getDumpData() + { + return array( + array('"a", "b"', $this->getArrayNode()), + ); + } + + protected function createArrayNode() + { + return new ArgumentsNode(); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/ArrayNodeTest.php b/vendor/symfony/expression-language/Tests/Node/ArrayNodeTest.php new file mode 100644 index 0000000..11a35d4 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/ArrayNodeTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ArrayNode; +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; + +class ArrayNodeTest extends AbstractNodeTest +{ + public function testSerialization() + { + $node = $this->createArrayNode(); + $node->addElement(new ConstantNode('foo')); + + $serializedNode = serialize($node); + $unserializedNode = unserialize($serializedNode); + + $this->assertEquals($node, $unserializedNode); + $this->assertNotEquals($this->createArrayNode(), $unserializedNode); + } + + public function getEvaluateData() + { + return array( + array(array('b' => 'a', 'b'), $this->getArrayNode()), + ); + } + + public function getCompileData() + { + return array( + array('array("b" => "a", 0 => "b")', $this->getArrayNode()), + ); + } + + public function getDumpData() + { + yield array('{"b": "a", 0: "b"}', $this->getArrayNode()); + + $array = $this->createArrayNode(); + $array->addElement(new ConstantNode('c'), new ConstantNode('a"b')); + $array->addElement(new ConstantNode('d'), new ConstantNode('a\b')); + yield array('{"a\\"b": "c", "a\\\\b": "d"}', $array); + + $array = $this->createArrayNode(); + $array->addElement(new ConstantNode('c')); + $array->addElement(new ConstantNode('d')); + yield array('["c", "d"]', $array); + } + + protected function getArrayNode() + { + $array = $this->createArrayNode(); + $array->addElement(new ConstantNode('a'), new ConstantNode('b')); + $array->addElement(new ConstantNode('b')); + + return $array; + } + + protected function createArrayNode() + { + return new ArrayNode(); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/BinaryNodeTest.php b/vendor/symfony/expression-language/Tests/Node/BinaryNodeTest.php new file mode 100644 index 0000000..f8456f8 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/BinaryNodeTest.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ArrayNode; +use Symfony\Component\ExpressionLanguage\Node\BinaryNode; +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; + +class BinaryNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + $array = new ArrayNode(); + $array->addElement(new ConstantNode('a')); + $array->addElement(new ConstantNode('b')); + + return array( + array(true, new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))), + array(true, new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))), + array(false, new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))), + array(false, new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))), + + array(0, new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))), + array(6, new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))), + array(6, new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))), + + array(true, new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))), + array(true, new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))), + array(true, new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))), + + array(false, new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))), + array(false, new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))), + array(true, new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))), + + array(true, new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))), + array(false, new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))), + + array(false, new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))), + array(true, new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))), + + array(-1, new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))), + array(3, new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))), + array(4, new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))), + array(1, new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))), + array(1, new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))), + array(25, new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))), + array('ab', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))), + + array(true, new BinaryNode('in', new ConstantNode('a'), $array)), + array(false, new BinaryNode('in', new ConstantNode('c'), $array)), + array(true, new BinaryNode('not in', new ConstantNode('c'), $array)), + array(false, new BinaryNode('not in', new ConstantNode('a'), $array)), + + array(array(1, 2, 3), new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))), + + array(1, new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+$/'))), + ); + } + + public function getCompileData() + { + $array = new ArrayNode(); + $array->addElement(new ConstantNode('a')); + $array->addElement(new ConstantNode('b')); + + return array( + array('(true || false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))), + array('(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))), + array('(true && false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))), + array('(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))), + + array('(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))), + array('(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))), + array('(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))), + + array('(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))), + array('(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))), + array('(1 <= 1)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))), + + array('(1 > 2)', new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))), + array('(1 >= 2)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))), + array('(1 >= 1)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))), + + array('(true === true)', new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))), + array('(true !== true)', new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))), + + array('(2 == 1)', new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))), + array('(2 != 1)', new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))), + + array('(1 - 2)', new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))), + array('(1 + 2)', new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))), + array('(2 * 2)', new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))), + array('(2 / 2)', new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))), + array('(5 % 2)', new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))), + array('pow(5, 2)', new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))), + array('("a" . "b")', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))), + + array('in_array("a", array(0 => "a", 1 => "b"))', new BinaryNode('in', new ConstantNode('a'), $array)), + array('in_array("c", array(0 => "a", 1 => "b"))', new BinaryNode('in', new ConstantNode('c'), $array)), + array('!in_array("c", array(0 => "a", 1 => "b"))', new BinaryNode('not in', new ConstantNode('c'), $array)), + array('!in_array("a", array(0 => "a", 1 => "b"))', new BinaryNode('not in', new ConstantNode('a'), $array)), + + array('range(1, 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))), + + array('preg_match("/^[a-z]+/i\$/", "abc")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))), + ); + } + + public function getDumpData() + { + $array = new ArrayNode(); + $array->addElement(new ConstantNode('a')); + $array->addElement(new ConstantNode('b')); + + return array( + array('(true or false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))), + array('(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))), + array('(true and false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))), + array('(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))), + + array('(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))), + array('(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))), + array('(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))), + + array('(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))), + array('(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))), + array('(1 <= 1)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))), + + array('(1 > 2)', new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))), + array('(1 >= 2)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))), + array('(1 >= 1)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))), + + array('(true === true)', new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))), + array('(true !== true)', new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))), + + array('(2 == 1)', new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))), + array('(2 != 1)', new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))), + + array('(1 - 2)', new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))), + array('(1 + 2)', new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))), + array('(2 * 2)', new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))), + array('(2 / 2)', new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))), + array('(5 % 2)', new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))), + array('(5 ** 2)', new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))), + array('("a" ~ "b")', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))), + + array('("a" in ["a", "b"])', new BinaryNode('in', new ConstantNode('a'), $array)), + array('("c" in ["a", "b"])', new BinaryNode('in', new ConstantNode('c'), $array)), + array('("c" not in ["a", "b"])', new BinaryNode('not in', new ConstantNode('c'), $array)), + array('("a" not in ["a", "b"])', new BinaryNode('not in', new ConstantNode('a'), $array)), + + array('(1 .. 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))), + + array('("abc" matches "/^[a-z]+/i$/")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/ConditionalNodeTest.php b/vendor/symfony/expression-language/Tests/Node/ConditionalNodeTest.php new file mode 100644 index 0000000..cbf9e8d --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/ConditionalNodeTest.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\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ConditionalNode; +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; + +class ConditionalNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + return array( + array(1, new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))), + array(2, new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))), + ); + } + + public function getCompileData() + { + return array( + array('((true) ? (1) : (2))', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))), + array('((false) ? (1) : (2))', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))), + ); + } + + public function getDumpData() + { + return array( + array('(true ? 1 : 2)', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))), + array('(false ? 1 : 2)', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/ConstantNodeTest.php b/vendor/symfony/expression-language/Tests/Node/ConstantNodeTest.php new file mode 100644 index 0000000..af79a91 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/ConstantNodeTest.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; + +class ConstantNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + return array( + array(false, new ConstantNode(false)), + array(true, new ConstantNode(true)), + array(null, new ConstantNode(null)), + array(3, new ConstantNode(3)), + array(3.3, new ConstantNode(3.3)), + array('foo', new ConstantNode('foo')), + array(array(1, 'b' => 'a'), new ConstantNode(array(1, 'b' => 'a'))), + ); + } + + public function getCompileData() + { + return array( + array('false', new ConstantNode(false)), + array('true', new ConstantNode(true)), + array('null', new ConstantNode(null)), + array('3', new ConstantNode(3)), + array('3.3', new ConstantNode(3.3)), + array('"foo"', new ConstantNode('foo')), + array('array(0 => 1, "b" => "a")', new ConstantNode(array(1, 'b' => 'a'))), + ); + } + + public function getDumpData() + { + return array( + array('false', new ConstantNode(false)), + array('true', new ConstantNode(true)), + array('null', new ConstantNode(null)), + array('3', new ConstantNode(3)), + array('3.3', new ConstantNode(3.3)), + array('"foo"', new ConstantNode('foo')), + array('foo', new ConstantNode('foo', true)), + array('{0: 1, "b": "a", 1: true}', new ConstantNode(array(1, 'b' => 'a', true))), + array('{"a\\"b": "c", "a\\\\b": "d"}', new ConstantNode(array('a"b' => 'c', 'a\\b' => 'd'))), + array('["c", "d"]', new ConstantNode(array('c', 'd'))), + array('{"a": ["b"]}', new ConstantNode(array('a' => array('b')))), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/FunctionNodeTest.php b/vendor/symfony/expression-language/Tests/Node/FunctionNodeTest.php new file mode 100644 index 0000000..3bddc5e --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/FunctionNodeTest.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\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; +use Symfony\Component\ExpressionLanguage\Node\FunctionNode; +use Symfony\Component\ExpressionLanguage\Node\Node; + +class FunctionNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + return array( + array('bar', new FunctionNode('foo', new Node(array(new ConstantNode('bar')))), array(), array('foo' => $this->getCallables())), + ); + } + + public function getCompileData() + { + return array( + array('foo("bar")', new FunctionNode('foo', new Node(array(new ConstantNode('bar')))), array('foo' => $this->getCallables())), + ); + } + + public function getDumpData() + { + return array( + array('foo("bar")', new FunctionNode('foo', new Node(array(new ConstantNode('bar')))), array('foo' => $this->getCallables())), + ); + } + + protected function getCallables() + { + return array( + 'compiler' => function ($arg) { + return sprintf('foo(%s)', $arg); + }, + 'evaluator' => function ($variables, $arg) { + return $arg; + }, + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/GetAttrNodeTest.php b/vendor/symfony/expression-language/Tests/Node/GetAttrNodeTest.php new file mode 100644 index 0000000..750acaf --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/GetAttrNodeTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ArrayNode; +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; +use Symfony\Component\ExpressionLanguage\Node\GetAttrNode; +use Symfony\Component\ExpressionLanguage\Node\NameNode; + +class GetAttrNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + return array( + array('b', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), array('foo' => array('b' => 'a', 'b'))), + array('a', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), array('foo' => array('b' => 'a', 'b'))), + + array('bar', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())), + + array('baz', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())), + array('a', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), array('foo' => array('b' => 'a', 'b'), 'index' => 'b')), + ); + } + + public function getCompileData() + { + return array( + array('$foo[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)), + array('$foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)), + + array('$foo->foo', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())), + + array('$foo->foo(array("b" => "a", 0 => "b"))', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())), + array('$foo[$index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)), + ); + } + + public function getDumpData() + { + return array( + array('foo[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)), + array('foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)), + + array('foo.foo', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), array('foo' => new Obj())), + + array('foo.foo({"b": "a", 0: "b"})', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), array('foo' => new Obj())), + array('foo[index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)), + ); + } + + protected function getArrayNode() + { + $array = new ArrayNode(); + $array->addElement(new ConstantNode('a'), new ConstantNode('b')); + $array->addElement(new ConstantNode('b')); + + return $array; + } +} + +class Obj +{ + public $foo = 'bar'; + + public function foo() + { + return 'baz'; + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/NameNodeTest.php b/vendor/symfony/expression-language/Tests/Node/NameNodeTest.php new file mode 100644 index 0000000..5fa2c37 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/NameNodeTest.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\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\NameNode; + +class NameNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + return array( + array('bar', new NameNode('foo'), array('foo' => 'bar')), + ); + } + + public function getCompileData() + { + return array( + array('$foo', new NameNode('foo')), + ); + } + + public function getDumpData() + { + return array( + array('foo', new NameNode('foo')), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/NodeTest.php b/vendor/symfony/expression-language/Tests/Node/NodeTest.php new file mode 100644 index 0000000..140f152 --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/NodeTest.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\ExpressionLanguage\Tests\Node; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; +use Symfony\Component\ExpressionLanguage\Node\Node; + +class NodeTest extends TestCase +{ + public function testToString() + { + $node = new Node(array(new ConstantNode('foo'))); + + $this->assertEquals(<<<'EOF' +Node( + ConstantNode(value: 'foo') +) +EOF + , (string) $node); + } + + public function testSerialization() + { + $node = new Node(array('foo' => 'bar'), array('bar' => 'foo')); + + $serializedNode = serialize($node); + $unserializedNode = unserialize($serializedNode); + + $this->assertEquals($node, $unserializedNode); + } +} diff --git a/vendor/symfony/expression-language/Tests/Node/UnaryNodeTest.php b/vendor/symfony/expression-language/Tests/Node/UnaryNodeTest.php new file mode 100644 index 0000000..ef072cb --- /dev/null +++ b/vendor/symfony/expression-language/Tests/Node/UnaryNodeTest.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\ExpressionLanguage\Tests\Node; + +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; +use Symfony\Component\ExpressionLanguage\Node\UnaryNode; + +class UnaryNodeTest extends AbstractNodeTest +{ + public function getEvaluateData() + { + return array( + array(-1, new UnaryNode('-', new ConstantNode(1))), + array(3, new UnaryNode('+', new ConstantNode(3))), + array(false, new UnaryNode('!', new ConstantNode(true))), + array(false, new UnaryNode('not', new ConstantNode(true))), + ); + } + + public function getCompileData() + { + return array( + array('(-1)', new UnaryNode('-', new ConstantNode(1))), + array('(+3)', new UnaryNode('+', new ConstantNode(3))), + array('(!true)', new UnaryNode('!', new ConstantNode(true))), + array('(!true)', new UnaryNode('not', new ConstantNode(true))), + ); + } + + public function getDumpData() + { + return array( + array('(- 1)', new UnaryNode('-', new ConstantNode(1))), + array('(+ 3)', new UnaryNode('+', new ConstantNode(3))), + array('(! true)', new UnaryNode('!', new ConstantNode(true))), + array('(not true)', new UnaryNode('not', new ConstantNode(true))), + ); + } +} diff --git a/vendor/symfony/expression-language/Tests/ParsedExpressionTest.php b/vendor/symfony/expression-language/Tests/ParsedExpressionTest.php new file mode 100644 index 0000000..d28101f --- /dev/null +++ b/vendor/symfony/expression-language/Tests/ParsedExpressionTest.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\ExpressionLanguage\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Node\ConstantNode; +use Symfony\Component\ExpressionLanguage\ParsedExpression; + +class ParsedExpressionTest extends TestCase +{ + public function testSerialization() + { + $expression = new ParsedExpression('25', new ConstantNode('25')); + + $serializedExpression = serialize($expression); + $unserializedExpression = unserialize($serializedExpression); + + $this->assertEquals($expression, $unserializedExpression); + } +} diff --git a/vendor/symfony/expression-language/Tests/ParserTest.php b/vendor/symfony/expression-language/Tests/ParserTest.php new file mode 100644 index 0000000..1e9ffba --- /dev/null +++ b/vendor/symfony/expression-language/Tests/ParserTest.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\ExpressionLanguage\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\ExpressionLanguage\Lexer; +use Symfony\Component\ExpressionLanguage\Node; +use Symfony\Component\ExpressionLanguage\Parser; + +class ParserTest extends TestCase +{ + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Variable "foo" is not valid around position 1 for expression `foo`. + */ + public function testParseWithInvalidName() + { + $lexer = new Lexer(); + $parser = new Parser(array()); + $parser->parse($lexer->tokenize('foo')); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Variable "foo" is not valid around position 1 for expression `foo`. + */ + public function testParseWithZeroInNames() + { + $lexer = new Lexer(); + $parser = new Parser(array()); + $parser->parse($lexer->tokenize('foo'), array(0)); + } + + /** + * @dataProvider getParseData + */ + public function testParse($node, $expression, $names = array()) + { + $lexer = new Lexer(); + $parser = new Parser(array()); + $this->assertEquals($node, $parser->parse($lexer->tokenize($expression), $names)); + } + + public function getParseData() + { + $arguments = new Node\ArgumentsNode(); + $arguments->addElement(new Node\ConstantNode('arg1')); + $arguments->addElement(new Node\ConstantNode(2)); + $arguments->addElement(new Node\ConstantNode(true)); + + return array( + array( + new Node\NameNode('a'), + 'a', + array('a'), + ), + array( + new Node\ConstantNode('a'), + '"a"', + ), + array( + new Node\ConstantNode(3), + '3', + ), + array( + new Node\ConstantNode(false), + 'false', + ), + array( + new Node\ConstantNode(true), + 'true', + ), + array( + new Node\ConstantNode(null), + 'null', + ), + array( + new Node\UnaryNode('-', new Node\ConstantNode(3)), + '-3', + ), + array( + new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)), + '3 - 3', + ), + array( + new Node\BinaryNode('*', + new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)), + new Node\ConstantNode(2) + ), + '(3 - 3) * 2', + ), + array( + new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::PROPERTY_CALL), + 'foo.bar', + array('foo'), + ), + array( + new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL), + 'foo.bar()', + array('foo'), + ), + array( + new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('not', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL), + 'foo.not()', + array('foo'), + ), + array( + new Node\GetAttrNode( + new Node\NameNode('foo'), + new Node\ConstantNode('bar', true), + $arguments, + Node\GetAttrNode::METHOD_CALL + ), + 'foo.bar("arg1", 2, true)', + array('foo'), + ), + array( + new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode(3), new Node\ArgumentsNode(), Node\GetAttrNode::ARRAY_CALL), + 'foo[3]', + array('foo'), + ), + array( + new Node\ConditionalNode(new Node\ConstantNode(true), new Node\ConstantNode(true), new Node\ConstantNode(false)), + 'true ? true : false', + ), + array( + new Node\BinaryNode('matches', new Node\ConstantNode('foo'), new Node\ConstantNode('/foo/')), + '"foo" matches "/foo/"', + ), + + // chained calls + array( + $this->createGetAttrNode( + $this->createGetAttrNode( + $this->createGetAttrNode( + $this->createGetAttrNode(new Node\NameNode('foo'), 'bar', Node\GetAttrNode::METHOD_CALL), + 'foo', Node\GetAttrNode::METHOD_CALL), + 'baz', Node\GetAttrNode::PROPERTY_CALL), + '3', Node\GetAttrNode::ARRAY_CALL), + 'foo.bar().foo().baz[3]', + array('foo'), + ), + + array( + new Node\NameNode('foo'), + 'bar', + array('foo' => 'bar'), + ), + ); + } + + private function createGetAttrNode($node, $item, $type) + { + return new Node\GetAttrNode($node, new Node\ConstantNode($item, Node\GetAttrNode::ARRAY_CALL !== $type), new Node\ArgumentsNode(), $type); + } + + /** + * @dataProvider getInvalidPostfixData + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + */ + public function testParseWithInvalidPostfixData($expr, $names = array()) + { + $lexer = new Lexer(); + $parser = new Parser(array()); + $parser->parse($lexer->tokenize($expr), $names); + } + + public function getInvalidPostfixData() + { + return array( + array( + 'foo."#"', + array('foo'), + ), + array( + 'foo."bar"', + array('foo'), + ), + array( + 'foo.**', + array('foo'), + ), + array( + 'foo.123', + array('foo'), + ), + ); + } + + /** + * @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError + * @expectedExceptionMessage Did you mean "baz"? + */ + public function testNameProposal() + { + $lexer = new Lexer(); + $parser = new Parser(array()); + + $parser->parse($lexer->tokenize('foo > bar'), array('foo', 'baz')); + } +} diff --git a/vendor/symfony/expression-language/Token.php b/vendor/symfony/expression-language/Token.php new file mode 100644 index 0000000..40bb755 --- /dev/null +++ b/vendor/symfony/expression-language/Token.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\ExpressionLanguage; + +/** + * Represents a Token. + * + * @author Fabien Potencier + */ +class Token +{ + public $value; + public $type; + public $cursor; + + const EOF_TYPE = 'end of expression'; + const NAME_TYPE = 'name'; + const NUMBER_TYPE = 'number'; + const STRING_TYPE = 'string'; + const OPERATOR_TYPE = 'operator'; + const PUNCTUATION_TYPE = 'punctuation'; + + /** + * @param string $type The type of the token (self::*_TYPE) + * @param string|int|float|null $value The token value + * @param int $cursor The cursor position in the source + */ + public function __construct(string $type, $value, ?int $cursor) + { + $this->type = $type; + $this->value = $value; + $this->cursor = $cursor; + } + + /** + * Returns a string representation of the token. + * + * @return string A string representation of the token + */ + public function __toString() + { + return sprintf('%3d %-11s %s', $this->cursor, strtoupper($this->type), $this->value); + } + + /** + * Tests the current token for a type and/or a value. + * + * @param array|int $type The type to test + * @param string|null $value The token value + * + * @return bool + */ + public function test($type, $value = null) + { + return $this->type === $type && (null === $value || $this->value == $value); + } +} diff --git a/vendor/symfony/expression-language/TokenStream.php b/vendor/symfony/expression-language/TokenStream.php new file mode 100644 index 0000000..919add6 --- /dev/null +++ b/vendor/symfony/expression-language/TokenStream.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\ExpressionLanguage; + +/** + * Represents a token stream. + * + * @author Fabien Potencier + */ +class TokenStream +{ + public $current; + + private $tokens; + private $position = 0; + private $expression; + + public function __construct(array $tokens, string $expression = '') + { + $this->tokens = $tokens; + $this->current = $tokens[0]; + $this->expression = $expression; + } + + /** + * Returns a string representation of the token stream. + * + * @return string + */ + public function __toString() + { + return implode("\n", $this->tokens); + } + + /** + * Sets the pointer to the next token and returns the old one. + */ + public function next() + { + ++$this->position; + + if (!isset($this->tokens[$this->position])) { + throw new SyntaxError('Unexpected end of expression', $this->current->cursor, $this->expression); + } + + $this->current = $this->tokens[$this->position]; + } + + /** + * Tests a token. + * + * @param array|int $type The type to test + * @param string|null $value The token value + * @param string|null $message The syntax error message + */ + public function expect($type, $value = null, $message = null) + { + $token = $this->current; + if (!$token->test($type, $value)) { + throw new SyntaxError(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)', $message ? $message.'. ' : '', $token->type, $token->value, $type, $value ? sprintf(' with value "%s"', $value) : ''), $token->cursor, $this->expression); + } + $this->next(); + } + + /** + * Checks if end of stream was reached. + * + * @return bool + */ + public function isEOF() + { + return Token::EOF_TYPE === $this->current->type; + } + + /** + * @internal + * + * @return string + */ + public function getExpression() + { + return $this->expression; + } +} diff --git a/vendor/symfony/expression-language/composer.json b/vendor/symfony/expression-language/composer.json new file mode 100644 index 0000000..3c453b8 --- /dev/null +++ b/vendor/symfony/expression-language/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/expression-language", + "type": "library", + "description": "Symfony ExpressionLanguage 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": "^7.1.3", + "symfony/cache": "~3.4|~4.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\ExpressionLanguage\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/expression-language/phpunit.xml.dist b/vendor/symfony/expression-language/phpunit.xml.dist new file mode 100644 index 0000000..517322f --- /dev/null +++ b/vendor/symfony/expression-language/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/filesystem/.gitignore b/vendor/symfony/filesystem/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/filesystem/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/filesystem/CHANGELOG.md b/vendor/symfony/filesystem/CHANGELOG.md new file mode 100644 index 0000000..9f1f817 --- /dev/null +++ b/vendor/symfony/filesystem/CHANGELOG.md @@ -0,0 +1,59 @@ +CHANGELOG +========= + +4.0.0 +----- + + * removed `LockHandler` + * Support for passing relative paths to `Filesystem::makePathRelative()` has been removed. + +3.4.0 +----- + + * support for passing relative paths to `Filesystem::makePathRelative()` is deprecated and will be removed in 4.0 + +3.3.0 +----- + + * added `appendToFile()` to append contents to existing files + +3.2.0 +----- + + * added `readlink()` as a platform independent method to read links + +3.0.0 +----- + + * removed `$mode` argument from `Filesystem::dumpFile()` + +2.8.0 +----- + + * added tempnam() a stream aware version of PHP's native tempnam() + +2.6.0 +----- + + * added LockHandler + +2.3.12 +------ + + * deprecated dumpFile() file mode argument. + +2.3.0 +----- + + * added the dumpFile() method to atomically write files + +2.2.0 +----- + + * added a delete option for the mirror() method + +2.1.0 +----- + + * 24eb396 : BC Break : mkdir() function now throws exception in case of failure instead of returning Boolean value + * created the component diff --git a/vendor/symfony/filesystem/Exception/ExceptionInterface.php b/vendor/symfony/filesystem/Exception/ExceptionInterface.php new file mode 100644 index 0000000..8f4f10a --- /dev/null +++ b/vendor/symfony/filesystem/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\Filesystem\Exception; + +/** + * Exception interface for all exceptions thrown by the component. + * + * @author Romain Neutron + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/filesystem/Exception/FileNotFoundException.php b/vendor/symfony/filesystem/Exception/FileNotFoundException.php new file mode 100644 index 0000000..f941b53 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/FileNotFoundException.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\Filesystem\Exception; + +/** + * Exception class thrown when a file couldn't be found. + * + * @author Fabien Potencier + * @author Christian Gärtner + */ +class FileNotFoundException extends IOException +{ + public function __construct(string $message = null, int $code = 0, \Exception $previous = null, string $path = null) + { + if (null === $message) { + if (null === $path) { + $message = 'File could not be found.'; + } else { + $message = sprintf('File "%s" could not be found.', $path); + } + } + + parent::__construct($message, $code, $previous, $path); + } +} diff --git a/vendor/symfony/filesystem/Exception/IOException.php b/vendor/symfony/filesystem/Exception/IOException.php new file mode 100644 index 0000000..f02cbd8 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/IOException.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\Filesystem\Exception; + +/** + * Exception class thrown when a filesystem operation failure happens. + * + * @author Romain Neutron + * @author Christian Gärtner + * @author Fabien Potencier + */ +class IOException extends \RuntimeException implements IOExceptionInterface +{ + private $path; + + public function __construct(string $message, int $code = 0, \Exception $previous = null, string $path = null) + { + $this->path = $path; + + parent::__construct($message, $code, $previous); + } + + /** + * {@inheritdoc} + */ + public function getPath() + { + return $this->path; + } +} diff --git a/vendor/symfony/filesystem/Exception/IOExceptionInterface.php b/vendor/symfony/filesystem/Exception/IOExceptionInterface.php new file mode 100644 index 0000000..c11965a --- /dev/null +++ b/vendor/symfony/filesystem/Exception/IOExceptionInterface.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\Filesystem\Exception; + +/** + * IOException interface for file and input/output stream related exceptions thrown by the component. + * + * @author Christian Gärtner + */ +interface IOExceptionInterface extends ExceptionInterface +{ + /** + * Returns the associated path for the exception. + * + * @return string The path + */ + public function getPath(); +} diff --git a/vendor/symfony/filesystem/Exception/InvalidArgumentException.php b/vendor/symfony/filesystem/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..abadc20 --- /dev/null +++ b/vendor/symfony/filesystem/Exception/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Exception; + +/** + * @author Christian Flothmann + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/filesystem/Filesystem.php b/vendor/symfony/filesystem/Filesystem.php new file mode 100644 index 0000000..9e44f2f --- /dev/null +++ b/vendor/symfony/filesystem/Filesystem.php @@ -0,0 +1,766 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem; + +use Symfony\Component\Filesystem\Exception\FileNotFoundException; +use Symfony\Component\Filesystem\Exception\InvalidArgumentException; +use Symfony\Component\Filesystem\Exception\IOException; + +/** + * Provides basic utility to manipulate the file system. + * + * @author Fabien Potencier + */ +class Filesystem +{ + private static $lastError; + + /** + * Copies a file. + * + * If the target file is older than the origin file, it's always overwritten. + * If the target file is newer, it is overwritten only when the + * $overwriteNewerFiles option is set to true. + * + * @param string $originFile The original filename + * @param string $targetFile The target filename + * @param bool $overwriteNewerFiles If true, target files newer than origin files are overwritten + * + * @throws FileNotFoundException When originFile doesn't exist + * @throws IOException When copy fails + */ + public function copy($originFile, $targetFile, $overwriteNewerFiles = false) + { + $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://'); + if ($originIsLocal && !is_file($originFile)) { + throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); + } + + $this->mkdir(\dirname($targetFile)); + + $doCopy = true; + if (!$overwriteNewerFiles && null === parse_url($originFile, PHP_URL_HOST) && is_file($targetFile)) { + $doCopy = filemtime($originFile) > filemtime($targetFile); + } + + if ($doCopy) { + // https://bugs.php.net/bug.php?id=64634 + if (false === $source = @fopen($originFile, 'r')) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because source file could not be opened for reading.', $originFile, $targetFile), 0, null, $originFile); + } + + // Stream context created to allow files overwrite when using FTP stream wrapper - disabled by default + if (false === $target = @fopen($targetFile, 'w', null, stream_context_create(array('ftp' => array('overwrite' => true))))) { + throw new IOException(sprintf('Failed to copy "%s" to "%s" because target file could not be opened for writing.', $originFile, $targetFile), 0, null, $originFile); + } + + $bytesCopied = stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + unset($source, $target); + + if (!is_file($targetFile)) { + throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); + } + + if ($originIsLocal) { + // Like `cp`, preserve executable permission bits + @chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); + + if ($bytesCopied !== $bytesOrigin = filesize($originFile)) { + throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + } + } + } + } + + /** + * Creates a directory recursively. + * + * @param string|iterable $dirs The directory path + * @param int $mode The directory mode + * + * @throws IOException On any directory creation failure + */ + public function mkdir($dirs, $mode = 0777) + { + foreach ($this->toIterable($dirs) as $dir) { + if (is_dir($dir)) { + continue; + } + + if (!self::box('mkdir', $dir, $mode, true)) { + if (!is_dir($dir)) { + // The directory was not created by a concurrent process. Let's throw an exception with a developer friendly error message if we have one + if (self::$lastError) { + throw new IOException(sprintf('Failed to create "%s": %s.', $dir, self::$lastError), 0, null, $dir); + } + throw new IOException(sprintf('Failed to create "%s"', $dir), 0, null, $dir); + } + } + } + } + + /** + * Checks the existence of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to check + * + * @return bool true if the file exists, false otherwise + */ + public function exists($files) + { + $maxPathLength = PHP_MAXPATHLEN - 2; + + foreach ($this->toIterable($files) as $file) { + if (\strlen($file) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file exist because path length exceeds %d characters.', $maxPathLength), 0, null, $file); + } + + if (!file_exists($file)) { + return false; + } + } + + return true; + } + + /** + * Sets access and modification time of file. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to create + * @param int $time The touch time as a Unix timestamp + * @param int $atime The access time as a Unix timestamp + * + * @throws IOException When touch fails + */ + public function touch($files, $time = null, $atime = null) + { + foreach ($this->toIterable($files) as $file) { + $touch = $time ? @touch($file, $time, $atime) : @touch($file); + if (true !== $touch) { + throw new IOException(sprintf('Failed to touch "%s".', $file), 0, null, $file); + } + } + } + + /** + * Removes files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to remove + * + * @throws IOException When removal fails + */ + public function remove($files) + { + if ($files instanceof \Traversable) { + $files = iterator_to_array($files, false); + } elseif (!\is_array($files)) { + $files = array($files); + } + $files = array_reverse($files); + foreach ($files as $file) { + if (is_link($file)) { + // See https://bugs.php.net/52176 + if (!(self::box('unlink', $file) || '\\' !== \DIRECTORY_SEPARATOR || self::box('rmdir', $file)) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, self::$lastError)); + } + } elseif (is_dir($file)) { + $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS)); + + if (!self::box('rmdir', $file) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, self::$lastError)); + } + } elseif (!self::box('unlink', $file) && file_exists($file)) { + throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, self::$lastError)); + } + } + } + + /** + * Change mode for an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change mode + * @param int $mode The new mode (octal) + * @param int $umask The mode mask (octal) + * @param bool $recursive Whether change the mod recursively or not + * + * @throws IOException When the change fail + */ + public function chmod($files, $mode, $umask = 0000, $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if (true !== @chmod($file, $mode & ~$umask)) { + throw new IOException(sprintf('Failed to chmod file "%s".', $file), 0, null, $file); + } + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chmod(new \FilesystemIterator($file), $mode, $umask, true); + } + } + } + + /** + * Change the owner of an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change owner + * @param string $user The new owner user name + * @param bool $recursive Whether change the owner recursively or not + * + * @throws IOException When the change fail + */ + public function chown($files, $user, $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chown(new \FilesystemIterator($file), $user, true); + } + if (is_link($file) && \function_exists('lchown')) { + if (true !== @lchown($file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); + } + } else { + if (true !== @chown($file, $user)) { + throw new IOException(sprintf('Failed to chown file "%s".', $file), 0, null, $file); + } + } + } + } + + /** + * Change the group of an array of files or directories. + * + * @param string|iterable $files A filename, an array of files, or a \Traversable instance to change group + * @param string $group The group name + * @param bool $recursive Whether change the group recursively or not + * + * @throws IOException When the change fail + */ + public function chgrp($files, $group, $recursive = false) + { + foreach ($this->toIterable($files) as $file) { + if ($recursive && is_dir($file) && !is_link($file)) { + $this->chgrp(new \FilesystemIterator($file), $group, true); + } + if (is_link($file) && \function_exists('lchgrp')) { + if (true !== @lchgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + } + } else { + if (true !== @chgrp($file, $group)) { + throw new IOException(sprintf('Failed to chgrp file "%s".', $file), 0, null, $file); + } + } + } + } + + /** + * Renames a file or a directory. + * + * @param string $origin The origin filename or directory + * @param string $target The new filename or directory + * @param bool $overwrite Whether to overwrite the target if it already exists + * + * @throws IOException When target file or directory already exists + * @throws IOException When origin cannot be renamed + */ + public function rename($origin, $target, $overwrite = false) + { + // we check that target does not exist + if (!$overwrite && $this->isReadable($target)) { + throw new IOException(sprintf('Cannot rename because the target "%s" already exists.', $target), 0, null, $target); + } + + if (true !== @rename($origin, $target)) { + if (is_dir($origin)) { + // See https://bugs.php.net/bug.php?id=54097 & http://php.net/manual/en/function.rename.php#113943 + $this->mirror($origin, $target, null, array('override' => $overwrite, 'delete' => $overwrite)); + $this->remove($origin); + + return; + } + throw new IOException(sprintf('Cannot rename "%s" to "%s".', $origin, $target), 0, null, $target); + } + } + + /** + * Tells whether a file exists and is readable. + * + * @param string $filename Path to the file + * + * @return bool + * + * @throws IOException When windows path is longer than 258 characters + */ + private function isReadable($filename) + { + $maxPathLength = PHP_MAXPATHLEN - 2; + + if (\strlen($filename) > $maxPathLength) { + throw new IOException(sprintf('Could not check if file is readable because path length exceeds %d characters.', $maxPathLength), 0, null, $filename); + } + + return is_readable($filename); + } + + /** + * Creates a symbolic link or copy a directory. + * + * @param string $originDir The origin directory path + * @param string $targetDir The symbolic link name + * @param bool $copyOnWindows Whether to copy files if on Windows + * + * @throws IOException When symlink fails + */ + public function symlink($originDir, $targetDir, $copyOnWindows = false) + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $originDir = strtr($originDir, '/', '\\'); + $targetDir = strtr($targetDir, '/', '\\'); + + if ($copyOnWindows) { + $this->mirror($originDir, $targetDir); + + return; + } + } + + $this->mkdir(\dirname($targetDir)); + + if (is_link($targetDir)) { + if (readlink($targetDir) === $originDir) { + return; + } + $this->remove($targetDir); + } + + if (!self::box('symlink', $originDir, $targetDir)) { + $this->linkException($originDir, $targetDir, 'symbolic'); + } + } + + /** + * Creates a hard link, or several hard links to a file. + * + * @param string $originFile The original file + * @param string|string[] $targetFiles The target file(s) + * + * @throws FileNotFoundException When original file is missing or not a file + * @throws IOException When link fails, including if link already exists + */ + public function hardlink($originFile, $targetFiles) + { + if (!$this->exists($originFile)) { + throw new FileNotFoundException(null, 0, null, $originFile); + } + + if (!is_file($originFile)) { + throw new FileNotFoundException(sprintf('Origin file "%s" is not a file', $originFile)); + } + + foreach ($this->toIterable($targetFiles) as $targetFile) { + if (is_file($targetFile)) { + if (fileinode($originFile) === fileinode($targetFile)) { + continue; + } + $this->remove($targetFile); + } + + if (!self::box('link', $originFile, $targetFile)) { + $this->linkException($originFile, $targetFile, 'hard'); + } + } + } + + /** + * @param string $origin + * @param string $target + * @param string $linkType Name of the link type, typically 'symbolic' or 'hard' + */ + private function linkException($origin, $target, $linkType) + { + if (self::$lastError) { + if ('\\' === \DIRECTORY_SEPARATOR && false !== strpos(self::$lastError, 'error code(1314)')) { + throw new IOException(sprintf('Unable to create %s link due to error code 1314: \'A required privilege is not held by the client\'. Do you have the required Administrator-rights?', $linkType), 0, null, $target); + } + } + throw new IOException(sprintf('Failed to create %s link from "%s" to "%s".', $linkType, $origin, $target), 0, null, $target); + } + + /** + * Resolves links in paths. + * + * With $canonicalize = false (default) + * - if $path does not exist or is not a link, returns null + * - if $path is a link, returns the next direct target of the link without considering the existence of the target + * + * With $canonicalize = true + * - if $path does not exist, returns null + * - if $path exists, returns its absolute fully resolved final version + * + * @param string $path A filesystem path + * @param bool $canonicalize Whether or not to return a canonicalized path + * + * @return string|null + */ + public function readlink($path, $canonicalize = false) + { + if (!$canonicalize && !is_link($path)) { + return; + } + + if ($canonicalize) { + if (!$this->exists($path)) { + return; + } + + if ('\\' === \DIRECTORY_SEPARATOR) { + $path = readlink($path); + } + + return realpath($path); + } + + if ('\\' === \DIRECTORY_SEPARATOR) { + return realpath($path); + } + + return readlink($path); + } + + /** + * Given an existing path, convert it to a path relative to a given starting path. + * + * @param string $endPath Absolute path of target + * @param string $startPath Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public function makePathRelative($endPath, $startPath) + { + if (!$this->isAbsolutePath($startPath)) { + throw new InvalidArgumentException(sprintf('The start path "%s" is not absolute.', $startPath)); + } + + if (!$this->isAbsolutePath($endPath)) { + throw new InvalidArgumentException(sprintf('The end path "%s" is not absolute.', $endPath)); + } + + // Normalize separators on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $endPath = str_replace('\\', '/', $endPath); + $startPath = str_replace('\\', '/', $startPath); + } + + $stripDriveLetter = function ($path) { + if (\strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0])) { + return substr($path, 2); + } + + return $path; + }; + + $endPath = $stripDriveLetter($endPath); + $startPath = $stripDriveLetter($startPath); + + // Split the paths into arrays + $startPathArr = explode('/', trim($startPath, '/')); + $endPathArr = explode('/', trim($endPath, '/')); + + $normalizePathArray = function ($pathSegments) { + $result = array(); + + foreach ($pathSegments as $segment) { + if ('..' === $segment) { + array_pop($result); + } elseif ('.' !== $segment) { + $result[] = $segment; + } + } + + return $result; + }; + + $startPathArr = $normalizePathArray($startPathArr); + $endPathArr = $normalizePathArray($endPathArr); + + // Find for which directory the common path stops + $index = 0; + while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { + ++$index; + } + + // Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels) + if (1 === \count($startPathArr) && '' === $startPathArr[0]) { + $depth = 0; + } else { + $depth = \count($startPathArr) - $index; + } + + // Repeated "../" for each level need to reach the common path + $traverser = str_repeat('../', $depth); + + $endPathRemainder = implode('/', \array_slice($endPathArr, $index)); + + // Construct $endPath from traversing to the common path, then to the remaining $endPath + $relativePath = $traverser.('' !== $endPathRemainder ? $endPathRemainder.'/' : ''); + + return '' === $relativePath ? './' : $relativePath; + } + + /** + * Mirrors a directory to another. + * + * Copies files and directories from the origin directory into the target directory. By default: + * + * - existing files in the target directory will be overwritten, except if they are newer (see the `override` option) + * - files in the target directory that do not exist in the source directory will not be deleted (see the `delete` option) + * + * @param string $originDir The origin directory + * @param string $targetDir The target directory + * @param \Traversable $iterator Iterator that filters which files and directories to copy + * @param array $options An array of boolean options + * Valid options are: + * - $options['override'] If true, target files newer than origin files are overwritten (see copy(), defaults to false) + * - $options['copy_on_windows'] Whether to copy files instead of links on Windows (see symlink(), defaults to false) + * - $options['delete'] Whether to delete files that are not in the source directory (defaults to false) + * + * @throws IOException When file type is unknown + */ + public function mirror($originDir, $targetDir, \Traversable $iterator = null, $options = array()) + { + $targetDir = rtrim($targetDir, '/\\'); + $originDir = rtrim($originDir, '/\\'); + $originDirLen = \strlen($originDir); + + // Iterate in destination folder to remove obsolete entries + if ($this->exists($targetDir) && isset($options['delete']) && $options['delete']) { + $deleteIterator = $iterator; + if (null === $deleteIterator) { + $flags = \FilesystemIterator::SKIP_DOTS; + $deleteIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($targetDir, $flags), \RecursiveIteratorIterator::CHILD_FIRST); + } + $targetDirLen = \strlen($targetDir); + foreach ($deleteIterator as $file) { + $origin = $originDir.substr($file->getPathname(), $targetDirLen); + if (!$this->exists($origin)) { + $this->remove($file); + } + } + } + + $copyOnWindows = false; + if (isset($options['copy_on_windows'])) { + $copyOnWindows = $options['copy_on_windows']; + } + + if (null === $iterator) { + $flags = $copyOnWindows ? \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS : \FilesystemIterator::SKIP_DOTS; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($originDir, $flags), \RecursiveIteratorIterator::SELF_FIRST); + } + + if ($this->exists($originDir)) { + $this->mkdir($targetDir); + } + + foreach ($iterator as $file) { + $target = $targetDir.substr($file->getPathname(), $originDirLen); + + if ($copyOnWindows) { + if (is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } elseif (is_dir($file)) { + $this->mkdir($target); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } else { + if (is_link($file)) { + $this->symlink($file->getLinkTarget(), $target); + } elseif (is_dir($file)) { + $this->mkdir($target); + } elseif (is_file($file)) { + $this->copy($file, $target, isset($options['override']) ? $options['override'] : false); + } else { + throw new IOException(sprintf('Unable to guess "%s" file type.', $file), 0, null, $file); + } + } + } + } + + /** + * Returns whether the file path is an absolute path. + * + * @param string $file A file path + * + * @return bool + */ + public function isAbsolutePath($file) + { + return strspn($file, '/\\', 0, 1) + || (\strlen($file) > 3 && ctype_alpha($file[0]) + && ':' === $file[1] + && strspn($file, '/\\', 2, 1) + ) + || null !== parse_url($file, PHP_URL_SCHEME) + ; + } + + /** + * Creates a temporary file with support for custom stream wrappers. + * + * @param string $dir The directory where the temporary filename will be created + * @param string $prefix The prefix of the generated temporary filename + * Note: Windows uses only the first three characters of prefix + * + * @return string The new temporary filename (with path), or throw an exception on failure + */ + public function tempnam($dir, $prefix) + { + list($scheme, $hierarchy) = $this->getSchemeAndHierarchy($dir); + + // If no scheme or scheme is "file" or "gs" (Google Cloud) create temp file in local filesystem + if (null === $scheme || 'file' === $scheme || 'gs' === $scheme) { + $tmpFile = @tempnam($hierarchy, $prefix); + + // If tempnam failed or no scheme return the filename otherwise prepend the scheme + if (false !== $tmpFile) { + if (null !== $scheme && 'gs' !== $scheme) { + return $scheme.'://'.$tmpFile; + } + + return $tmpFile; + } + + throw new IOException('A temporary file could not be created.'); + } + + // Loop until we create a valid temp file or have reached 10 attempts + for ($i = 0; $i < 10; ++$i) { + // Create a unique filename + $tmpFile = $dir.'/'.$prefix.uniqid(mt_rand(), true); + + // Use fopen instead of file_exists as some streams do not support stat + // Use mode 'x+' to atomically check existence and create to avoid a TOCTOU vulnerability + $handle = @fopen($tmpFile, 'x+'); + + // If unsuccessful restart the loop + if (false === $handle) { + continue; + } + + // Close the file if it was successfully opened + @fclose($handle); + + return $tmpFile; + } + + throw new IOException('A temporary file could not be created.'); + } + + /** + * Atomically dumps content into a file. + * + * @param string $filename The file to be written to + * @param string $content The data to write into the file + * + * @throws IOException if the file cannot be written to + */ + public function dumpFile($filename, $content) + { + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + if (!is_writable($dir)) { + throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); + } + + // Will create a temp file with 0600 access rights + // when the filesystem supports chmod. + $tmpFile = $this->tempnam($dir, basename($filename)); + + if (false === @file_put_contents($tmpFile, $content)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } + + @chmod($tmpFile, file_exists($filename) ? fileperms($filename) : 0666 & ~umask()); + + $this->rename($tmpFile, $filename, true); + } + + /** + * Appends content to an existing file. + * + * @param string $filename The file to which to append content + * @param string $content The content to append + * + * @throws IOException If the file is not writable + */ + public function appendToFile($filename, $content) + { + $dir = \dirname($filename); + + if (!is_dir($dir)) { + $this->mkdir($dir); + } + + if (!is_writable($dir)) { + throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir); + } + + if (false === @file_put_contents($filename, $content, FILE_APPEND)) { + throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, null, $filename); + } + } + + private function toIterable($files): iterable + { + return \is_array($files) || $files instanceof \Traversable ? $files : array($files); + } + + /** + * Gets a 2-tuple of scheme (may be null) and hierarchical part of a filename (e.g. file:///tmp -> array(file, tmp)). + */ + private function getSchemeAndHierarchy(string $filename): array + { + $components = explode('://', $filename, 2); + + return 2 === \count($components) ? array($components[0], $components[1]) : array(null, $components[0]); + } + + private static function box($func) + { + self::$lastError = null; + \set_error_handler(__CLASS__.'::handleError'); + try { + $result = \call_user_func_array($func, \array_slice(\func_get_args(), 1)); + \restore_error_handler(); + + return $result; + } catch (\Throwable $e) { + } catch (\Exception $e) { + } + \restore_error_handler(); + + throw $e; + } + + /** + * @internal + */ + public static function handleError($type, $msg) + { + self::$lastError = $msg; + } +} diff --git a/vendor/symfony/filesystem/LICENSE b/vendor/symfony/filesystem/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/filesystem/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/vendor/symfony/filesystem/README.md b/vendor/symfony/filesystem/README.md new file mode 100644 index 0000000..877ab35 --- /dev/null +++ b/vendor/symfony/filesystem/README.md @@ -0,0 +1,13 @@ +Filesystem Component +==================== + +The Filesystem component provides basic utilities for the filesystem. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/filesystem/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/vendor/symfony/filesystem/Tests/ExceptionTest.php b/vendor/symfony/filesystem/Tests/ExceptionTest.php new file mode 100644 index 0000000..300acf1 --- /dev/null +++ b/vendor/symfony/filesystem/Tests/ExceptionTest.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\Filesystem\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Filesystem\Exception\FileNotFoundException; +use Symfony\Component\Filesystem\Exception\IOException; + +/** + * Test class for Filesystem. + */ +class ExceptionTest extends TestCase +{ + public function testGetPath() + { + $e = new IOException('', 0, null, '/foo'); + $this->assertEquals('/foo', $e->getPath(), 'The pass should be returned.'); + } + + public function testGeneratedMessage() + { + $e = new FileNotFoundException(null, 0, null, '/foo'); + $this->assertEquals('/foo', $e->getPath()); + $this->assertEquals('File "/foo" could not be found.', $e->getMessage(), 'A message should be generated.'); + } + + public function testGeneratedMessageWithoutPath() + { + $e = new FileNotFoundException(); + $this->assertEquals('File could not be found.', $e->getMessage(), 'A message should be generated.'); + } + + public function testCustomMessage() + { + $e = new FileNotFoundException('bar', 0, null, '/foo'); + $this->assertEquals('bar', $e->getMessage(), 'A custom message should be possible still.'); + } +} diff --git a/vendor/symfony/filesystem/Tests/FilesystemTest.php b/vendor/symfony/filesystem/Tests/FilesystemTest.php new file mode 100644 index 0000000..d9da651 --- /dev/null +++ b/vendor/symfony/filesystem/Tests/FilesystemTest.php @@ -0,0 +1,1646 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +/** + * Test class for Filesystem. + */ +class FilesystemTest extends FilesystemTestCase +{ + public function testCopyCreatesNewFile() + { + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE'); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testCopyFails() + { + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testCopyUnreadableFileFails() + { + // skip test on Windows; PHP can't easily set file as unreadable on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test cannot run on Windows.'); + } + + if (!getenv('USER') || 'root' === getenv('USER')) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + // make sure target cannot be read + $this->filesystem->chmod($sourceFilePath, 0222); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + } + + public function testCopyOverridesExistingFileIfModified() + { + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + touch($targetFilePath, time() - 1000); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE'); + } + + public function testCopyDoesNotOverrideExistingFileByDefault() + { + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFileExists($targetFilePath); + $this->assertStringEqualsFile($targetFilePath, 'TARGET FILE'); + } + + public function testCopyOverridesExistingFileIfForced() + { + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, true); + + $this->assertFileExists($targetFilePath); + $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE'); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testCopyWithOverrideWithReadOnlyTargetFails() + { + // skip test on Windows; PHP can't easily set file as unwritable on Windows + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('This test cannot run on Windows.'); + } + + if (!getenv('USER') || 'root' === getenv('USER')) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + file_put_contents($targetFilePath, 'TARGET FILE'); + + // make sure both files have the same modification time + $modificationTime = time() - 1000; + touch($sourceFilePath, $modificationTime); + touch($targetFilePath, $modificationTime); + + // make sure target is read-only + $this->filesystem->chmod($targetFilePath, 0444); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, true); + } + + public function testCopyCreatesTargetDirectoryIfItDoesNotExist() + { + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFileDirectory = $this->workspace.\DIRECTORY_SEPARATOR.'directory'; + $targetFilePath = $targetFileDirectory.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertTrue(is_dir($targetFileDirectory)); + $this->assertFileExists($targetFilePath); + $this->assertStringEqualsFile($targetFilePath, 'SOURCE FILE'); + } + + /** + * @group network + */ + public function testCopyForOriginUrlsAndExistingLocalFileDefaultsToCopy() + { + if (!\in_array('https', stream_get_wrappers())) { + $this->markTestSkipped('"https" stream wrapper is not enabled.'); + } + $sourceFilePath = 'https://symfony.com/images/common/logo/logo_symfony_header.png'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($targetFilePath, 'TARGET FILE'); + + $this->filesystem->copy($sourceFilePath, $targetFilePath, false); + + $this->assertFileExists($targetFilePath); + $this->assertEquals(file_get_contents($sourceFilePath), file_get_contents($targetFilePath)); + } + + public function testMkdirCreatesDirectoriesRecursively() + { + $directory = $this->workspace + .\DIRECTORY_SEPARATOR.'directory' + .\DIRECTORY_SEPARATOR.'sub_directory'; + + $this->filesystem->mkdir($directory); + + $this->assertTrue(is_dir($directory)); + } + + public function testMkdirCreatesDirectoriesFromArray() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + $directories = array( + $basePath.'1', $basePath.'2', $basePath.'3', + ); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + public function testMkdirCreatesDirectoriesFromTraversableObject() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + $directories = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3', + )); + + $this->filesystem->mkdir($directories); + + $this->assertTrue(is_dir($basePath.'1')); + $this->assertTrue(is_dir($basePath.'2')); + $this->assertTrue(is_dir($basePath.'3')); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testMkdirCreatesDirectoriesFails() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + $dir = $basePath.'2'; + + file_put_contents($dir, ''); + + $this->filesystem->mkdir($dir); + } + + public function testTouchCreatesEmptyFile() + { + $file = $this->workspace.\DIRECTORY_SEPARATOR.'1'; + + $this->filesystem->touch($file); + + $this->assertFileExists($file); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTouchFails() + { + $file = $this->workspace.\DIRECTORY_SEPARATOR.'1'.\DIRECTORY_SEPARATOR.'2'; + + $this->filesystem->touch($file); + } + + public function testTouchCreatesEmptyFilesFromArray() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + $files = array( + $basePath.'1', $basePath.'2', $basePath.'3', + ); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testTouchCreatesEmptyFilesFromTraversableObject() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + $files = new \ArrayObject(array( + $basePath.'1', $basePath.'2', $basePath.'3', + )); + + $this->filesystem->touch($files); + + $this->assertFileExists($basePath.'1'); + $this->assertFileExists($basePath.'2'); + $this->assertFileExists($basePath.'3'); + } + + public function testRemoveCleansFilesAndDirectoriesIteratively() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR.'directory'.\DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $this->filesystem->remove($basePath); + + $this->assertFileNotExists($basePath); + } + + public function testRemoveCleansArrayOfFilesAndDirectories() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = array( + $basePath.'dir', $basePath.'file', + ); + + $this->filesystem->remove($files); + + $this->assertFileNotExists($basePath.'dir'); + $this->assertFileNotExists($basePath.'file'); + } + + public function testRemoveCleansTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file', + )); + + $this->filesystem->remove($files); + + $this->assertFileNotExists($basePath.'dir'); + $this->assertFileNotExists($basePath.'file'); + } + + public function testRemoveIgnoresNonExistingFiles() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + + $files = array( + $basePath.'dir', $basePath.'file', + ); + + $this->filesystem->remove($files); + + $this->assertFileNotExists($basePath.'dir'); + } + + public function testRemoveCleansInvalidLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $basePath = $this->workspace.\DIRECTORY_SEPARATOR.'directory'.\DIRECTORY_SEPARATOR; + + mkdir($basePath); + mkdir($basePath.'dir'); + // create symlink to nonexistent file + @symlink($basePath.'file', $basePath.'file-link'); + + // create symlink to dir using trailing forward slash + $this->filesystem->symlink($basePath.'dir/', $basePath.'dir-link'); + $this->assertTrue(is_dir($basePath.'dir-link')); + + // create symlink to nonexistent dir + rmdir($basePath.'dir'); + $this->assertFalse('\\' === \DIRECTORY_SEPARATOR ? @readlink($basePath.'dir-link') : is_dir($basePath.'dir-link')); + + $this->filesystem->remove($basePath); + + $this->assertFileNotExists($basePath); + } + + public function testFilesExists() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR.'directory'.\DIRECTORY_SEPARATOR; + + mkdir($basePath); + touch($basePath.'file1'); + mkdir($basePath.'folder'); + + $this->assertTrue($this->filesystem->exists($basePath.'file1')); + $this->assertTrue($this->filesystem->exists($basePath.'folder')); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testFilesExistsFails() + { + if ('\\' !== \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Long file names are an issue on Windows'); + } + $basePath = $this->workspace.'\\directory\\'; + $maxPathLength = PHP_MAXPATHLEN - 2; + + $oldPath = getcwd(); + mkdir($basePath); + chdir($basePath); + $file = str_repeat('T', $maxPathLength - \strlen($basePath) + 1); + $path = $basePath.$file; + exec('TYPE NUL >>'.$file); // equivalent of touch, we can not use the php touch() here because it suffers from the same limitation + $this->longPathNamesWindows[] = $path; // save this so we can clean up later + chdir($oldPath); + $this->filesystem->exists($path); + } + + public function testFilesExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file', + )); + + $this->assertTrue($this->filesystem->exists($files)); + } + + public function testFilesNotExistsTraversableObjectOfFilesAndDirectories() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR; + + mkdir($basePath.'dir'); + touch($basePath.'file'); + touch($basePath.'file2'); + + $files = new \ArrayObject(array( + $basePath.'dir', $basePath.'file', $basePath.'file2', + )); + + unlink($basePath.'file'); + + $this->assertFalse($this->filesystem->exists($files)); + } + + public function testInvalidFileNotExists() + { + $basePath = $this->workspace.\DIRECTORY_SEPARATOR.'directory'.\DIRECTORY_SEPARATOR; + + $this->assertFalse($this->filesystem->exists($basePath.time())); + } + + public function testChmodChangesFileMode() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.\DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400); + $this->filesystem->chmod($dir, 0753); + + $this->assertFilePermissions(753, $dir); + $this->assertFilePermissions(400, $file); + } + + public function testChmodWithWrongModLeavesPreviousPermissionsUntouched() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + touch($dir); + + $permissions = fileperms($dir); + + $this->filesystem->chmod($dir, 'Wrongmode'); + + $this->assertSame($permissions, fileperms($dir)); + } + + public function testChmodRecursive() + { + $this->markAsSkippedIfChmodIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.\DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0400, 0000, true); + $this->filesystem->chmod($dir, 0753, 0000, true); + + $this->assertFilePermissions(753, $dir); + $this->assertFilePermissions(753, $file); + } + + public function testChmodAppliesUmask() + { + $this->markAsSkippedIfChmodIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + touch($file); + + $this->filesystem->chmod($file, 0770, 0022); + $this->assertFilePermissions(750, $file); + } + + public function testChmodChangesModeOfArrayOfFiles() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.\DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $files = array($directory, $file); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertFilePermissions(753, $file); + $this->assertFilePermissions(753, $directory); + } + + public function testChmodChangesModeOfTraversableFileObject() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.\DIRECTORY_SEPARATOR.'directory'; + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $files = new \ArrayObject(array($directory, $file)); + + mkdir($directory); + touch($file); + + $this->filesystem->chmod($files, 0753); + + $this->assertFilePermissions(753, $file); + $this->assertFilePermissions(753, $directory); + } + + public function testChmodChangesZeroModeOnSubdirectoriesOnRecursive() + { + $this->markAsSkippedIfChmodIsMissing(); + + $directory = $this->workspace.\DIRECTORY_SEPARATOR.'directory'; + $subdirectory = $directory.\DIRECTORY_SEPARATOR.'subdirectory'; + + mkdir($directory); + mkdir($subdirectory); + chmod($subdirectory, 0000); + + $this->filesystem->chmod($directory, 0753, 0000, true); + + $this->assertFilePermissions(753, $subdirectory); + } + + public function testChown() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $owner = $this->getFileOwner($dir); + $this->filesystem->chown($dir, $owner); + + $this->assertSame($owner, $this->getFileOwner($dir)); + } + + public function testChownRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.\DIRECTORY_SEPARATOR.'file'; + touch($file); + + $owner = $this->getFileOwner($dir); + $this->filesystem->chown($dir, $owner, true); + + $this->assertSame($owner, $this->getFileOwner($file)); + } + + public function testChownSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $owner = $this->getFileOwner($link); + $this->filesystem->chown($link, $owner); + + $this->assertSame($owner, $this->getFileOwner($link)); + } + + public function testChownLink() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->hardlink($file, $link); + + $owner = $this->getFileOwner($link); + $this->filesystem->chown($link, $owner); + + $this->assertSame($owner, $this->getFileOwner($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownLinkFails() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->hardlink($file, $link); + + $this->filesystem->chown($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChownFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chown($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testChgrp() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $group = $this->getFileGroup($dir); + $this->filesystem->chgrp($dir, $group); + + $this->assertSame($group, $this->getFileGroup($dir)); + } + + public function testChgrpRecursive() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + $file = $dir.\DIRECTORY_SEPARATOR.'file'; + touch($file); + + $group = $this->getFileGroup($dir); + $this->filesystem->chgrp($dir, $group, true); + + $this->assertSame($group, $this->getFileGroup($file)); + } + + public function testChgrpSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $group = $this->getFileGroup($link); + $this->filesystem->chgrp($link, $group); + + $this->assertSame($group, $this->getFileGroup($link)); + } + + public function testChgrpLink() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->hardlink($file, $link); + + $group = $this->getFileGroup($link); + $this->filesystem->chgrp($link, $group); + + $this->assertSame($group, $this->getFileGroup($link)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpSymlinkFails() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link); + + $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpLinkFails() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->hardlink($file, $link); + + $this->filesystem->chgrp($link, 'user'.time().mt_rand(1000, 9999)); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testChgrpFail() + { + $this->markAsSkippedIfPosixIsMissing(); + + $dir = $this->workspace.\DIRECTORY_SEPARATOR.'dir'; + mkdir($dir); + + $this->filesystem->chgrp($dir, 'user'.time().mt_rand(1000, 9999)); + } + + public function testRename() + { + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.\DIRECTORY_SEPARATOR.'new_file'; + touch($file); + + $this->filesystem->rename($file, $newPath); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionIfTargetAlreadyExists() + { + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.\DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath); + } + + public function testRenameOverwritesTheTargetIfItAlreadyExists() + { + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $newPath = $this->workspace.\DIRECTORY_SEPARATOR.'new_file'; + + touch($file); + touch($newPath); + + $this->filesystem->rename($file, $newPath, true); + + $this->assertFileNotExists($file); + $this->assertFileExists($newPath); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testRenameThrowsExceptionOnError() + { + $file = $this->workspace.\DIRECTORY_SEPARATOR.uniqid('fs_test_', true); + $newPath = $this->workspace.\DIRECTORY_SEPARATOR.'new_file'; + + $this->filesystem->rename($file, $newPath); + } + + public function testSymlink() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Windows does not support creating "broken" symlinks'); + } + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + // $file does not exists right now: creating "broken" links is a wanted feature + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + + // Create the linked file AFTER creating the link + touch($file); + + $this->assertEquals($file, readlink($link)); + } + + /** + * @depends testSymlink + */ + public function testRemoveSymlink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + $this->filesystem->remove($link); + + $this->assertFalse(is_link($link)); + $this->assertFalse(is_file($link)); + $this->assertFalse(is_dir($link)); + } + + public function testSymlinkIsOverwrittenIfPointsToDifferentTarget() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($this->workspace, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkIsNotOverwrittenIfAlreadyCreated() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + symlink($file, $link); + + $this->filesystem->symlink($file, $link); + + $this->assertTrue(is_link($link)); + $this->assertEquals($file, readlink($link)); + } + + public function testSymlinkCreatesTargetDirectoryIfItDoesNotExist() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link1 = $this->workspace.\DIRECTORY_SEPARATOR.'dir'.\DIRECTORY_SEPARATOR.'link'; + $link2 = $this->workspace.\DIRECTORY_SEPARATOR.'dir'.\DIRECTORY_SEPARATOR.'subdir'.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + $this->filesystem->symlink($file, $link1); + $this->filesystem->symlink($file, $link2); + + $this->assertTrue(is_link($link1)); + $this->assertEquals($file, readlink($link1)); + $this->assertTrue(is_link($link2)); + $this->assertEquals($file, readlink($link2)); + } + + public function testLink() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + $this->filesystem->hardlink($file, $link); + + $this->assertTrue(is_file($link)); + $this->assertEquals(fileinode($file), fileinode($link)); + } + + /** + * @depends testLink + */ + public function testRemoveLink() + { + $this->markAsSkippedIfLinkIsMissing(); + + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + $this->filesystem->remove($link); + + $this->assertTrue(!is_file($link)); + } + + public function testLinkIsOverwrittenIfPointsToDifferentTarget() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $file2 = $this->workspace.\DIRECTORY_SEPARATOR.'file2'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + touch($file2); + link($file2, $link); + + $this->filesystem->hardlink($file, $link); + + $this->assertTrue(is_file($link)); + $this->assertEquals(fileinode($file), fileinode($link)); + } + + public function testLinkIsNotOverwrittenIfAlreadyCreated() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + link($file, $link); + + $this->filesystem->hardlink($file, $link); + + $this->assertTrue(is_file($link)); + $this->assertEquals(fileinode($file), fileinode($link)); + } + + public function testLinkWithSeveralTargets() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link1 = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + $link2 = $this->workspace.\DIRECTORY_SEPARATOR.'link2'; + + touch($file); + + $this->filesystem->hardlink($file, array($link1, $link2)); + + $this->assertTrue(is_file($link1)); + $this->assertEquals(fileinode($file), fileinode($link1)); + $this->assertTrue(is_file($link2)); + $this->assertEquals(fileinode($file), fileinode($link2)); + } + + public function testLinkWithSameTarget() + { + $this->markAsSkippedIfLinkIsMissing(); + + $file = $this->workspace.\DIRECTORY_SEPARATOR.'file'; + $link = $this->workspace.\DIRECTORY_SEPARATOR.'link'; + + touch($file); + + // practically same as testLinkIsNotOverwrittenIfAlreadyCreated + $this->filesystem->hardlink($file, array($link, $link)); + + $this->assertTrue(is_file($link)); + $this->assertEquals(fileinode($file), fileinode($link)); + } + + public function testReadRelativeLink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Relative symbolic links are not supported on Windows'); + } + + $file = $this->workspace.'/file'; + $link1 = $this->workspace.'/dir/link'; + $link2 = $this->workspace.'/dir/link2'; + touch($file); + + $this->filesystem->symlink('../file', $link1); + $this->filesystem->symlink('link', $link2); + + $this->assertEquals($this->normalize('../file'), $this->filesystem->readlink($link1)); + $this->assertEquals('link', $this->filesystem->readlink($link2)); + $this->assertEquals($file, $this->filesystem->readlink($link1, true)); + $this->assertEquals($file, $this->filesystem->readlink($link2, true)); + $this->assertEquals($file, $this->filesystem->readlink($file, true)); + } + + public function testReadAbsoluteLink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->normalize($this->workspace.'/file'); + $link1 = $this->normalize($this->workspace.'/dir/link'); + $link2 = $this->normalize($this->workspace.'/dir/link2'); + touch($file); + + $this->filesystem->symlink($file, $link1); + $this->filesystem->symlink($link1, $link2); + + $this->assertEquals($file, $this->filesystem->readlink($link1)); + $this->assertEquals($link1, $this->filesystem->readlink($link2)); + $this->assertEquals($file, $this->filesystem->readlink($link1, true)); + $this->assertEquals($file, $this->filesystem->readlink($link2, true)); + $this->assertEquals($file, $this->filesystem->readlink($file, true)); + } + + public function testReadBrokenLink() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Windows does not support creating "broken" symlinks'); + } + + $file = $this->workspace.'/file'; + $link = $this->workspace.'/link'; + + $this->filesystem->symlink($file, $link); + + $this->assertEquals($file, $this->filesystem->readlink($link)); + $this->assertNull($this->filesystem->readlink($link, true)); + + touch($file); + $this->assertEquals($file, $this->filesystem->readlink($link, true)); + } + + public function testReadLinkDefaultPathDoesNotExist() + { + $this->assertNull($this->filesystem->readlink($this->normalize($this->workspace.'/invalid'))); + } + + public function testReadLinkDefaultPathNotLink() + { + $file = $this->normalize($this->workspace.'/file'); + touch($file); + + $this->assertNull($this->filesystem->readlink($file)); + } + + public function testReadLinkCanonicalizePath() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $file = $this->normalize($this->workspace.'/file'); + mkdir($this->normalize($this->workspace.'/dir')); + touch($file); + + $this->assertEquals($file, $this->filesystem->readlink($this->normalize($this->workspace.'/dir/../file'), true)); + } + + public function testReadLinkCanonicalizedPathDoesNotExist() + { + $this->assertNull($this->filesystem->readlink($this->normalize($this->workspace.'invalid'), true)); + } + + /** + * @dataProvider providePathsForMakePathRelative + */ + public function testMakePathRelative($endPath, $startPath, $expectedPath) + { + $path = $this->filesystem->makePathRelative($endPath, $startPath); + + $this->assertEquals($expectedPath, $path); + } + + public function providePathsForMakePathRelative() + { + $paths = array( + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component', '../'), + array('/var/lib/symfony/src/Symfony', '/var/lib/symfony/src/Symfony/Component/', '../'), + array('/usr/lib/symfony/', '/var/lib/symfony/src/Symfony/Component', '../../../../../../usr/lib/symfony/'), + array('/var/lib/symfony/src/Symfony/', '/var/lib/symfony/', 'src/Symfony/'), + array('/aa/bb', '/aa/bb', './'), + array('/aa/bb', '/aa/bb/', './'), + array('/aa/bb/', '/aa/bb', './'), + array('/aa/bb/', '/aa/bb/', './'), + array('/aa/bb/cc', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd', '../'), + array('/aa/bb/cc/', '/aa/bb/cc/dd/', '../'), + array('/aa/bb/cc', '/aa', 'bb/cc/'), + array('/aa/bb/cc', '/aa/', 'bb/cc/'), + array('/aa/bb/cc/', '/aa', 'bb/cc/'), + array('/aa/bb/cc/', '/aa/', 'bb/cc/'), + array('/a/aab/bb', '/a/aa', '../aab/bb/'), + array('/a/aab/bb', '/a/aa/', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa', '../aab/bb/'), + array('/a/aab/bb/', '/a/aa/', '../aab/bb/'), + array('/a/aab/bb/', '/', 'a/aab/bb/'), + array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'), + array('/aab/bb', '/aa', '../aab/bb/'), + array('/aab', '/aa', '../aab/'), + array('/aa/bb/cc', '/aa/dd/..', 'bb/cc/'), + array('/aa/../bb/cc', '/aa/dd/..', '../bb/cc/'), + array('/aa/bb/../../cc', '/aa/../dd/..', 'cc/'), + array('/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'), + array('/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'), + array('C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'), + array('c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'), + array('C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'), + array('C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'), + array('C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'), + ); + + if ('\\' === \DIRECTORY_SEPARATOR) { + $paths[] = array('c:\var\lib/symfony/src/Symfony/', 'c:/var/lib/symfony/', 'src/Symfony/'); + } + + return $paths; + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\InvalidArgumentException + * @expectedExceptionMessage The start path "var/lib/symfony/src/Symfony/Component" is not absolute. + */ + public function testMakePathRelativeWithRelativeStartPath() + { + $this->assertSame('../../../', $this->filesystem->makePathRelative('/var/lib/symfony/', 'var/lib/symfony/src/Symfony/Component')); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\InvalidArgumentException + * @expectedExceptionMessage The end path "var/lib/symfony/" is not absolute. + */ + public function testMakePathRelativeWithRelativeEndPath() + { + $this->assertSame('../../../', $this->filesystem->makePathRelative('var/lib/symfony/', '/var/lib/symfony/src/Symfony/Component')); + } + + public function testMirrorCopiesFilesAndDirectoriesRecursively() + { + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + $directory = $sourcePath.'directory'.\DIRECTORY_SEPARATOR; + $file1 = $directory.'file1'; + $file2 = $sourcePath.'file2'; + + mkdir($sourcePath); + mkdir($directory); + file_put_contents($file1, 'FILE1'); + file_put_contents($file2, 'FILE2'); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertTrue(is_dir($targetPath.'directory')); + $this->assertFileEquals($file1, $targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1'); + $this->assertFileEquals($file2, $targetPath.'file2'); + + $this->filesystem->remove($file1); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => false)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1')); + + file_put_contents($file1, 'FILE1'); + + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertTrue($this->filesystem->exists($targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1')); + + $this->filesystem->remove($directory); + $this->filesystem->mirror($sourcePath, $targetPath, null, array('delete' => true)); + $this->assertFalse($this->filesystem->exists($targetPath.'directory')); + $this->assertFalse($this->filesystem->exists($targetPath.'directory'.\DIRECTORY_SEPARATOR.'file1')); + } + + public function testMirrorCreatesEmptyDirectory() + { + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + + $this->filesystem->remove($sourcePath); + } + + public function testMirrorCopiesLinks() + { + $this->markAsSkippedIfSymlinkIsMissing(); + + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + file_put_contents($sourcePath.'file1', 'FILE1'); + symlink($sourcePath.'file1', $sourcePath.'link1'); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'file1', $targetPath.'link1'); + $this->assertTrue(is_link($targetPath.\DIRECTORY_SEPARATOR.'link1')); + } + + public function testMirrorCopiesLinkedDirectoryContents() + { + $this->markAsSkippedIfSymlinkIsMissing(true); + + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + + mkdir($sourcePath.'nested/', 0777, true); + file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); + // Note: We symlink directory, not file + symlink($sourcePath.'nested', $sourcePath.'link1'); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt'); + $this->assertTrue(is_link($targetPath.\DIRECTORY_SEPARATOR.'link1')); + } + + public function testMirrorCopiesRelativeLinkedContents() + { + $this->markAsSkippedIfSymlinkIsMissing(true); + + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + $oldPath = getcwd(); + + mkdir($sourcePath.'nested/', 0777, true); + file_put_contents($sourcePath.'/nested/file1.txt', 'FILE1'); + // Note: Create relative symlink + chdir($sourcePath); + symlink('nested', 'link1'); + + chdir($oldPath); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + $this->filesystem->mirror($sourcePath, $targetPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileEquals($sourcePath.'/nested/file1.txt', $targetPath.'link1/file1.txt'); + $this->assertTrue(is_link($targetPath.\DIRECTORY_SEPARATOR.'link1')); + $this->assertEquals('\\' === \DIRECTORY_SEPARATOR ? realpath($sourcePath.'\nested') : 'nested', readlink($targetPath.\DIRECTORY_SEPARATOR.'link1')); + } + + public function testMirrorContentsWithSameNameAsSourceOrTargetWithoutDeleteOption() + { + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + touch($sourcePath.'source'); + touch($sourcePath.'target'); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + $oldPath = getcwd(); + chdir($this->workspace); + + $this->filesystem->mirror('source', $targetPath); + + chdir($oldPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileExists($targetPath.'source'); + $this->assertFileExists($targetPath.'target'); + } + + public function testMirrorContentsWithSameNameAsSourceOrTargetWithDeleteOption() + { + $sourcePath = $this->workspace.\DIRECTORY_SEPARATOR.'source'.\DIRECTORY_SEPARATOR; + + mkdir($sourcePath); + touch($sourcePath.'source'); + + $targetPath = $this->workspace.\DIRECTORY_SEPARATOR.'target'.\DIRECTORY_SEPARATOR; + + mkdir($targetPath); + touch($targetPath.'source'); + touch($targetPath.'target'); + + $oldPath = getcwd(); + chdir($this->workspace); + + $this->filesystem->mirror('source', 'target', null, array('delete' => true)); + + chdir($oldPath); + + $this->assertTrue(is_dir($targetPath)); + $this->assertFileExists($targetPath.'source'); + $this->assertFileNotExists($targetPath.'target'); + } + + /** + * @dataProvider providePathsForIsAbsolutePath + */ + public function testIsAbsolutePath($path, $expectedResult) + { + $result = $this->filesystem->isAbsolutePath($path); + + $this->assertEquals($expectedResult, $result); + } + + public function providePathsForIsAbsolutePath() + { + return array( + array('/var/lib', true), + array('c:\\\\var\\lib', true), + array('\\var\\lib', true), + array('var/lib', false), + array('../var/lib', false), + array('', false), + array(null, false), + ); + } + + public function testTempnam() + { + $dirname = $this->workspace; + + $filename = $this->filesystem->tempnam($dirname, 'foo'); + + $this->assertFileExists($filename); + } + + public function testTempnamWithFileScheme() + { + $scheme = 'file://'; + $dirname = $scheme.$this->workspace; + + $filename = $this->filesystem->tempnam($dirname, 'foo'); + + $this->assertStringStartsWith($scheme, $filename); + $this->assertFileExists($filename); + } + + public function testTempnamWithMockScheme() + { + stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream'); + + $scheme = 'mock://'; + $dirname = $scheme.$this->workspace; + + $filename = $this->filesystem->tempnam($dirname, 'foo'); + + $this->assertStringStartsWith($scheme, $filename); + $this->assertFileExists($filename); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTempnamWithZlibSchemeFails() + { + $scheme = 'compress.zlib://'; + $dirname = $scheme.$this->workspace; + + // The compress.zlib:// stream does not support mode x: creates the file, errors "failed to open stream: operation failed" and returns false + $this->filesystem->tempnam($dirname, 'bar'); + } + + public function testTempnamWithPHPTempSchemeFails() + { + $scheme = 'php://temp'; + $dirname = $scheme; + + $filename = $this->filesystem->tempnam($dirname, 'bar'); + + $this->assertStringStartsWith($scheme, $filename); + + // The php://temp stream deletes the file after close + $this->assertFileNotExists($filename); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTempnamWithPharSchemeFails() + { + // Skip test if Phar disabled phar.readonly must be 0 in php.ini + if (!\Phar::canWrite()) { + $this->markTestSkipped('This test cannot run when phar.readonly is 1.'); + } + + $scheme = 'phar://'; + $dirname = $scheme.$this->workspace; + $pharname = 'foo.phar'; + + new \Phar($this->workspace.'/'.$pharname, 0, $pharname); + // The phar:// stream does not support mode x: fails to create file, errors "failed to open stream: phar error: "$filename" is not a file in phar "$pharname"" and returns false + $this->filesystem->tempnam($dirname, $pharname.'/bar'); + } + + /** + * @expectedException \Symfony\Component\Filesystem\Exception\IOException + */ + public function testTempnamWithHTTPSchemeFails() + { + $scheme = 'http://'; + $dirname = $scheme.$this->workspace; + + // The http:// scheme is read-only + $this->filesystem->tempnam($dirname, 'bar'); + } + + public function testTempnamOnUnwritableFallsBackToSysTmp() + { + $scheme = 'file://'; + $dirname = $scheme.$this->workspace.\DIRECTORY_SEPARATOR.'does_not_exist'; + + $filename = $this->filesystem->tempnam($dirname, 'bar'); + $realTempDir = realpath(sys_get_temp_dir()); + $this->assertStringStartsWith(rtrim($scheme.$realTempDir, \DIRECTORY_SEPARATOR), $filename); + $this->assertFileExists($filename); + + // Tear down + @unlink($filename); + } + + public function testDumpFile() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + // skip mode check on Windows + if ('\\' !== \DIRECTORY_SEPARATOR) { + $oldMask = umask(0002); + } + + $this->filesystem->dumpFile($filename, 'bar'); + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + + // skip mode check on Windows + if ('\\' !== \DIRECTORY_SEPARATOR) { + $this->assertFilePermissions(664, $filename); + umask($oldMask); + } + } + + public function testDumpFileWithArray() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, array('bar')); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testDumpFileWithResource() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + $resource = fopen('php://memory', 'rw'); + fwrite($resource, 'bar'); + fseek($resource, 0); + + $this->filesystem->dumpFile($filename, $resource); + + fclose($resource); + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testDumpFileOverwritesAnExistingFile() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + + $this->filesystem->dumpFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testDumpFileWithFileScheme() + { + $scheme = 'file://'; + $filename = $scheme.$this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testDumpFileWithZlibScheme() + { + $scheme = 'compress.zlib://'; + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + + $this->filesystem->dumpFile($filename, 'bar'); + + // Zlib stat uses file:// wrapper so remove scheme + $this->assertFileExists(str_replace($scheme, '', $filename)); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testAppendToFile() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'bar.txt'; + + // skip mode check on Windows + if ('\\' !== \DIRECTORY_SEPARATOR) { + $oldMask = umask(0002); + } + + $this->filesystem->dumpFile($filename, 'foo'); + + $this->filesystem->appendToFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'foobar'); + + // skip mode check on Windows + if ('\\' !== \DIRECTORY_SEPARATOR) { + $this->assertFilePermissions(664, $filename); + umask($oldMask); + } + } + + public function testAppendToFileWithScheme() + { + $scheme = 'file://'; + $filename = $scheme.$this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + $this->filesystem->dumpFile($filename, 'foo'); + + $this->filesystem->appendToFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'foobar'); + } + + public function testAppendToFileWithZlibScheme() + { + $scheme = 'compress.zlib://'; + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'baz.txt'; + $this->filesystem->dumpFile($filename, 'foo'); + + // Zlib stat uses file:// wrapper so remove it + $this->assertStringEqualsFile(str_replace($scheme, '', $filename), 'foo'); + + $this->filesystem->appendToFile($filename, 'bar'); + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'foobar'); + } + + public function testAppendToFileCreateTheFileIfNotExists() + { + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo'.\DIRECTORY_SEPARATOR.'bar.txt'; + + // skip mode check on Windows + if ('\\' !== \DIRECTORY_SEPARATOR) { + $oldMask = umask(0002); + } + + $this->filesystem->appendToFile($filename, 'bar'); + + // skip mode check on Windows + if ('\\' !== \DIRECTORY_SEPARATOR) { + $this->assertFilePermissions(664, $filename); + umask($oldMask); + } + + $this->assertFileExists($filename); + $this->assertStringEqualsFile($filename, 'bar'); + } + + public function testDumpKeepsExistingPermissionsWhenOverwritingAnExistingFile() + { + $this->markAsSkippedIfChmodIsMissing(); + + $filename = $this->workspace.\DIRECTORY_SEPARATOR.'foo.txt'; + file_put_contents($filename, 'FOO BAR'); + chmod($filename, 0745); + + $this->filesystem->dumpFile($filename, 'bar', null); + + $this->assertFilePermissions(745, $filename); + } + + public function testCopyShouldKeepExecutionPermission() + { + $this->markAsSkippedIfChmodIsMissing(); + + $sourceFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_source_file'; + $targetFilePath = $this->workspace.\DIRECTORY_SEPARATOR.'copy_target_file'; + + file_put_contents($sourceFilePath, 'SOURCE FILE'); + chmod($sourceFilePath, 0745); + + $this->filesystem->copy($sourceFilePath, $targetFilePath); + + $this->assertFilePermissions(767, $targetFilePath); + } + + /** + * Normalize the given path (transform each blackslash into a real directory separator). + */ + private function normalize(string $path): string + { + return str_replace('/', \DIRECTORY_SEPARATOR, $path); + } +} diff --git a/vendor/symfony/filesystem/Tests/FilesystemTestCase.php b/vendor/symfony/filesystem/Tests/FilesystemTestCase.php new file mode 100644 index 0000000..642925d --- /dev/null +++ b/vendor/symfony/filesystem/Tests/FilesystemTestCase.php @@ -0,0 +1,166 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Filesystem\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Filesystem\Filesystem; + +class FilesystemTestCase extends TestCase +{ + private $umask; + + protected $longPathNamesWindows = array(); + + /** + * @var \Symfony\Component\Filesystem\Filesystem + */ + protected $filesystem = null; + + /** + * @var string + */ + protected $workspace = null; + + /** + * @var bool|null Flag for hard links on Windows + */ + private static $linkOnWindows = null; + + /** + * @var bool|null Flag for symbolic links on Windows + */ + private static $symlinkOnWindows = null; + + public static function setUpBeforeClass() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + self::$linkOnWindows = true; + $originFile = tempnam(sys_get_temp_dir(), 'li'); + $targetFile = tempnam(sys_get_temp_dir(), 'li'); + if (true !== @link($originFile, $targetFile)) { + $report = error_get_last(); + if (\is_array($report) && false !== strpos($report['message'], 'error code(1314)')) { + self::$linkOnWindows = false; + } + } else { + @unlink($targetFile); + } + + self::$symlinkOnWindows = true; + $originDir = tempnam(sys_get_temp_dir(), 'sl'); + $targetDir = tempnam(sys_get_temp_dir(), 'sl'); + if (true !== @symlink($originDir, $targetDir)) { + $report = error_get_last(); + if (\is_array($report) && false !== strpos($report['message'], 'error code(1314)')) { + self::$symlinkOnWindows = false; + } + } else { + @unlink($targetDir); + } + } + } + + protected function setUp() + { + $this->umask = umask(0); + $this->filesystem = new Filesystem(); + $this->workspace = sys_get_temp_dir().'/'.microtime(true).'.'.mt_rand(); + mkdir($this->workspace, 0777, true); + $this->workspace = realpath($this->workspace); + } + + protected function tearDown() + { + if (!empty($this->longPathNamesWindows)) { + foreach ($this->longPathNamesWindows as $path) { + exec('DEL '.$path); + } + $this->longPathNamesWindows = array(); + } + + $this->filesystem->remove($this->workspace); + umask($this->umask); + } + + /** + * @param int $expectedFilePerms Expected file permissions as three digits (i.e. 755) + * @param string $filePath + */ + protected function assertFilePermissions($expectedFilePerms, $filePath) + { + $actualFilePerms = (int) substr(sprintf('%o', fileperms($filePath)), -3); + $this->assertEquals( + $expectedFilePerms, + $actualFilePerms, + sprintf('File permissions for %s must be %s. Actual %s', $filePath, $expectedFilePerms, $actualFilePerms) + ); + } + + protected function getFileOwner($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getpwuid($infos['uid'])) { + return $datas['name']; + } + } + + protected function getFileGroup($filepath) + { + $this->markAsSkippedIfPosixIsMissing(); + + $infos = stat($filepath); + if ($datas = posix_getgrgid($infos['gid'])) { + return $datas['name']; + } + + $this->markTestSkipped('Unable to retrieve file group name'); + } + + protected function markAsSkippedIfLinkIsMissing() + { + if (!\function_exists('link')) { + $this->markTestSkipped('link is not supported'); + } + + if ('\\' === \DIRECTORY_SEPARATOR && false === self::$linkOnWindows) { + $this->markTestSkipped('link requires "Create hard links" privilege on windows'); + } + } + + protected function markAsSkippedIfSymlinkIsMissing($relative = false) + { + if ('\\' === \DIRECTORY_SEPARATOR && false === self::$symlinkOnWindows) { + $this->markTestSkipped('symlink requires "Create symbolic links" privilege on Windows'); + } + + // https://bugs.php.net/bug.php?id=69473 + if ($relative && '\\' === \DIRECTORY_SEPARATOR && 1 === PHP_ZTS) { + $this->markTestSkipped('symlink does not support relative paths on thread safe Windows PHP versions'); + } + } + + protected function markAsSkippedIfChmodIsMissing() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('chmod is not supported on Windows'); + } + } + + protected function markAsSkippedIfPosixIsMissing() + { + if (!\function_exists('posix_isatty')) { + $this->markTestSkipped('Function posix_isatty is required.'); + } + } +} diff --git a/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php b/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.php new file mode 100644 index 0000000..f14420f --- /dev/null +++ b/vendor/symfony/filesystem/Tests/Fixtures/MockStream/MockStream.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\Filesystem\Tests\Fixtures\MockStream; + +/** + * Mock stream class to be used with stream_wrapper_register. + * stream_wrapper_register('mock', 'Symfony\Component\Filesystem\Tests\Fixtures\MockStream\MockStream'). + */ +class MockStream +{ + /** + * Opens file or URL. + * + * @param string $path Specifies the URL that was passed to the original function + * @param string $mode The mode used to open the file, as detailed for fopen() + * @param int $options Holds additional flags set by the streams API + * @param string $opened_path If the path is opened successfully, and STREAM_USE_PATH is set in options, + * opened_path should be set to the full path of the file/resource that was actually opened + * + * @return bool + */ + public function stream_open($path, $mode, $options, &$opened_path) + { + return true; + } + + /** + * @param string $path The file path or URL to stat + * @param array $flags Holds additional flags set by the streams API + * + * @return array File stats + */ + public function url_stat($path, $flags) + { + return array(); + } +} diff --git a/vendor/symfony/filesystem/composer.json b/vendor/symfony/filesystem/composer.json new file mode 100644 index 0000000..77f62b7 --- /dev/null +++ b/vendor/symfony/filesystem/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/filesystem", + "type": "library", + "description": "Symfony Filesystem 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": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Filesystem\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/filesystem/phpunit.xml.dist b/vendor/symfony/filesystem/phpunit.xml.dist new file mode 100644 index 0000000..7bba6fc --- /dev/null +++ b/vendor/symfony/filesystem/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/finder/.gitignore b/vendor/symfony/finder/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/finder/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/finder/CHANGELOG.md b/vendor/symfony/finder/CHANGELOG.md new file mode 100644 index 0000000..c795e54 --- /dev/null +++ b/vendor/symfony/finder/CHANGELOG.md @@ -0,0 +1,61 @@ +CHANGELOG +========= + +4.0.0 +----- + + * removed `ExceptionInterface` + * removed `Symfony\Component\Finder\Iterator\FilterIterator` + +3.4.0 +----- + + * deprecated `Symfony\Component\Finder\Iterator\FilterIterator` + * added Finder::hasResults() method to check if any results were found + +3.3.0 +----- + + * added double-star matching to Glob::toRegex() + +3.0.0 +----- + + * removed deprecated classes + +2.8.0 +----- + + * deprecated adapters and related classes + +2.5.0 +----- + * added support for GLOB_BRACE in the paths passed to Finder::in() + +2.3.0 +----- + + * added a way to ignore unreadable directories (via Finder::ignoreUnreadableDirs()) + * unified the way subfolders that are not executable are handled by always throwing an AccessDeniedException exception + +2.2.0 +----- + + * added Finder::path() and Finder::notPath() methods + * added finder adapters to improve performance on specific platforms + * added support for wildcard characters (glob patterns) in the paths passed + to Finder::in() + +2.1.0 +----- + + * added Finder::sortByAccessedTime(), Finder::sortByChangedTime(), and + Finder::sortByModifiedTime() + * added Countable to Finder + * added support for an array of directories as an argument to + Finder::exclude() + * added searching based on the file content via Finder::contains() and + Finder::notContains() + * added support for the != operator in the Comparator + * [BC BREAK] filter expressions (used for file name and content) are no more + considered as regexps but glob patterns when they are enclosed in '*' or '?' diff --git a/vendor/symfony/finder/Comparator/Comparator.php b/vendor/symfony/finder/Comparator/Comparator.php new file mode 100644 index 0000000..ea37566 --- /dev/null +++ b/vendor/symfony/finder/Comparator/Comparator.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\Finder\Comparator; + +/** + * Comparator. + * + * @author Fabien Potencier + */ +class Comparator +{ + private $target; + private $operator = '=='; + + /** + * Gets the target value. + * + * @return string The target value + */ + public function getTarget() + { + return $this->target; + } + + /** + * Sets the target value. + * + * @param string $target The target value + */ + public function setTarget($target) + { + $this->target = $target; + } + + /** + * Gets the comparison operator. + * + * @return string The operator + */ + public function getOperator() + { + return $this->operator; + } + + /** + * Sets the comparison operator. + * + * @param string $operator A valid operator + * + * @throws \InvalidArgumentException + */ + public function setOperator($operator) + { + if (!$operator) { + $operator = '=='; + } + + if (!\in_array($operator, array('>', '<', '>=', '<=', '==', '!='))) { + throw new \InvalidArgumentException(sprintf('Invalid operator "%s".', $operator)); + } + + $this->operator = $operator; + } + + /** + * Tests against the target. + * + * @param mixed $test A test value + * + * @return bool + */ + public function test($test) + { + switch ($this->operator) { + case '>': + return $test > $this->target; + case '>=': + return $test >= $this->target; + case '<': + return $test < $this->target; + case '<=': + return $test <= $this->target; + case '!=': + return $test != $this->target; + } + + return $test == $this->target; + } +} diff --git a/vendor/symfony/finder/Comparator/DateComparator.php b/vendor/symfony/finder/Comparator/DateComparator.php new file mode 100644 index 0000000..d17c77a --- /dev/null +++ b/vendor/symfony/finder/Comparator/DateComparator.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\Finder\Comparator; + +/** + * DateCompare compiles date comparisons. + * + * @author Fabien Potencier + */ +class DateComparator extends Comparator +{ + /** + * @param string $test A comparison string + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct(string $test) + { + if (!preg_match('#^\s*(==|!=|[<>]=?|after|since|before|until)?\s*(.+?)\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a date test.', $test)); + } + + try { + $date = new \DateTime($matches[2]); + $target = $date->format('U'); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('"%s" is not a valid date.', $matches[2])); + } + + $operator = isset($matches[1]) ? $matches[1] : '=='; + if ('since' === $operator || 'after' === $operator) { + $operator = '>'; + } + + if ('until' === $operator || 'before' === $operator) { + $operator = '<'; + } + + $this->setOperator($operator); + $this->setTarget($target); + } +} diff --git a/vendor/symfony/finder/Comparator/NumberComparator.php b/vendor/symfony/finder/Comparator/NumberComparator.php new file mode 100644 index 0000000..80667c9 --- /dev/null +++ b/vendor/symfony/finder/Comparator/NumberComparator.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\Finder\Comparator; + +/** + * NumberComparator compiles a simple comparison to an anonymous + * subroutine, which you can call with a value to be tested again. + * + * Now this would be very pointless, if NumberCompare didn't understand + * magnitudes. + * + * The target value may use magnitudes of kilobytes (k, ki), + * megabytes (m, mi), or gigabytes (g, gi). Those suffixed + * with an i use the appropriate 2**n version in accordance with the + * IEC standard: http://physics.nist.gov/cuu/Units/binary.html + * + * Based on the Perl Number::Compare module. + * + * @author Fabien Potencier PHP port + * @author Richard Clamp Perl version + * @copyright 2004-2005 Fabien Potencier + * @copyright 2002 Richard Clamp + * + * @see http://physics.nist.gov/cuu/Units/binary.html + */ +class NumberComparator extends Comparator +{ + /** + * @param string|int $test A comparison string or an integer + * + * @throws \InvalidArgumentException If the test is not understood + */ + public function __construct(?string $test) + { + if (!preg_match('#^\s*(==|!=|[<>]=?)?\s*([0-9\.]+)\s*([kmg]i?)?\s*$#i', $test, $matches)) { + throw new \InvalidArgumentException(sprintf('Don\'t understand "%s" as a number test.', $test)); + } + + $target = $matches[2]; + if (!is_numeric($target)) { + throw new \InvalidArgumentException(sprintf('Invalid number "%s".', $target)); + } + if (isset($matches[3])) { + // magnitude + switch (strtolower($matches[3])) { + case 'k': + $target *= 1000; + break; + case 'ki': + $target *= 1024; + break; + case 'm': + $target *= 1000000; + break; + case 'mi': + $target *= 1024 * 1024; + break; + case 'g': + $target *= 1000000000; + break; + case 'gi': + $target *= 1024 * 1024 * 1024; + break; + } + } + + $this->setTarget($target); + $this->setOperator(isset($matches[1]) ? $matches[1] : '=='); + } +} diff --git a/vendor/symfony/finder/Exception/AccessDeniedException.php b/vendor/symfony/finder/Exception/AccessDeniedException.php new file mode 100644 index 0000000..ee195ea --- /dev/null +++ b/vendor/symfony/finder/Exception/AccessDeniedException.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\Finder\Exception; + +/** + * @author Jean-François Simon + */ +class AccessDeniedException extends \UnexpectedValueException +{ +} diff --git a/vendor/symfony/finder/Finder.php b/vendor/symfony/finder/Finder.php new file mode 100644 index 0000000..e8026ed --- /dev/null +++ b/vendor/symfony/finder/Finder.php @@ -0,0 +1,746 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +use Symfony\Component\Finder\Comparator\DateComparator; +use Symfony\Component\Finder\Comparator\NumberComparator; +use Symfony\Component\Finder\Iterator\CustomFilterIterator; +use Symfony\Component\Finder\Iterator\DateRangeFilterIterator; +use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator; +use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator; +use Symfony\Component\Finder\Iterator\FilecontentFilterIterator; +use Symfony\Component\Finder\Iterator\FilenameFilterIterator; +use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator; +use Symfony\Component\Finder\Iterator\SortableIterator; + +/** + * Finder allows to build rules to find files and directories. + * + * It is a thin wrapper around several specialized iterator classes. + * + * All rules may be invoked several times. + * + * All methods return the current Finder object to allow easy chaining: + * + * $finder = Finder::create()->files()->name('*.php')->in(__DIR__); + * + * @author Fabien Potencier + */ +class Finder implements \IteratorAggregate, \Countable +{ + const IGNORE_VCS_FILES = 1; + const IGNORE_DOT_FILES = 2; + + private $mode = 0; + private $names = array(); + private $notNames = array(); + private $exclude = array(); + private $filters = array(); + private $depths = array(); + private $sizes = array(); + private $followLinks = false; + private $sort = false; + private $ignore = 0; + private $dirs = array(); + private $dates = array(); + private $iterators = array(); + private $contains = array(); + private $notContains = array(); + private $paths = array(); + private $notPaths = array(); + private $ignoreUnreadableDirs = false; + + private static $vcsPatterns = array('.svn', '_svn', 'CVS', '_darcs', '.arch-params', '.monotone', '.bzr', '.git', '.hg'); + + public function __construct() + { + $this->ignore = static::IGNORE_VCS_FILES | static::IGNORE_DOT_FILES; + } + + /** + * Creates a new Finder. + * + * @return static + */ + public static function create() + { + return new static(); + } + + /** + * Restricts the matching to directories only. + * + * @return $this + */ + public function directories() + { + $this->mode = Iterator\FileTypeFilterIterator::ONLY_DIRECTORIES; + + return $this; + } + + /** + * Restricts the matching to files only. + * + * @return $this + */ + public function files() + { + $this->mode = Iterator\FileTypeFilterIterator::ONLY_FILES; + + return $this; + } + + /** + * Adds tests for the directory depth. + * + * Usage: + * + * $finder->depth('> 1') // the Finder will start matching at level 1. + * $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point. + * + * @param string|int $level The depth level expression + * + * @return $this + * + * @see DepthRangeFilterIterator + * @see NumberComparator + */ + public function depth($level) + { + $this->depths[] = new Comparator\NumberComparator($level); + + return $this; + } + + /** + * Adds tests for file dates (last modified). + * + * The date must be something that strtotime() is able to parse: + * + * $finder->date('since yesterday'); + * $finder->date('until 2 days ago'); + * $finder->date('> now - 2 hours'); + * $finder->date('>= 2005-10-15'); + * + * @param string $date A date range string + * + * @return $this + * + * @see strtotime + * @see DateRangeFilterIterator + * @see DateComparator + */ + public function date($date) + { + $this->dates[] = new Comparator\DateComparator($date); + + return $this; + } + + /** + * Adds rules that files must match. + * + * You can use patterns (delimited with / sign), globs or simple strings. + * + * $finder->name('*.php') + * $finder->name('/\.php$/') // same as above + * $finder->name('test.php') + * + * @param string $pattern A pattern (a regexp, a glob, or a string) + * + * @return $this + * + * @see FilenameFilterIterator + */ + public function name($pattern) + { + $this->names[] = $pattern; + + return $this; + } + + /** + * Adds rules that files must not match. + * + * @param string $pattern A pattern (a regexp, a glob, or a string) + * + * @return $this + * + * @see FilenameFilterIterator + */ + public function notName($pattern) + { + $this->notNames[] = $pattern; + + return $this; + } + + /** + * Adds tests that file contents must match. + * + * Strings or PCRE patterns can be used: + * + * $finder->contains('Lorem ipsum') + * $finder->contains('/Lorem ipsum/i') + * + * @param string $pattern A pattern (string or regexp) + * + * @return $this + * + * @see FilecontentFilterIterator + */ + public function contains($pattern) + { + $this->contains[] = $pattern; + + return $this; + } + + /** + * Adds tests that file contents must not match. + * + * Strings or PCRE patterns can be used: + * + * $finder->notContains('Lorem ipsum') + * $finder->notContains('/Lorem ipsum/i') + * + * @param string $pattern A pattern (string or regexp) + * + * @return $this + * + * @see FilecontentFilterIterator + */ + public function notContains($pattern) + { + $this->notContains[] = $pattern; + + return $this; + } + + /** + * Adds rules that filenames must match. + * + * You can use patterns (delimited with / sign) or simple strings. + * + * $finder->path('some/special/dir') + * $finder->path('/some\/special\/dir/') // same as above + * + * Use only / as dirname separator. + * + * @param string $pattern A pattern (a regexp or a string) + * + * @return $this + * + * @see FilenameFilterIterator + */ + public function path($pattern) + { + $this->paths[] = $pattern; + + return $this; + } + + /** + * Adds rules that filenames must not match. + * + * You can use patterns (delimited with / sign) or simple strings. + * + * $finder->notPath('some/special/dir') + * $finder->notPath('/some\/special\/dir/') // same as above + * + * Use only / as dirname separator. + * + * @param string $pattern A pattern (a regexp or a string) + * + * @return $this + * + * @see FilenameFilterIterator + */ + public function notPath($pattern) + { + $this->notPaths[] = $pattern; + + return $this; + } + + /** + * Adds tests for file sizes. + * + * $finder->size('> 10K'); + * $finder->size('<= 1Ki'); + * $finder->size(4); + * + * @param string|int $size A size range string or an integer + * + * @return $this + * + * @see SizeRangeFilterIterator + * @see NumberComparator + */ + public function size($size) + { + $this->sizes[] = new Comparator\NumberComparator($size); + + return $this; + } + + /** + * Excludes directories. + * + * Directories passed as argument must be relative to the ones defined with the `in()` method. For example: + * + * $finder->in(__DIR__)->exclude('ruby'); + * + * @param string|array $dirs A directory path or an array of directories + * + * @return $this + * + * @see ExcludeDirectoryFilterIterator + */ + public function exclude($dirs) + { + $this->exclude = array_merge($this->exclude, (array) $dirs); + + return $this; + } + + /** + * Excludes "hidden" directories and files (starting with a dot). + * + * This option is enabled by default. + * + * @param bool $ignoreDotFiles Whether to exclude "hidden" files or not + * + * @return $this + * + * @see ExcludeDirectoryFilterIterator + */ + public function ignoreDotFiles($ignoreDotFiles) + { + if ($ignoreDotFiles) { + $this->ignore |= static::IGNORE_DOT_FILES; + } else { + $this->ignore &= ~static::IGNORE_DOT_FILES; + } + + return $this; + } + + /** + * Forces the finder to ignore version control directories. + * + * This option is enabled by default. + * + * @param bool $ignoreVCS Whether to exclude VCS files or not + * + * @return $this + * + * @see ExcludeDirectoryFilterIterator + */ + public function ignoreVCS($ignoreVCS) + { + if ($ignoreVCS) { + $this->ignore |= static::IGNORE_VCS_FILES; + } else { + $this->ignore &= ~static::IGNORE_VCS_FILES; + } + + return $this; + } + + /** + * Adds VCS patterns. + * + * @see ignoreVCS() + * + * @param string|string[] $pattern VCS patterns to ignore + */ + public static function addVCSPattern($pattern) + { + foreach ((array) $pattern as $p) { + self::$vcsPatterns[] = $p; + } + + self::$vcsPatterns = array_unique(self::$vcsPatterns); + } + + /** + * Sorts files and directories by an anonymous function. + * + * The anonymous function receives two \SplFileInfo instances to compare. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return $this + * + * @see SortableIterator + */ + public function sort(\Closure $closure) + { + $this->sort = $closure; + + return $this; + } + + /** + * Sorts files and directories by name. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return $this + * + * @see SortableIterator + */ + public function sortByName() + { + $this->sort = Iterator\SortableIterator::SORT_BY_NAME; + + return $this; + } + + /** + * Sorts files and directories by type (directories before files), then by name. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return $this + * + * @see SortableIterator + */ + public function sortByType() + { + $this->sort = Iterator\SortableIterator::SORT_BY_TYPE; + + return $this; + } + + /** + * Sorts files and directories by the last accessed time. + * + * This is the time that the file was last accessed, read or written to. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return $this + * + * @see SortableIterator + */ + public function sortByAccessedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_ACCESSED_TIME; + + return $this; + } + + /** + * Sorts files and directories by the last inode changed time. + * + * This is the time that the inode information was last modified (permissions, owner, group or other metadata). + * + * On Windows, since inode is not available, changed time is actually the file creation time. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return $this + * + * @see SortableIterator + */ + public function sortByChangedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_CHANGED_TIME; + + return $this; + } + + /** + * Sorts files and directories by the last modified time. + * + * This is the last time the actual contents of the file were last modified. + * + * This can be slow as all the matching files and directories must be retrieved for comparison. + * + * @return $this + * + * @see SortableIterator + */ + public function sortByModifiedTime() + { + $this->sort = Iterator\SortableIterator::SORT_BY_MODIFIED_TIME; + + return $this; + } + + /** + * Filters the iterator with an anonymous function. + * + * The anonymous function receives a \SplFileInfo and must return false + * to remove files. + * + * @return $this + * + * @see CustomFilterIterator + */ + public function filter(\Closure $closure) + { + $this->filters[] = $closure; + + return $this; + } + + /** + * Forces the following of symlinks. + * + * @return $this + */ + public function followLinks() + { + $this->followLinks = true; + + return $this; + } + + /** + * Tells finder to ignore unreadable directories. + * + * By default, scanning unreadable directories content throws an AccessDeniedException. + * + * @param bool $ignore + * + * @return $this + */ + public function ignoreUnreadableDirs($ignore = true) + { + $this->ignoreUnreadableDirs = (bool) $ignore; + + return $this; + } + + /** + * Searches files and directories which match defined rules. + * + * @param string|array $dirs A directory path or an array of directories + * + * @return $this + * + * @throws \InvalidArgumentException if one of the directories does not exist + */ + public function in($dirs) + { + $resolvedDirs = array(); + + foreach ((array) $dirs as $dir) { + if (is_dir($dir)) { + $resolvedDirs[] = $this->normalizeDir($dir); + } elseif ($glob = glob($dir, (\defined('GLOB_BRACE') ? GLOB_BRACE : 0) | GLOB_ONLYDIR)) { + $resolvedDirs = array_merge($resolvedDirs, array_map(array($this, 'normalizeDir'), $glob)); + } else { + throw new \InvalidArgumentException(sprintf('The "%s" directory does not exist.', $dir)); + } + } + + $this->dirs = array_merge($this->dirs, $resolvedDirs); + + return $this; + } + + /** + * Returns an Iterator for the current Finder configuration. + * + * This method implements the IteratorAggregate interface. + * + * @return \Iterator|SplFileInfo[] An iterator + * + * @throws \LogicException if the in() method has not been called + */ + public function getIterator() + { + if (0 === \count($this->dirs) && 0 === \count($this->iterators)) { + throw new \LogicException('You must call one of in() or append() methods before iterating over a Finder.'); + } + + if (1 === \count($this->dirs) && 0 === \count($this->iterators)) { + return $this->searchInDirectory($this->dirs[0]); + } + + $iterator = new \AppendIterator(); + foreach ($this->dirs as $dir) { + $iterator->append($this->searchInDirectory($dir)); + } + + foreach ($this->iterators as $it) { + $iterator->append($it); + } + + return $iterator; + } + + /** + * Appends an existing set of files/directories to the finder. + * + * The set can be another Finder, an Iterator, an IteratorAggregate, or even a plain array. + * + * @param iterable $iterator + * + * @return $this + * + * @throws \InvalidArgumentException when the given argument is not iterable + */ + public function append($iterator) + { + if ($iterator instanceof \IteratorAggregate) { + $this->iterators[] = $iterator->getIterator(); + } elseif ($iterator instanceof \Iterator) { + $this->iterators[] = $iterator; + } elseif ($iterator instanceof \Traversable || \is_array($iterator)) { + $it = new \ArrayIterator(); + foreach ($iterator as $file) { + $it->append($file instanceof \SplFileInfo ? $file : new \SplFileInfo($file)); + } + $this->iterators[] = $it; + } else { + throw new \InvalidArgumentException('Finder::append() method wrong argument type.'); + } + + return $this; + } + + /** + * Check if the any results were found. + * + * @return bool + */ + public function hasResults() + { + foreach ($this->getIterator() as $_) { + return true; + } + + return false; + } + + /** + * Counts all the results collected by the iterators. + * + * @return int + */ + public function count() + { + return iterator_count($this->getIterator()); + } + + private function searchInDirectory(string $dir): \Iterator + { + if (static::IGNORE_VCS_FILES === (static::IGNORE_VCS_FILES & $this->ignore)) { + $this->exclude = array_merge($this->exclude, self::$vcsPatterns); + } + + if (static::IGNORE_DOT_FILES === (static::IGNORE_DOT_FILES & $this->ignore)) { + $this->notPaths[] = '#(^|/)\..+(/|$)#'; + } + + $minDepth = 0; + $maxDepth = PHP_INT_MAX; + + foreach ($this->depths as $comparator) { + switch ($comparator->getOperator()) { + case '>': + $minDepth = $comparator->getTarget() + 1; + break; + case '>=': + $minDepth = $comparator->getTarget(); + break; + case '<': + $maxDepth = $comparator->getTarget() - 1; + break; + case '<=': + $maxDepth = $comparator->getTarget(); + break; + default: + $minDepth = $maxDepth = $comparator->getTarget(); + } + } + + $flags = \RecursiveDirectoryIterator::SKIP_DOTS; + + if ($this->followLinks) { + $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + } + + $iterator = new Iterator\RecursiveDirectoryIterator($dir, $flags, $this->ignoreUnreadableDirs); + + if ($this->exclude) { + $iterator = new Iterator\ExcludeDirectoryFilterIterator($iterator, $this->exclude); + } + + $iterator = new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST); + + if ($minDepth > 0 || $maxDepth < PHP_INT_MAX) { + $iterator = new Iterator\DepthRangeFilterIterator($iterator, $minDepth, $maxDepth); + } + + if ($this->mode) { + $iterator = new Iterator\FileTypeFilterIterator($iterator, $this->mode); + } + + if ($this->names || $this->notNames) { + $iterator = new Iterator\FilenameFilterIterator($iterator, $this->names, $this->notNames); + } + + if ($this->contains || $this->notContains) { + $iterator = new Iterator\FilecontentFilterIterator($iterator, $this->contains, $this->notContains); + } + + if ($this->sizes) { + $iterator = new Iterator\SizeRangeFilterIterator($iterator, $this->sizes); + } + + if ($this->dates) { + $iterator = new Iterator\DateRangeFilterIterator($iterator, $this->dates); + } + + if ($this->filters) { + $iterator = new Iterator\CustomFilterIterator($iterator, $this->filters); + } + + if ($this->paths || $this->notPaths) { + $iterator = new Iterator\PathFilterIterator($iterator, $this->paths, $this->notPaths); + } + + if ($this->sort) { + $iteratorAggregate = new Iterator\SortableIterator($iterator, $this->sort); + $iterator = $iteratorAggregate->getIterator(); + } + + return $iterator; + } + + /** + * Normalizes given directory names by removing trailing slashes. + * + * Excluding: (s)ftp:// wrapper + * + * @param string $dir + * + * @return string + */ + private function normalizeDir($dir) + { + $dir = rtrim($dir, '/'.\DIRECTORY_SEPARATOR); + + if (preg_match('#^s?ftp://#', $dir)) { + $dir .= '/'; + } + + return $dir; + } +} diff --git a/vendor/symfony/finder/Glob.php b/vendor/symfony/finder/Glob.php new file mode 100644 index 0000000..27d9ce3 --- /dev/null +++ b/vendor/symfony/finder/Glob.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +/** + * Glob matches globbing patterns against text. + * + * if match_glob("foo.*", "foo.bar") echo "matched\n"; + * + * // prints foo.bar and foo.baz + * $regex = glob_to_regex("foo.*"); + * for (array('foo.bar', 'foo.baz', 'foo', 'bar') as $t) + * { + * if (/$regex/) echo "matched: $car\n"; + * } + * + * Glob implements glob(3) style matching that can be used to match + * against text, rather than fetching names from a filesystem. + * + * Based on the Perl Text::Glob module. + * + * @author Fabien Potencier PHP port + * @author Richard Clamp Perl version + * @copyright 2004-2005 Fabien Potencier + * @copyright 2002 Richard Clamp + */ +class Glob +{ + /** + * Returns a regexp which is the equivalent of the glob pattern. + * + * @param string $glob The glob pattern + * @param bool $strictLeadingDot + * @param bool $strictWildcardSlash + * @param string $delimiter Optional delimiter + * + * @return string regex The regexp + */ + public static function toRegex($glob, $strictLeadingDot = true, $strictWildcardSlash = true, $delimiter = '#') + { + $firstByte = true; + $escaping = false; + $inCurlies = 0; + $regex = ''; + $sizeGlob = \strlen($glob); + for ($i = 0; $i < $sizeGlob; ++$i) { + $car = $glob[$i]; + if ($firstByte && $strictLeadingDot && '.' !== $car) { + $regex .= '(?=[^\.])'; + } + + $firstByte = '/' === $car; + + if ($firstByte && $strictWildcardSlash && isset($glob[$i + 2]) && '**' === $glob[$i + 1].$glob[$i + 2] && (!isset($glob[$i + 3]) || '/' === $glob[$i + 3])) { + $car = '[^/]++/'; + if (!isset($glob[$i + 3])) { + $car .= '?'; + } + + if ($strictLeadingDot) { + $car = '(?=[^\.])'.$car; + } + + $car = '/(?:'.$car.')*'; + $i += 2 + isset($glob[$i + 3]); + + if ('/' === $delimiter) { + $car = str_replace('/', '\\/', $car); + } + } + + if ($delimiter === $car || '.' === $car || '(' === $car || ')' === $car || '|' === $car || '+' === $car || '^' === $car || '$' === $car) { + $regex .= "\\$car"; + } elseif ('*' === $car) { + $regex .= $escaping ? '\\*' : ($strictWildcardSlash ? '[^/]*' : '.*'); + } elseif ('?' === $car) { + $regex .= $escaping ? '\\?' : ($strictWildcardSlash ? '[^/]' : '.'); + } elseif ('{' === $car) { + $regex .= $escaping ? '\\{' : '('; + if (!$escaping) { + ++$inCurlies; + } + } elseif ('}' === $car && $inCurlies) { + $regex .= $escaping ? '}' : ')'; + if (!$escaping) { + --$inCurlies; + } + } elseif (',' === $car && $inCurlies) { + $regex .= $escaping ? ',' : '|'; + } elseif ('\\' === $car) { + if ($escaping) { + $regex .= '\\\\'; + $escaping = false; + } else { + $escaping = true; + } + + continue; + } else { + $regex .= $car; + } + $escaping = false; + } + + return $delimiter.'^'.$regex.'$'.$delimiter; + } +} diff --git a/vendor/symfony/finder/Iterator/CustomFilterIterator.php b/vendor/symfony/finder/Iterator/CustomFilterIterator.php new file mode 100644 index 0000000..77bd3ac --- /dev/null +++ b/vendor/symfony/finder/Iterator/CustomFilterIterator.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\Finder\Iterator; + +/** + * CustomFilterIterator filters files by applying anonymous functions. + * + * The anonymous function receives a \SplFileInfo and must return false + * to remove files. + * + * @author Fabien Potencier + */ +class CustomFilterIterator extends \FilterIterator +{ + private $filters = array(); + + /** + * @param \Iterator $iterator The Iterator to filter + * @param callable[] $filters An array of PHP callbacks + * + * @throws \InvalidArgumentException + */ + public function __construct(\Iterator $iterator, array $filters) + { + foreach ($filters as $filter) { + if (!\is_callable($filter)) { + throw new \InvalidArgumentException('Invalid PHP callback.'); + } + } + $this->filters = $filters; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + + foreach ($this->filters as $filter) { + if (false === \call_user_func($filter, $fileinfo)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php b/vendor/symfony/finder/Iterator/DateRangeFilterIterator.php new file mode 100644 index 0000000..c08e0a1 --- /dev/null +++ b/vendor/symfony/finder/Iterator/DateRangeFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Comparator\DateComparator; + +/** + * DateRangeFilterIterator filters out files that are not in the given date range (last modified dates). + * + * @author Fabien Potencier + */ +class DateRangeFilterIterator extends \FilterIterator +{ + private $comparators = array(); + + /** + * @param \Iterator $iterator The Iterator to filter + * @param DateComparator[] $comparators An array of DateComparator instances + */ + public function __construct(\Iterator $iterator, array $comparators) + { + $this->comparators = $comparators; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + + if (!file_exists($fileinfo->getPathname())) { + return false; + } + + $filedate = $fileinfo->getMTime(); + foreach ($this->comparators as $compare) { + if (!$compare->test($filedate)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php b/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.php new file mode 100644 index 0000000..436a66d --- /dev/null +++ b/vendor/symfony/finder/Iterator/DepthRangeFilterIterator.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\Finder\Iterator; + +/** + * DepthRangeFilterIterator limits the directory depth. + * + * @author Fabien Potencier + */ +class DepthRangeFilterIterator extends \FilterIterator +{ + private $minDepth = 0; + + /** + * @param \RecursiveIteratorIterator $iterator The Iterator to filter + * @param int $minDepth The min depth + * @param int $maxDepth The max depth + */ + public function __construct(\RecursiveIteratorIterator $iterator, int $minDepth = 0, int $maxDepth = PHP_INT_MAX) + { + $this->minDepth = $minDepth; + $iterator->setMaxDepth(PHP_INT_MAX === $maxDepth ? -1 : $maxDepth); + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + return $this->getInnerIterator()->getDepth() >= $this->minDepth; + } +} diff --git a/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php b/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.php new file mode 100644 index 0000000..d2d41f4 --- /dev/null +++ b/vendor/symfony/finder/Iterator/ExcludeDirectoryFilterIterator.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\Finder\Iterator; + +/** + * ExcludeDirectoryFilterIterator filters out directories. + * + * @author Fabien Potencier + */ +class ExcludeDirectoryFilterIterator extends \FilterIterator implements \RecursiveIterator +{ + private $iterator; + private $isRecursive; + private $excludedDirs = array(); + private $excludedPattern; + + /** + * @param \Iterator $iterator The Iterator to filter + * @param array $directories An array of directories to exclude + */ + public function __construct(\Iterator $iterator, array $directories) + { + $this->iterator = $iterator; + $this->isRecursive = $iterator instanceof \RecursiveIterator; + $patterns = array(); + foreach ($directories as $directory) { + $directory = rtrim($directory, '/'); + if (!$this->isRecursive || false !== strpos($directory, '/')) { + $patterns[] = preg_quote($directory, '#'); + } else { + $this->excludedDirs[$directory] = true; + } + } + if ($patterns) { + $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#'; + } + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool True if the value should be kept, false otherwise + */ + public function accept() + { + if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) { + return false; + } + + if ($this->excludedPattern) { + $path = $this->isDir() ? $this->current()->getRelativePathname() : $this->current()->getRelativePath(); + $path = str_replace('\\', '/', $path); + + return !preg_match($this->excludedPattern, $path); + } + + return true; + } + + public function hasChildren() + { + return $this->isRecursive && $this->iterator->hasChildren(); + } + + public function getChildren() + { + $children = new self($this->iterator->getChildren(), array()); + $children->excludedDirs = $this->excludedDirs; + $children->excludedPattern = $this->excludedPattern; + + return $children; + } +} diff --git a/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php b/vendor/symfony/finder/Iterator/FileTypeFilterIterator.php new file mode 100644 index 0000000..a4c4eec --- /dev/null +++ b/vendor/symfony/finder/Iterator/FileTypeFilterIterator.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\Finder\Iterator; + +/** + * FileTypeFilterIterator only keeps files, directories, or both. + * + * @author Fabien Potencier + */ +class FileTypeFilterIterator extends \FilterIterator +{ + const ONLY_FILES = 1; + const ONLY_DIRECTORIES = 2; + + private $mode; + + /** + * @param \Iterator $iterator The Iterator to filter + * @param int $mode The mode (self::ONLY_FILES or self::ONLY_DIRECTORIES) + */ + public function __construct(\Iterator $iterator, int $mode) + { + $this->mode = $mode; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + if (self::ONLY_DIRECTORIES === (self::ONLY_DIRECTORIES & $this->mode) && $fileinfo->isFile()) { + return false; + } elseif (self::ONLY_FILES === (self::ONLY_FILES & $this->mode) && $fileinfo->isDir()) { + return false; + } + + return true; + } +} diff --git a/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php b/vendor/symfony/finder/Iterator/FilecontentFilterIterator.php new file mode 100644 index 0000000..81594b8 --- /dev/null +++ b/vendor/symfony/finder/Iterator/FilecontentFilterIterator.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\Finder\Iterator; + +/** + * FilecontentFilterIterator filters files by their contents using patterns (regexps or strings). + * + * @author Fabien Potencier + * @author Włodzimierz Gajda + */ +class FilecontentFilterIterator extends MultiplePcreFilterIterator +{ + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + if (!$this->matchRegexps && !$this->noMatchRegexps) { + return true; + } + + $fileinfo = $this->current(); + + if ($fileinfo->isDir() || !$fileinfo->isReadable()) { + return false; + } + + $content = $fileinfo->getContents(); + if (!$content) { + return false; + } + + return $this->isAccepted($content); + } + + /** + * Converts string to regexp if necessary. + * + * @param string $str Pattern: string or regexp + * + * @return string regexp corresponding to a given string or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; + } +} diff --git a/vendor/symfony/finder/Iterator/FilenameFilterIterator.php b/vendor/symfony/finder/Iterator/FilenameFilterIterator.php new file mode 100644 index 0000000..e168cd8 --- /dev/null +++ b/vendor/symfony/finder/Iterator/FilenameFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Glob; + +/** + * FilenameFilterIterator filters files by patterns (a regexp, a glob, or a string). + * + * @author Fabien Potencier + */ +class FilenameFilterIterator extends MultiplePcreFilterIterator +{ + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + return $this->isAccepted($this->current()->getFilename()); + } + + /** + * Converts glob to regexp. + * + * PCRE patterns are left unchanged. + * Glob strings are transformed with Glob::toRegex(). + * + * @param string $str Pattern: glob or regexp + * + * @return string regexp corresponding to a given glob or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : Glob::toRegex($str); + } +} diff --git a/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php b/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.php new file mode 100644 index 0000000..b94e1d4 --- /dev/null +++ b/vendor/symfony/finder/Iterator/MultiplePcreFilterIterator.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\Finder\Iterator; + +/** + * MultiplePcreFilterIterator filters files using patterns (regexps, globs or strings). + * + * @author Fabien Potencier + */ +abstract class MultiplePcreFilterIterator extends \FilterIterator +{ + protected $matchRegexps = array(); + protected $noMatchRegexps = array(); + + /** + * @param \Iterator $iterator The Iterator to filter + * @param array $matchPatterns An array of patterns that need to match + * @param array $noMatchPatterns An array of patterns that need to not match + */ + public function __construct(\Iterator $iterator, array $matchPatterns, array $noMatchPatterns) + { + foreach ($matchPatterns as $pattern) { + $this->matchRegexps[] = $this->toRegex($pattern); + } + + foreach ($noMatchPatterns as $pattern) { + $this->noMatchRegexps[] = $this->toRegex($pattern); + } + + parent::__construct($iterator); + } + + /** + * Checks whether the string is accepted by the regex filters. + * + * If there is no regexps defined in the class, this method will accept the string. + * Such case can be handled by child classes before calling the method if they want to + * apply a different behavior. + * + * @param string $string The string to be matched against filters + * + * @return bool + */ + protected function isAccepted($string) + { + // should at least not match one rule to exclude + foreach ($this->noMatchRegexps as $regex) { + if (preg_match($regex, $string)) { + return false; + } + } + + // should at least match one rule + if ($this->matchRegexps) { + foreach ($this->matchRegexps as $regex) { + if (preg_match($regex, $string)) { + return true; + } + } + + return false; + } + + // If there is no match rules, the file is accepted + return true; + } + + /** + * Checks whether the string is a regex. + * + * @param string $str + * + * @return bool Whether the given string is a regex + */ + protected function isRegex($str) + { + if (preg_match('/^(.{3,}?)[imsxuADU]*$/', $str, $m)) { + $start = substr($m[1], 0, 1); + $end = substr($m[1], -1); + + if ($start === $end) { + return !preg_match('/[*?[:alnum:] \\\\]/', $start); + } + + foreach (array(array('{', '}'), array('(', ')'), array('[', ']'), array('<', '>')) as $delimiters) { + if ($start === $delimiters[0] && $end === $delimiters[1]) { + return true; + } + } + } + + return false; + } + + /** + * Converts string into regexp. + * + * @param string $str Pattern + * + * @return string regexp corresponding to a given string + */ + abstract protected function toRegex($str); +} diff --git a/vendor/symfony/finder/Iterator/PathFilterIterator.php b/vendor/symfony/finder/Iterator/PathFilterIterator.php new file mode 100644 index 0000000..3fda557 --- /dev/null +++ b/vendor/symfony/finder/Iterator/PathFilterIterator.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\Finder\Iterator; + +/** + * PathFilterIterator filters files by path patterns (e.g. some/special/dir). + * + * @author Fabien Potencier + * @author Włodzimierz Gajda + */ +class PathFilterIterator extends MultiplePcreFilterIterator +{ + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $filename = $this->current()->getRelativePathname(); + + if ('\\' === \DIRECTORY_SEPARATOR) { + $filename = str_replace('\\', '/', $filename); + } + + return $this->isAccepted($filename); + } + + /** + * Converts strings to regexp. + * + * PCRE patterns are left unchanged. + * + * Default conversion: + * 'lorem/ipsum/dolor' ==> 'lorem\/ipsum\/dolor/' + * + * Use only / as directory separator (on Windows also). + * + * @param string $str Pattern: regexp or dirname + * + * @return string regexp corresponding to a given string or regexp + */ + protected function toRegex($str) + { + return $this->isRegex($str) ? $str : '/'.preg_quote($str, '/').'/'; + } +} diff --git a/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php b/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.php new file mode 100644 index 0000000..844ba46 --- /dev/null +++ b/vendor/symfony/finder/Iterator/RecursiveDirectoryIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Exception\AccessDeniedException; +use Symfony\Component\Finder\SplFileInfo; + +/** + * Extends the \RecursiveDirectoryIterator to support relative paths. + * + * @author Victor Berchet + */ +class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator +{ + /** + * @var bool + */ + private $ignoreUnreadableDirs; + + /** + * @var bool + */ + private $rewindable; + + // these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations + private $rootPath; + private $subPath; + private $directorySeparator = '/'; + + /** + * @throws \RuntimeException + */ + public function __construct(string $path, int $flags, bool $ignoreUnreadableDirs = false) + { + if ($flags & (self::CURRENT_AS_PATHNAME | self::CURRENT_AS_SELF)) { + throw new \RuntimeException('This iterator only support returning current as fileinfo.'); + } + + parent::__construct($path, $flags); + $this->ignoreUnreadableDirs = $ignoreUnreadableDirs; + $this->rootPath = $path; + if ('/' !== \DIRECTORY_SEPARATOR && !($flags & self::UNIX_PATHS)) { + $this->directorySeparator = \DIRECTORY_SEPARATOR; + } + } + + /** + * Return an instance of SplFileInfo with support for relative paths. + * + * @return SplFileInfo File information + */ + public function current() + { + // the logic here avoids redoing the same work in all iterations + + if (null === $subPathname = $this->subPath) { + $subPathname = $this->subPath = (string) $this->getSubPath(); + } + if ('' !== $subPathname) { + $subPathname .= $this->directorySeparator; + } + $subPathname .= $this->getFilename(); + + return new SplFileInfo($this->rootPath.$this->directorySeparator.$subPathname, $this->subPath, $subPathname); + } + + /** + * @return \RecursiveIterator + * + * @throws AccessDeniedException + */ + public function getChildren() + { + try { + $children = parent::getChildren(); + + if ($children instanceof self) { + // parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore + $children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs; + + // performance optimization to avoid redoing the same work in all children + $children->rewindable = &$this->rewindable; + $children->rootPath = $this->rootPath; + } + + return $children; + } catch (\UnexpectedValueException $e) { + if ($this->ignoreUnreadableDirs) { + // If directory is unreadable and finder is set to ignore it, a fake empty content is returned. + return new \RecursiveArrayIterator(array()); + } else { + throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e); + } + } + } + + /** + * Do nothing for non rewindable stream. + */ + public function rewind() + { + if (false === $this->isRewindable()) { + return; + } + + parent::rewind(); + } + + /** + * Checks if the stream is rewindable. + * + * @return bool true when the stream is rewindable, false otherwise + */ + public function isRewindable() + { + if (null !== $this->rewindable) { + return $this->rewindable; + } + + if (false !== $stream = @opendir($this->getPath())) { + $infos = stream_get_meta_data($stream); + closedir($stream); + + if ($infos['seekable']) { + return $this->rewindable = true; + } + } + + return $this->rewindable = false; + } +} diff --git a/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php b/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.php new file mode 100644 index 0000000..a68666b --- /dev/null +++ b/vendor/symfony/finder/Iterator/SizeRangeFilterIterator.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\Finder\Iterator; + +use Symfony\Component\Finder\Comparator\NumberComparator; + +/** + * SizeRangeFilterIterator filters out files that are not in the given size range. + * + * @author Fabien Potencier + */ +class SizeRangeFilterIterator extends \FilterIterator +{ + private $comparators = array(); + + /** + * @param \Iterator $iterator The Iterator to filter + * @param NumberComparator[] $comparators An array of NumberComparator instances + */ + public function __construct(\Iterator $iterator, array $comparators) + { + $this->comparators = $comparators; + + parent::__construct($iterator); + } + + /** + * Filters the iterator values. + * + * @return bool true if the value should be kept, false otherwise + */ + public function accept() + { + $fileinfo = $this->current(); + if (!$fileinfo->isFile()) { + return true; + } + + $filesize = $fileinfo->getSize(); + foreach ($this->comparators as $compare) { + if (!$compare->test($filesize)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/symfony/finder/Iterator/SortableIterator.php b/vendor/symfony/finder/Iterator/SortableIterator.php new file mode 100644 index 0000000..53f8e31 --- /dev/null +++ b/vendor/symfony/finder/Iterator/SortableIterator.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Iterator; + +/** + * SortableIterator applies a sort on a given Iterator. + * + * @author Fabien Potencier + */ +class SortableIterator implements \IteratorAggregate +{ + const SORT_BY_NAME = 1; + const SORT_BY_TYPE = 2; + const SORT_BY_ACCESSED_TIME = 3; + const SORT_BY_CHANGED_TIME = 4; + const SORT_BY_MODIFIED_TIME = 5; + + private $iterator; + private $sort; + + /** + * @param \Traversable $iterator The Iterator to filter + * @param int|callable $sort The sort type (SORT_BY_NAME, SORT_BY_TYPE, or a PHP callback) + * + * @throws \InvalidArgumentException + */ + public function __construct(\Traversable $iterator, $sort) + { + $this->iterator = $iterator; + + if (self::SORT_BY_NAME === $sort) { + $this->sort = function ($a, $b) { + return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname()); + }; + } elseif (self::SORT_BY_TYPE === $sort) { + $this->sort = function ($a, $b) { + if ($a->isDir() && $b->isFile()) { + return -1; + } elseif ($a->isFile() && $b->isDir()) { + return 1; + } + + return strcmp($a->getRealpath() ?: $a->getPathname(), $b->getRealpath() ?: $b->getPathname()); + }; + } elseif (self::SORT_BY_ACCESSED_TIME === $sort) { + $this->sort = function ($a, $b) { + return $a->getATime() - $b->getATime(); + }; + } elseif (self::SORT_BY_CHANGED_TIME === $sort) { + $this->sort = function ($a, $b) { + return $a->getCTime() - $b->getCTime(); + }; + } elseif (self::SORT_BY_MODIFIED_TIME === $sort) { + $this->sort = function ($a, $b) { + return $a->getMTime() - $b->getMTime(); + }; + } elseif (\is_callable($sort)) { + $this->sort = $sort; + } else { + throw new \InvalidArgumentException('The SortableIterator takes a PHP callable or a valid built-in sort algorithm as an argument.'); + } + } + + public function getIterator() + { + $array = iterator_to_array($this->iterator, true); + uasort($array, $this->sort); + + return new \ArrayIterator($array); + } +} diff --git a/vendor/symfony/finder/LICENSE b/vendor/symfony/finder/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/finder/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/vendor/symfony/finder/README.md b/vendor/symfony/finder/README.md new file mode 100644 index 0000000..0b19c75 --- /dev/null +++ b/vendor/symfony/finder/README.md @@ -0,0 +1,14 @@ +Finder Component +================ + +The Finder component finds files and directories via an intuitive fluent +interface. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/finder.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/finder/SplFileInfo.php b/vendor/symfony/finder/SplFileInfo.php new file mode 100644 index 0000000..b8143ed --- /dev/null +++ b/vendor/symfony/finder/SplFileInfo.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder; + +/** + * Extends \SplFileInfo to support relative paths. + * + * @author Fabien Potencier + */ +class SplFileInfo extends \SplFileInfo +{ + private $relativePath; + private $relativePathname; + + /** + * @param string $file The file name + * @param string $relativePath The relative path + * @param string $relativePathname The relative path name + */ + public function __construct(string $file, string $relativePath, string $relativePathname) + { + parent::__construct($file); + $this->relativePath = $relativePath; + $this->relativePathname = $relativePathname; + } + + /** + * Returns the relative path. + * + * This path does not contain the file name. + * + * @return string the relative path + */ + public function getRelativePath() + { + return $this->relativePath; + } + + /** + * Returns the relative path name. + * + * This path contains the file name. + * + * @return string the relative path name + */ + public function getRelativePathname() + { + return $this->relativePathname; + } + + /** + * Returns the contents of the file. + * + * @return string the contents of the file + * + * @throws \RuntimeException + */ + public function getContents() + { + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $content = file_get_contents($this->getPathname()); + restore_error_handler(); + if (false === $content) { + throw new \RuntimeException($error); + } + + return $content; + } +} diff --git a/vendor/symfony/finder/Tests/Comparator/ComparatorTest.php b/vendor/symfony/finder/Tests/Comparator/ComparatorTest.php new file mode 100644 index 0000000..656fc57 --- /dev/null +++ b/vendor/symfony/finder/Tests/Comparator/ComparatorTest.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\Finder\Tests\Comparator; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Finder\Comparator\Comparator; + +class ComparatorTest extends TestCase +{ + public function testGetSetOperator() + { + $comparator = new Comparator(); + try { + $comparator->setOperator('foo'); + $this->fail('->setOperator() throws an \InvalidArgumentException if the operator is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->setOperator() throws an \InvalidArgumentException if the operator is not valid.'); + } + + $comparator = new Comparator(); + $comparator->setOperator('>'); + $this->assertEquals('>', $comparator->getOperator(), '->getOperator() returns the current operator'); + } + + public function testGetSetTarget() + { + $comparator = new Comparator(); + $comparator->setTarget(8); + $this->assertEquals(8, $comparator->getTarget(), '->getTarget() returns the target'); + } + + /** + * @dataProvider getTestData + */ + public function testTest($operator, $target, $match, $noMatch) + { + $c = new Comparator(); + $c->setOperator($operator); + $c->setTarget($target); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('<', '1000', array('500', '999'), array('1000', '1500')), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php b/vendor/symfony/finder/Tests/Comparator/DateComparatorTest.php new file mode 100644 index 0000000..8a6c1dd --- /dev/null +++ b/vendor/symfony/finder/Tests/Comparator/DateComparatorTest.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\Finder\Tests\Comparator; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Finder\Comparator\DateComparator; + +class DateComparatorTest extends TestCase +{ + public function testConstructor() + { + try { + new DateComparator('foobar'); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + + try { + new DateComparator(''); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + } + + /** + * @dataProvider getTestData + */ + public function testTest($test, $match, $noMatch) + { + $c = new DateComparator($test); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('< 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('until 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('before 2005-10-10', array(strtotime('2005-10-09')), array(strtotime('2005-10-15'))), + array('> 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('after 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('since 2005-10-10', array(strtotime('2005-10-15')), array(strtotime('2005-10-09'))), + array('!= 2005-10-10', array(strtotime('2005-10-11')), array(strtotime('2005-10-10'))), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php b/vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.php new file mode 100644 index 0000000..30a75c7 --- /dev/null +++ b/vendor/symfony/finder/Tests/Comparator/NumberComparatorTest.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\Finder\Tests\Comparator; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Finder\Comparator\NumberComparator; + +class NumberComparatorTest extends TestCase +{ + /** + * @dataProvider getConstructorTestData + */ + public function testConstructor($successes, $failures) + { + foreach ($successes as $s) { + new NumberComparator($s); + } + + foreach ($failures as $f) { + try { + new NumberComparator($f); + $this->fail('__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException if the test expression is not valid.'); + } + } + } + + /** + * @dataProvider getTestData + */ + public function testTest($test, $match, $noMatch) + { + $c = new NumberComparator($test); + + foreach ($match as $m) { + $this->assertTrue($c->test($m), '->test() tests a string against the expression'); + } + + foreach ($noMatch as $m) { + $this->assertFalse($c->test($m), '->test() tests a string against the expression'); + } + } + + public function getTestData() + { + return array( + array('< 1000', array('500', '999'), array('1000', '1500')), + + array('< 1K', array('500', '999'), array('1000', '1500')), + array('<1k', array('500', '999'), array('1000', '1500')), + array(' < 1 K ', array('500', '999'), array('1000', '1500')), + array('<= 1K', array('1000'), array('1001')), + array('> 1K', array('1001'), array('1000')), + array('>= 1K', array('1000'), array('999')), + + array('< 1KI', array('500', '1023'), array('1024', '1500')), + array('<= 1KI', array('1024'), array('1025')), + array('> 1KI', array('1025'), array('1024')), + array('>= 1KI', array('1024'), array('1023')), + + array('1KI', array('1024'), array('1023', '1025')), + array('==1KI', array('1024'), array('1023', '1025')), + + array('==1m', array('1000000'), array('999999', '1000001')), + array('==1mi', array(1024 * 1024), array(1024 * 1024 - 1, 1024 * 1024 + 1)), + + array('==1g', array('1000000000'), array('999999999', '1000000001')), + array('==1gi', array(1024 * 1024 * 1024), array(1024 * 1024 * 1024 - 1, 1024 * 1024 * 1024 + 1)), + + array('!= 1000', array('500', '999'), array('1000')), + ); + } + + public function getConstructorTestData() + { + return array( + array( + array( + '1', '0', + '3.5', '33.55', '123.456', '123456.78', + '.1', '.123', + '.0', '0.0', + '1.', '0.', '123.', + '==1', '!=1', '<1', '>1', '<=1', '>=1', + '==1k', '==1ki', '==1m', '==1mi', '==1g', '==1gi', + '1k', '1ki', '1m', '1mi', '1g', '1gi', + ), + array( + false, null, '', + ' ', 'foobar', + '=1', '===1', + '0 . 1', '123 .45', '234. 567', + '..', '.0.', '0.1.2', + ), + ), + ); + } +} diff --git a/vendor/symfony/finder/Tests/FinderTest.php b/vendor/symfony/finder/Tests/FinderTest.php new file mode 100644 index 0000000..fbdcc36 --- /dev/null +++ b/vendor/symfony/finder/Tests/FinderTest.php @@ -0,0 +1,737 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Tests; + +use Symfony\Component\Finder\Finder; + +class FinderTest extends Iterator\RealIteratorTestCase +{ + public function testCreate() + { + $this->assertInstanceOf('Symfony\Component\Finder\Finder', Finder::create()); + } + + public function testDirectories() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->directories()); + $this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->directories(); + $finder->files(); + $finder->directories(); + $this->assertIterator($this->toAbsolute(array('foo', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testFiles() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->files()); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->files(); + $finder->directories(); + $finder->files(); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testRemoveTrailingSlash() + { + $finder = $this->buildFinder(); + + $expected = $this->toAbsolute(array('foo/bar.tmp', 'test.php', 'test.py', 'foo bar')); + $in = self::$tmpDir.'//'; + + $this->assertIterator($expected, $finder->in($in)->files()->getIterator()); + } + + public function testSymlinksNotResolved() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('symlinks are not supported on Windows'); + } + + $finder = $this->buildFinder(); + + symlink($this->toAbsolute('foo'), $this->toAbsolute('baz')); + $expected = $this->toAbsolute(array('baz/bar.tmp')); + $in = self::$tmpDir.'/baz/'; + try { + $this->assertIterator($expected, $finder->in($in)->files()->getIterator()); + unlink($this->toAbsolute('baz')); + } catch (\Exception $e) { + unlink($this->toAbsolute('baz')); + throw $e; + } + } + + public function testBackPathNotNormalized() + { + $finder = $this->buildFinder(); + + $expected = $this->toAbsolute(array('foo/../foo/bar.tmp')); + $in = self::$tmpDir.'/foo/../foo/'; + $this->assertIterator($expected, $finder->in($in)->files()->getIterator()); + } + + public function testDepth() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->depth('< 1')); + $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->depth('<= 0')); + $this->assertIterator($this->toAbsolute(array('foo', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->depth('>= 1')); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->depth('< 1')->depth('>= 1'); + $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testName() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->name('*.php')); + $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->name('test.ph*'); + $finder->name('test.py'); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->name('~^test~i'); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->name('~\\.php$~i'); + $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->name('test.p{hp,y}'); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testNotName() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->notName('*.php')); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->notName('*.php'); + $finder->notName('*.py'); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->name('test.ph*'); + $finder->name('test.py'); + $finder->notName('*.php'); + $finder->notName('*.py'); + $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->name('test.ph*'); + $finder->name('test.py'); + $finder->notName('*.p{hp,y}'); + $this->assertIterator(array(), $finder->in(self::$tmpDir)->getIterator()); + } + + /** + * @dataProvider getRegexNameTestData + */ + public function testRegexName($regex) + { + $finder = $this->buildFinder(); + $finder->name($regex); + $this->assertIterator($this->toAbsolute(array('test.py', 'test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSize() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->files()->size('< 1K')->size('> 500')); + $this->assertIterator($this->toAbsolute(array('test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testDate() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->files()->date('until last month')); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testExclude() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->exclude('foo')); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testIgnoreVCS() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->ignoreVCS(false)->ignoreDotFiles(false)); + $this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->ignoreVCS(false)->ignoreVCS(false)->ignoreDotFiles(false); + $this->assertIterator($this->toAbsolute(array('.git', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->ignoreVCS(true)->ignoreDotFiles(false)); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testIgnoreDotFiles() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->ignoreDotFiles(false)->ignoreVCS(false)); + $this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $finder->ignoreDotFiles(false)->ignoreDotFiles(false)->ignoreVCS(false); + $this->assertIterator($this->toAbsolute(array('.git', '.bar', '.foo', '.foo/.bar', '.foo/bar', 'foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'toto/.git', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->ignoreDotFiles(true)->ignoreVCS(false)); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSortByName() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->sortByName()); + $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSortByType() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->sortByType()); + $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'toto', 'foo/bar.tmp', 'test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSortByAccessedTime() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->sortByAccessedTime()); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSortByChangedTime() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->sortByChangedTime()); + $this->assertIterator($this->toAbsolute(array('toto', 'test.py', 'test.php', 'foo/bar.tmp', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSortByModifiedTime() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->sortByModifiedTime()); + $this->assertIterator($this->toAbsolute(array('foo/bar.tmp', 'test.php', 'toto', 'test.py', 'foo', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testSort() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->sort(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); })); + $this->assertIterator($this->toAbsolute(array('foo', 'foo bar', 'foo/bar.tmp', 'test.php', 'test.py', 'toto')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testFilter() + { + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->filter(function (\SplFileInfo $f) { return false !== strpos($f, 'test'); })); + $this->assertIterator($this->toAbsolute(array('test.php', 'test.py')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testFollowLinks() + { + if ('\\' == \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('symlinks are not supported on Windows'); + } + + $finder = $this->buildFinder(); + $this->assertSame($finder, $finder->followLinks()); + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'test.php', 'test.py', 'toto', 'foo bar')), $finder->in(self::$tmpDir)->getIterator()); + } + + public function testIn() + { + $finder = $this->buildFinder(); + $iterator = $finder->files()->name('*.php')->depth('< 1')->in(array(self::$tmpDir, __DIR__))->getIterator(); + + $expected = array( + self::$tmpDir.\DIRECTORY_SEPARATOR.'test.php', + __DIR__.\DIRECTORY_SEPARATOR.'FinderTest.php', + __DIR__.\DIRECTORY_SEPARATOR.'GlobTest.php', + ); + + $this->assertIterator($expected, $iterator); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInWithNonExistentDirectory() + { + $finder = new Finder(); + $finder->in('foobar'); + } + + public function testInWithGlob() + { + $finder = $this->buildFinder(); + $finder->in(array(__DIR__.'/Fixtures/*/B/C/', __DIR__.'/Fixtures/*/*/B/C/'))->getIterator(); + + $this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInWithNonDirectoryGlob() + { + $finder = new Finder(); + $finder->in(__DIR__.'/Fixtures/A/a*'); + } + + public function testInWithGlobBrace() + { + $finder = $this->buildFinder(); + $finder->in(array(__DIR__.'/Fixtures/{A,copy/A}/B/C'))->getIterator(); + + $this->assertIterator($this->toAbsoluteFixtures(array('A/B/C/abc.dat', 'copy/A/B/C/abc.dat.copy')), $finder); + } + + /** + * @expectedException \LogicException + */ + public function testGetIteratorWithoutIn() + { + $finder = Finder::create(); + $finder->getIterator(); + } + + public function testGetIterator() + { + $finder = $this->buildFinder(); + $dirs = array(); + foreach ($finder->directories()->in(self::$tmpDir) as $dir) { + $dirs[] = (string) $dir; + } + + $expected = $this->toAbsolute(array('foo', 'toto')); + + sort($dirs); + sort($expected); + + $this->assertEquals($expected, $dirs, 'implements the \IteratorAggregate interface'); + + $finder = $this->buildFinder(); + $this->assertEquals(2, iterator_count($finder->directories()->in(self::$tmpDir)), 'implements the \IteratorAggregate interface'); + + $finder = $this->buildFinder(); + $a = iterator_to_array($finder->directories()->in(self::$tmpDir)); + $a = array_values(array_map('strval', $a)); + sort($a); + $this->assertEquals($expected, $a, 'implements the \IteratorAggregate interface'); + } + + public function testRelativePath() + { + $finder = $this->buildFinder()->in(self::$tmpDir); + + $paths = array(); + + foreach ($finder as $file) { + $paths[] = $file->getRelativePath(); + } + + $ref = array('', '', '', '', 'foo', ''); + + sort($ref); + sort($paths); + + $this->assertEquals($ref, $paths); + } + + public function testRelativePathname() + { + $finder = $this->buildFinder()->in(self::$tmpDir)->sortByName(); + + $paths = array(); + + foreach ($finder as $file) { + $paths[] = $file->getRelativePathname(); + } + + $ref = array('test.php', 'toto', 'test.py', 'foo', 'foo'.\DIRECTORY_SEPARATOR.'bar.tmp', 'foo bar'); + + sort($paths); + sort($ref); + + $this->assertEquals($ref, $paths); + } + + public function testAppendWithAFinder() + { + $finder = $this->buildFinder(); + $finder->files()->in(self::$tmpDir.\DIRECTORY_SEPARATOR.'foo'); + + $finder1 = $this->buildFinder(); + $finder1->directories()->in(self::$tmpDir); + + $finder = $finder->append($finder1); + + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator()); + } + + public function testAppendWithAnArray() + { + $finder = $this->buildFinder(); + $finder->files()->in(self::$tmpDir.\DIRECTORY_SEPARATOR.'foo'); + + $finder->append($this->toAbsolute(array('foo', 'toto'))); + + $this->assertIterator($this->toAbsolute(array('foo', 'foo/bar.tmp', 'toto')), $finder->getIterator()); + } + + public function testAppendReturnsAFinder() + { + $this->assertInstanceOf('Symfony\\Component\\Finder\\Finder', Finder::create()->append(array())); + } + + public function testAppendDoesNotRequireIn() + { + $finder = $this->buildFinder(); + $finder->in(self::$tmpDir.\DIRECTORY_SEPARATOR.'foo'); + + $finder1 = Finder::create()->append($finder); + + $this->assertIterator(iterator_to_array($finder->getIterator()), $finder1->getIterator()); + } + + public function testCountDirectories() + { + $directory = Finder::create()->directories()->in(self::$tmpDir); + $i = 0; + + foreach ($directory as $dir) { + ++$i; + } + + $this->assertCount($i, $directory); + } + + public function testCountFiles() + { + $files = Finder::create()->files()->in(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'); + $i = 0; + + foreach ($files as $file) { + ++$i; + } + + $this->assertCount($i, $files); + } + + /** + * @expectedException \LogicException + */ + public function testCountWithoutIn() + { + $finder = Finder::create()->files(); + \count($finder); + } + + public function testHasResults() + { + $finder = $this->buildFinder(); + $finder->in(__DIR__); + $this->assertTrue($finder->hasResults()); + } + + public function testNoResults() + { + $finder = $this->buildFinder(); + $finder->in(__DIR__)->name('DoesNotExist'); + $this->assertFalse($finder->hasResults()); + } + + /** + * @dataProvider getContainsTestData + */ + public function testContains($matchPatterns, $noMatchPatterns, $expected) + { + $finder = $this->buildFinder(); + $finder->in(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures') + ->name('*.txt')->sortByName() + ->contains($matchPatterns) + ->notContains($noMatchPatterns); + + $this->assertIterator($this->toAbsoluteFixtures($expected), $finder); + } + + public function testContainsOnDirectory() + { + $finder = $this->buildFinder(); + $finder->in(__DIR__) + ->directories() + ->name('Fixtures') + ->contains('abc'); + $this->assertIterator(array(), $finder); + } + + public function testNotContainsOnDirectory() + { + $finder = $this->buildFinder(); + $finder->in(__DIR__) + ->directories() + ->name('Fixtures') + ->notContains('abc'); + $this->assertIterator(array(), $finder); + } + + /** + * Searching in multiple locations involves AppendIterator which does an unnecessary rewind which leaves FilterIterator + * with inner FilesystemIterator in an invalid state. + * + * @see https://bugs.php.net/68557 + */ + public function testMultipleLocations() + { + $locations = array( + self::$tmpDir.'/', + self::$tmpDir.'/toto/', + ); + + // it is expected that there are test.py test.php in the tmpDir + $finder = new Finder(); + $finder->in($locations) + // the default flag IGNORE_DOT_FILES fixes the problem indirectly + // so we set it to false for better isolation + ->ignoreDotFiles(false) + ->depth('< 1')->name('test.php'); + + $this->assertCount(1, $finder); + } + + /** + * Searching in multiple locations with sub directories involves + * AppendIterator which does an unnecessary rewind which leaves + * FilterIterator with inner FilesystemIterator in an invalid state. + * + * @see https://bugs.php.net/68557 + */ + public function testMultipleLocationsWithSubDirectories() + { + $locations = array( + __DIR__.'/Fixtures/one', + self::$tmpDir.\DIRECTORY_SEPARATOR.'toto', + ); + + $finder = $this->buildFinder(); + $finder->in($locations)->depth('< 10')->name('*.neon'); + + $expected = array( + __DIR__.'/Fixtures/one'.\DIRECTORY_SEPARATOR.'b'.\DIRECTORY_SEPARATOR.'c.neon', + __DIR__.'/Fixtures/one'.\DIRECTORY_SEPARATOR.'b'.\DIRECTORY_SEPARATOR.'d.neon', + ); + + $this->assertIterator($expected, $finder); + $this->assertIteratorInForeach($expected, $finder); + } + + /** + * Iterator keys must be the file pathname. + */ + public function testIteratorKeys() + { + $finder = $this->buildFinder()->in(self::$tmpDir); + foreach ($finder as $key => $file) { + $this->assertEquals($file->getPathname(), $key); + } + } + + public function testRegexSpecialCharsLocationWithPathRestrictionContainingStartFlag() + { + $finder = $this->buildFinder(); + $finder->in(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.'r+e.gex[c]a(r)s') + ->path('/^dir/'); + + $expected = array('r+e.gex[c]a(r)s'.\DIRECTORY_SEPARATOR.'dir', 'r+e.gex[c]a(r)s'.\DIRECTORY_SEPARATOR.'dir'.\DIRECTORY_SEPARATOR.'bar.dat'); + $this->assertIterator($this->toAbsoluteFixtures($expected), $finder); + } + + public function getContainsTestData() + { + return array( + array('', '', array()), + array('foo', 'bar', array()), + array('', 'foobar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')), + array('lorem ipsum dolor sit amet', 'foobar', array('lorem.txt')), + array('sit', 'bar', array('dolor.txt', 'ipsum.txt', 'lorem.txt')), + array('dolor sit amet', '@^L@m', array('dolor.txt', 'ipsum.txt')), + array('/^lorem ipsum dolor sit amet$/m', 'foobar', array('lorem.txt')), + array('lorem', 'foobar', array('lorem.txt')), + array('', 'lorem', array('dolor.txt', 'ipsum.txt')), + array('ipsum dolor sit amet', '/^IPSUM/m', array('lorem.txt')), + ); + } + + public function getRegexNameTestData() + { + return array( + array('~.+\\.p.+~i'), + array('~t.*s~i'), + ); + } + + /** + * @dataProvider getTestPathData + */ + public function testPath($matchPatterns, $noMatchPatterns, array $expected) + { + $finder = $this->buildFinder(); + $finder->in(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures') + ->path($matchPatterns) + ->notPath($noMatchPatterns); + + $this->assertIterator($this->toAbsoluteFixtures($expected), $finder); + } + + public function getTestPathData() + { + return array( + array('', '', array()), + array('/^A\/B\/C/', '/C$/', + array('A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat'), + ), + array('/^A\/B/', 'foobar', + array( + 'A'.\DIRECTORY_SEPARATOR.'B', + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C', + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'ab.dat', + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat', + ), + ), + array('A/B/C', 'foobar', + array( + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C', + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat', + 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C', + 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat.copy', + ), + ), + array('A/B', 'foobar', + array( + //dirs + 'A'.\DIRECTORY_SEPARATOR.'B', + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C', + 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B', + 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C', + //files + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'ab.dat', + 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat', + 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'ab.dat.copy', + 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat.copy', + ), + ), + array('/^with space\//', 'foobar', + array( + 'with space'.\DIRECTORY_SEPARATOR.'foo.txt', + ), + ), + ); + } + + public function testAccessDeniedException() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('chmod is not supported on Windows'); + } + + $finder = $this->buildFinder(); + $finder->files()->in(self::$tmpDir); + + // make 'foo' directory non-readable + $testDir = self::$tmpDir.\DIRECTORY_SEPARATOR.'foo'; + chmod($testDir, 0333); + + if (false === $couldRead = is_readable($testDir)) { + try { + $this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator()); + $this->fail('Finder should throw an exception when opening a non-readable directory.'); + } catch (\Exception $e) { + $expectedExceptionClass = 'Symfony\\Component\\Finder\\Exception\\AccessDeniedException'; + if ($e instanceof \PHPUnit_Framework_ExpectationFailedException) { + $this->fail(sprintf("Expected exception:\n%s\nGot:\n%s\nWith comparison failure:\n%s", $expectedExceptionClass, 'PHPUnit_Framework_ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString())); + } + + if ($e instanceof \PHPUnit\Framework\ExpectationFailedException) { + $this->fail(sprintf("Expected exception:\n%s\nGot:\n%s\nWith comparison failure:\n%s", $expectedExceptionClass, '\PHPUnit\Framework\ExpectationFailedException', $e->getComparisonFailure()->getExpectedAsString())); + } + + $this->assertInstanceOf($expectedExceptionClass, $e); + } + } + + // restore original permissions + chmod($testDir, 0777); + clearstatcache($testDir); + + if ($couldRead) { + $this->markTestSkipped('could read test files while test requires unreadable'); + } + } + + public function testIgnoredAccessDeniedException() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('chmod is not supported on Windows'); + } + + $finder = $this->buildFinder(); + $finder->files()->ignoreUnreadableDirs()->in(self::$tmpDir); + + // make 'foo' directory non-readable + $testDir = self::$tmpDir.\DIRECTORY_SEPARATOR.'foo'; + chmod($testDir, 0333); + + if (false === ($couldRead = is_readable($testDir))) { + $this->assertIterator($this->toAbsolute(array('foo bar', 'test.php', 'test.py')), $finder->getIterator()); + } + + // restore original permissions + chmod($testDir, 0777); + clearstatcache($testDir); + + if ($couldRead) { + $this->markTestSkipped('could read test files while test requires unreadable'); + } + } + + protected function buildFinder() + { + return Finder::create(); + } +} diff --git a/vendor/symfony/finder/Tests/Fixtures/.dot/a b/vendor/symfony/finder/Tests/Fixtures/.dot/a new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/.dot/b/c.neon b/vendor/symfony/finder/Tests/Fixtures/.dot/b/c.neon new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/.dot/b/d.neon b/vendor/symfony/finder/Tests/Fixtures/.dot/b/d.neon new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat b/vendor/symfony/finder/Tests/Fixtures/A/B/C/abc.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat b/vendor/symfony/finder/Tests/Fixtures/A/B/ab.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/A/a.dat b/vendor/symfony/finder/Tests/Fixtures/A/a.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy b/vendor/symfony/finder/Tests/Fixtures/copy/A/B/C/abc.dat.copy new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy b/vendor/symfony/finder/Tests/Fixtures/copy/A/B/ab.dat.copy new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy b/vendor/symfony/finder/Tests/Fixtures/copy/A/a.dat.copy new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/dolor.txt b/vendor/symfony/finder/Tests/Fixtures/dolor.txt new file mode 100644 index 0000000..658bec6 --- /dev/null +++ b/vendor/symfony/finder/Tests/Fixtures/dolor.txt @@ -0,0 +1,2 @@ +dolor sit amet +DOLOR SIT AMET \ No newline at end of file diff --git a/vendor/symfony/finder/Tests/Fixtures/ipsum.txt b/vendor/symfony/finder/Tests/Fixtures/ipsum.txt new file mode 100644 index 0000000..c7f392d --- /dev/null +++ b/vendor/symfony/finder/Tests/Fixtures/ipsum.txt @@ -0,0 +1,2 @@ +ipsum dolor sit amet +IPSUM DOLOR SIT AMET \ No newline at end of file diff --git a/vendor/symfony/finder/Tests/Fixtures/lorem.txt b/vendor/symfony/finder/Tests/Fixtures/lorem.txt new file mode 100644 index 0000000..2991a2c --- /dev/null +++ b/vendor/symfony/finder/Tests/Fixtures/lorem.txt @@ -0,0 +1,2 @@ +lorem ipsum dolor sit amet +LOREM IPSUM DOLOR SIT AMET \ No newline at end of file diff --git a/vendor/symfony/finder/Tests/Fixtures/one/.dot b/vendor/symfony/finder/Tests/Fixtures/one/.dot new file mode 100644 index 0000000..1028065 --- /dev/null +++ b/vendor/symfony/finder/Tests/Fixtures/one/.dot @@ -0,0 +1 @@ +.dot \ No newline at end of file diff --git a/vendor/symfony/finder/Tests/Fixtures/one/a b/vendor/symfony/finder/Tests/Fixtures/one/a new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/one/b/c.neon b/vendor/symfony/finder/Tests/Fixtures/one/b/c.neon new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/one/b/d.neon b/vendor/symfony/finder/Tests/Fixtures/one/b/d.neon new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat b/vendor/symfony/finder/Tests/Fixtures/r+e.gex[c]a(r)s/dir/bar.dat new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/Fixtures/with space/foo.txt b/vendor/symfony/finder/Tests/Fixtures/with space/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/finder/Tests/GlobTest.php b/vendor/symfony/finder/Tests/GlobTest.php new file mode 100644 index 0000000..3a5aab3 --- /dev/null +++ b/vendor/symfony/finder/Tests/GlobTest.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\Finder\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Glob; + +class GlobTest extends TestCase +{ + public function testGlobToRegexDelimiters() + { + $this->assertEquals('#^(?=[^\.])\#$#', Glob::toRegex('#')); + $this->assertEquals('#^\.[^/]*$#', Glob::toRegex('.*')); + $this->assertEquals('^\.[^/]*$', Glob::toRegex('.*', true, true, '')); + $this->assertEquals('/^\.[^/]*$/', Glob::toRegex('.*', true, true, '/')); + } + + public function testGlobToRegexDoubleStarStrictDots() + { + $finder = new Finder(); + $finder->ignoreDotFiles(false); + $regex = Glob::toRegex('/**/*.neon'); + + foreach ($finder->in(__DIR__) as $k => $v) { + $k = str_replace(\DIRECTORY_SEPARATOR, '/', $k); + if (preg_match($regex, substr($k, \strlen(__DIR__)))) { + $match[] = substr($k, 10 + \strlen(__DIR__)); + } + } + sort($match); + + $this->assertSame(array('one/b/c.neon', 'one/b/d.neon'), $match); + } + + public function testGlobToRegexDoubleStarNonStrictDots() + { + $finder = new Finder(); + $finder->ignoreDotFiles(false); + $regex = Glob::toRegex('/**/*.neon', false); + + foreach ($finder->in(__DIR__) as $k => $v) { + $k = str_replace(\DIRECTORY_SEPARATOR, '/', $k); + if (preg_match($regex, substr($k, \strlen(__DIR__)))) { + $match[] = substr($k, 10 + \strlen(__DIR__)); + } + } + sort($match); + + $this->assertSame(array('.dot/b/c.neon', '.dot/b/d.neon', 'one/b/c.neon', 'one/b/d.neon'), $match); + } + + public function testGlobToRegexDoubleStarWithoutLeadingSlash() + { + $finder = new Finder(); + $finder->ignoreDotFiles(false); + $regex = Glob::toRegex('/Fixtures/one/**'); + + foreach ($finder->in(__DIR__) as $k => $v) { + $k = str_replace(\DIRECTORY_SEPARATOR, '/', $k); + if (preg_match($regex, substr($k, \strlen(__DIR__)))) { + $match[] = substr($k, 10 + \strlen(__DIR__)); + } + } + sort($match); + + $this->assertSame(array('one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'), $match); + } + + public function testGlobToRegexDoubleStarWithoutLeadingSlashNotStrictLeadingDot() + { + $finder = new Finder(); + $finder->ignoreDotFiles(false); + $regex = Glob::toRegex('/Fixtures/one/**', false); + + foreach ($finder->in(__DIR__) as $k => $v) { + $k = str_replace(\DIRECTORY_SEPARATOR, '/', $k); + if (preg_match($regex, substr($k, \strlen(__DIR__)))) { + $match[] = substr($k, 10 + \strlen(__DIR__)); + } + } + sort($match); + + $this->assertSame(array('one/.dot', 'one/a', 'one/b', 'one/b/c.neon', 'one/b/d.neon'), $match); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.php new file mode 100644 index 0000000..b036ad1 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/CustomFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\CustomFilterIterator; + +class CustomFilterIteratorTest extends IteratorTestCase +{ + /** + * @expectedException \InvalidArgumentException + */ + public function testWithInvalidFilter() + { + new CustomFilterIterator(new Iterator(), array('foo')); + } + + /** + * @dataProvider getAcceptData + */ + public function testAccept($filters, $expected) + { + $inner = new Iterator(array('test.php', 'test.py', 'foo.php')); + + $iterator = new CustomFilterIterator($inner, $filters); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + return array( + array(array(function (\SplFileInfo $fileinfo) { return false; }), array()), + array(array(function (\SplFileInfo $fileinfo) { return 0 === strpos($fileinfo, 'test'); }), array('test.php', 'test.py')), + array(array('is_dir'), array()), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.php new file mode 100644 index 0000000..3226f70 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/DateRangeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Comparator\DateComparator; +use Symfony\Component\Finder\Iterator\DateRangeFilterIterator; + +class DateRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($size, $expected) + { + $files = self::$files; + $files[] = self::toAbsolute('doesnotexist'); + $inner = new Iterator($files); + + $iterator = new DateRangeFilterIterator($inner, $size); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $since20YearsAgo = array( + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + 'toto/.git', + '.bar', + '.foo', + '.foo/.bar', + 'foo bar', + '.foo/bar', + ); + + $since2MonthsAgo = array( + '.git', + 'test.py', + 'foo', + 'toto', + 'toto/.git', + '.bar', + '.foo', + '.foo/.bar', + 'foo bar', + '.foo/bar', + ); + + $untilLastMonth = array( + 'foo/bar.tmp', + 'test.php', + ); + + return array( + array(array(new DateComparator('since 20 years ago')), $this->toAbsolute($since20YearsAgo)), + array(array(new DateComparator('since 2 months ago')), $this->toAbsolute($since2MonthsAgo)), + array(array(new DateComparator('until last month')), $this->toAbsolute($untilLastMonth)), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.php new file mode 100644 index 0000000..2e90140 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/DepthRangeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\DepthRangeFilterIterator; + +class DepthRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($minDepth, $maxDepth, $expected) + { + $inner = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST); + + $iterator = new DepthRangeFilterIterator($inner, $minDepth, $maxDepth); + + $actual = array_keys(iterator_to_array($iterator)); + sort($expected); + sort($actual); + $this->assertEquals($expected, $actual); + } + + public function getAcceptData() + { + $lessThan1 = array( + '.git', + 'test.py', + 'foo', + 'test.php', + 'toto', + '.foo', + '.bar', + 'foo bar', + ); + + $lessThanOrEqualTo1 = array( + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + 'toto/.git', + '.foo', + '.foo/.bar', + '.bar', + 'foo bar', + '.foo/bar', + ); + + $graterThanOrEqualTo1 = array( + 'toto/.git', + 'foo/bar.tmp', + '.foo/.bar', + '.foo/bar', + ); + + $equalTo1 = array( + 'toto/.git', + 'foo/bar.tmp', + '.foo/.bar', + '.foo/bar', + ); + + return array( + array(0, 0, $this->toAbsolute($lessThan1)), + array(0, 1, $this->toAbsolute($lessThanOrEqualTo1)), + array(2, PHP_INT_MAX, array()), + array(1, PHP_INT_MAX, $this->toAbsolute($graterThanOrEqualTo1)), + array(1, 1, $this->toAbsolute($equalTo1)), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php new file mode 100644 index 0000000..fa192c3 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/ExcludeDirectoryFilterIteratorTest.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\ExcludeDirectoryFilterIterator; +use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; + +class ExcludeDirectoryFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($directories, $expected) + { + $inner = new \RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->toAbsolute(), \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST); + + $iterator = new ExcludeDirectoryFilterIterator($inner, $directories); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $foo = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'test.php', + 'toto', + 'toto/.git', + 'foo bar', + ); + + $fo = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'toto', + 'toto/.git', + 'foo bar', + ); + + $toto = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'test.py', + 'foo', + 'foo/bar.tmp', + 'test.php', + 'foo bar', + ); + + return array( + array(array('foo'), $this->toAbsolute($foo)), + array(array('fo'), $this->toAbsolute($fo)), + array(array('toto/'), $this->toAbsolute($toto)), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php new file mode 100644 index 0000000..4350b00 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/FileTypeFilterIteratorTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FileTypeFilterIterator; + +class FileTypeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($mode, $expected) + { + $inner = new InnerTypeIterator(self::$files); + + $iterator = new FileTypeFilterIterator($inner, $mode); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $onlyFiles = array( + 'test.py', + 'foo/bar.tmp', + 'test.php', + '.bar', + '.foo/.bar', + '.foo/bar', + 'foo bar', + ); + + $onlyDirectories = array( + '.git', + 'foo', + 'toto', + 'toto/.git', + '.foo', + ); + + return array( + array(FileTypeFilterIterator::ONLY_FILES, $this->toAbsolute($onlyFiles)), + array(FileTypeFilterIterator::ONLY_DIRECTORIES, $this->toAbsolute($onlyDirectories)), + ); + } +} + +class InnerTypeIterator extends \ArrayIterator +{ + public function current() + { + return new \SplFileInfo(parent::current()); + } + + public function isFile() + { + return $this->current()->isFile(); + } + + public function isDir() + { + return $this->current()->isDir(); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.php new file mode 100644 index 0000000..744bdae --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/FilecontentFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FilecontentFilterIterator; + +class FilecontentFilterIteratorTest extends IteratorTestCase +{ + public function testAccept() + { + $inner = new MockFileListIterator(array('test.txt')); + $iterator = new FilecontentFilterIterator($inner, array(), array()); + $this->assertIterator(array('test.txt'), $iterator); + } + + public function testDirectory() + { + $inner = new MockFileListIterator(array('directory')); + $iterator = new FilecontentFilterIterator($inner, array('directory'), array()); + $this->assertIterator(array(), $iterator); + } + + public function testUnreadableFile() + { + $inner = new MockFileListIterator(array('file r-')); + $iterator = new FilecontentFilterIterator($inner, array('file r-'), array()); + $this->assertIterator(array(), $iterator); + } + + /** + * @dataProvider getTestFilterData + */ + public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray) + { + $iterator = new FilecontentFilterIterator($inner, $matchPatterns, $noMatchPatterns); + $this->assertIterator($resultArray, $iterator); + } + + public function getTestFilterData() + { + $inner = new MockFileListIterator(); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'a.txt', + 'contents' => 'Lorem ipsum...', + 'type' => 'file', + 'mode' => 'r+', ) + ); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'b.yml', + 'contents' => 'dolor sit...', + 'type' => 'file', + 'mode' => 'r+', ) + ); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'some/other/dir/third.php', + 'contents' => 'amet...', + 'type' => 'file', + 'mode' => 'r+', ) + ); + + $inner[] = new MockSplFileInfo(array( + 'name' => 'unreadable-file.txt', + 'contents' => false, + 'type' => 'file', + 'mode' => 'r+', ) + ); + + return array( + array($inner, array('.'), array(), array('a.txt', 'b.yml', 'some/other/dir/third.php')), + array($inner, array('ipsum'), array(), array('a.txt')), + array($inner, array('i', 'amet'), array('Lorem', 'amet'), array('b.yml')), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.php new file mode 100644 index 0000000..c4b9795 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/FilenameFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\FilenameFilterIterator; + +class FilenameFilterIteratorTest extends IteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($matchPatterns, $noMatchPatterns, $expected) + { + $inner = new InnerNameIterator(array('test.php', 'test.py', 'foo.php')); + + $iterator = new FilenameFilterIterator($inner, $matchPatterns, $noMatchPatterns); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + return array( + array(array('test.*'), array(), array('test.php', 'test.py')), + array(array(), array('test.*'), array('foo.php')), + array(array('*.php'), array('test.*'), array('foo.php')), + array(array('*.php', '*.py'), array('foo.*'), array('test.php', 'test.py')), + array(array('/\.php$/'), array(), array('test.php', 'foo.php')), + array(array(), array('/\.php$/'), array('test.py')), + ); + } +} + +class InnerNameIterator extends \ArrayIterator +{ + public function current() + { + return new \SplFileInfo(parent::current()); + } + + public function getFilename() + { + return parent::current(); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/Iterator.php b/vendor/symfony/finder/Tests/Iterator/Iterator.php new file mode 100644 index 0000000..849bf08 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/Iterator.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\Finder\Tests\Iterator; + +class Iterator implements \Iterator +{ + protected $values = array(); + + public function __construct(array $values = array()) + { + foreach ($values as $value) { + $this->attach(new \SplFileInfo($value)); + } + $this->rewind(); + } + + public function attach(\SplFileInfo $fileinfo) + { + $this->values[] = $fileinfo; + } + + public function rewind() + { + reset($this->values); + } + + public function valid() + { + return false !== $this->current(); + } + + public function next() + { + next($this->values); + } + + public function current() + { + return current($this->values); + } + + public function key() + { + return key($this->values); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php b/vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php new file mode 100644 index 0000000..89f042a --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/IteratorTestCase.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Tests\Iterator; + +use PHPUnit\Framework\TestCase; + +abstract class IteratorTestCase extends TestCase +{ + protected function assertIterator($expected, \Traversable $iterator) + { + // set iterator_to_array $use_key to false to avoid values merge + // this made FinderTest::testAppendWithAnArray() fail with GnuFinderAdapter + $values = array_map(function (\SplFileInfo $fileinfo) { return str_replace('/', \DIRECTORY_SEPARATOR, $fileinfo->getPathname()); }, iterator_to_array($iterator, false)); + + $expected = array_map(function ($path) { return str_replace('/', \DIRECTORY_SEPARATOR, $path); }, $expected); + + sort($values); + sort($expected); + + $this->assertEquals($expected, array_values($values)); + } + + protected function assertOrderedIterator($expected, \Traversable $iterator) + { + $values = array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator)); + + $this->assertEquals($expected, array_values($values)); + } + + /** + * Same as assertOrderedIterator, but checks the order of groups of + * array elements. + * + * @param array $expected - an array of arrays. For any two subarrays + * $a and $b such that $a goes before $b in $expected, the method + * asserts that any element of $a goes before any element of $b + * in the sequence generated by $iterator + * @param \Traversable $iterator + */ + protected function assertOrderedIteratorForGroups($expected, \Traversable $iterator) + { + $values = array_values(array_map(function (\SplFileInfo $fileinfo) { return $fileinfo->getPathname(); }, iterator_to_array($iterator))); + + foreach ($expected as $subarray) { + $temp = array(); + while (\count($values) && \count($temp) < \count($subarray)) { + $temp[] = array_shift($values); + } + sort($temp); + sort($subarray); + $this->assertEquals($subarray, $temp); + } + } + + /** + * Same as IteratorTestCase::assertIterator with foreach usage. + * + * @param array $expected + * @param \Traversable $iterator + */ + protected function assertIteratorInForeach($expected, \Traversable $iterator) + { + $values = array(); + foreach ($iterator as $file) { + $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file); + $values[] = $file->getPathname(); + } + + sort($values); + sort($expected); + + $this->assertEquals($expected, array_values($values)); + } + + /** + * Same as IteratorTestCase::assertOrderedIterator with foreach usage. + * + * @param array $expected + * @param \Traversable $iterator + */ + protected function assertOrderedIteratorInForeach($expected, \Traversable $iterator) + { + $values = array(); + foreach ($iterator as $file) { + $this->assertInstanceOf('Symfony\\Component\\Finder\\SplFileInfo', $file); + $values[] = $file->getPathname(); + } + + $this->assertEquals($expected, array_values($values)); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php b/vendor/symfony/finder/Tests/Iterator/MockFileListIterator.php new file mode 100644 index 0000000..eb0adfa --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/MockFileListIterator.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\Finder\Tests\Iterator; + +class MockFileListIterator extends \ArrayIterator +{ + public function __construct(array $filesArray = array()) + { + $files = array_map(function ($file) { return new MockSplFileInfo($file); }, $filesArray); + parent::__construct($files); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php b/vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.php new file mode 100644 index 0000000..a5ac93a --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/MockSplFileInfo.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\Finder\Tests\Iterator; + +class MockSplFileInfo extends \SplFileInfo +{ + const TYPE_DIRECTORY = 1; + const TYPE_FILE = 2; + const TYPE_UNKNOWN = 3; + + private $contents = null; + private $mode = null; + private $type = null; + private $relativePath = null; + private $relativePathname = null; + + public function __construct($param) + { + if (\is_string($param)) { + parent::__construct($param); + } elseif (\is_array($param)) { + $defaults = array( + 'name' => 'file.txt', + 'contents' => null, + 'mode' => null, + 'type' => null, + 'relativePath' => null, + 'relativePathname' => null, + ); + $defaults = array_merge($defaults, $param); + parent::__construct($defaults['name']); + $this->setContents($defaults['contents']); + $this->setMode($defaults['mode']); + $this->setType($defaults['type']); + $this->setRelativePath($defaults['relativePath']); + $this->setRelativePathname($defaults['relativePathname']); + } else { + throw new \RuntimeException(sprintf('Incorrect parameter "%s"', $param)); + } + } + + public function isFile() + { + if (null === $this->type) { + return false !== strpos($this->getFilename(), 'file'); + } + + return self::TYPE_FILE === $this->type; + } + + public function isDir() + { + if (null === $this->type) { + return false !== strpos($this->getFilename(), 'directory'); + } + + return self::TYPE_DIRECTORY === $this->type; + } + + public function isReadable() + { + if (null === $this->mode) { + return preg_match('/r\+/', $this->getFilename()); + } + + return preg_match('/r\+/', $this->mode); + } + + public function getContents() + { + return $this->contents; + } + + public function setContents($contents) + { + $this->contents = $contents; + } + + public function setMode($mode) + { + $this->mode = $mode; + } + + public function setType($type) + { + if (\is_string($type)) { + switch ($type) { + case 'directory': + case 'd': + $this->type = self::TYPE_DIRECTORY; + break; + case 'file': + case 'f': + $this->type = self::TYPE_FILE; + break; + default: + $this->type = self::TYPE_UNKNOWN; + } + } else { + $this->type = $type; + } + } + + public function setRelativePath($relativePath) + { + $this->relativePath = $relativePath; + } + + public function setRelativePathname($relativePathname) + { + $this->relativePathname = $relativePathname; + } + + public function getRelativePath() + { + return $this->relativePath; + } + + public function getRelativePathname() + { + return $this->relativePathname; + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.php new file mode 100644 index 0000000..f2c1cd2 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/MultiplePcreFilterIteratorTest.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\Finder\Tests\Iterator; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Finder\Iterator\MultiplePcreFilterIterator; + +class MultiplePcreFilterIteratorTest extends TestCase +{ + /** + * @dataProvider getIsRegexFixtures + */ + public function testIsRegex($string, $isRegex, $message) + { + $testIterator = new TestMultiplePcreFilterIterator(); + $this->assertEquals($isRegex, $testIterator->isRegex($string), $message); + } + + public function getIsRegexFixtures() + { + return array( + array('foo', false, 'string'), + array(' foo ', false, '" " is not a valid delimiter'), + array('\\foo\\', false, '"\\" is not a valid delimiter'), + array('afooa', false, '"a" is not a valid delimiter'), + array('//', false, 'the pattern should contain at least 1 character'), + array('/a/', true, 'valid regex'), + array('/foo/', true, 'valid regex'), + array('/foo/i', true, 'valid regex with a single modifier'), + array('/foo/imsxu', true, 'valid regex with multiple modifiers'), + array('#foo#', true, '"#" is a valid delimiter'), + array('{foo}', true, '"{,}" is a valid delimiter pair'), + array('[foo]', true, '"[,]" is a valid delimiter pair'), + array('(foo)', true, '"(,)" is a valid delimiter pair'), + array('', true, '"<,>" is a valid delimiter pair'), + array('*foo.*', false, '"*" is not considered as a valid delimiter'), + array('?foo.?', false, '"?" is not considered as a valid delimiter'), + ); + } +} + +class TestMultiplePcreFilterIterator extends MultiplePcreFilterIterator +{ + public function __construct() + { + } + + public function accept() + { + throw new \BadFunctionCallException('Not implemented'); + } + + public function isRegex($str) + { + return parent::isRegex($str); + } + + public function toRegex($str) + { + throw new \BadFunctionCallException('Not implemented'); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.php new file mode 100644 index 0000000..38ed966 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/PathFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\PathFilterIterator; + +class PathFilterIteratorTest extends IteratorTestCase +{ + /** + * @dataProvider getTestFilterData + */ + public function testFilter(\Iterator $inner, array $matchPatterns, array $noMatchPatterns, array $resultArray) + { + $iterator = new PathFilterIterator($inner, $matchPatterns, $noMatchPatterns); + $this->assertIterator($resultArray, $iterator); + } + + public function getTestFilterData() + { + $inner = new MockFileListIterator(); + + //PATH: A/B/C/abc.dat + $inner[] = new MockSplFileInfo(array( + 'name' => 'abc.dat', + 'relativePathname' => 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat', + )); + + //PATH: A/B/ab.dat + $inner[] = new MockSplFileInfo(array( + 'name' => 'ab.dat', + 'relativePathname' => 'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'ab.dat', + )); + + //PATH: A/a.dat + $inner[] = new MockSplFileInfo(array( + 'name' => 'a.dat', + 'relativePathname' => 'A'.\DIRECTORY_SEPARATOR.'a.dat', + )); + + //PATH: copy/A/B/C/abc.dat.copy + $inner[] = new MockSplFileInfo(array( + 'name' => 'abc.dat.copy', + 'relativePathname' => 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'C'.\DIRECTORY_SEPARATOR.'abc.dat', + )); + + //PATH: copy/A/B/ab.dat.copy + $inner[] = new MockSplFileInfo(array( + 'name' => 'ab.dat.copy', + 'relativePathname' => 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'B'.\DIRECTORY_SEPARATOR.'ab.dat', + )); + + //PATH: copy/A/a.dat.copy + $inner[] = new MockSplFileInfo(array( + 'name' => 'a.dat.copy', + 'relativePathname' => 'copy'.\DIRECTORY_SEPARATOR.'A'.\DIRECTORY_SEPARATOR.'a.dat', + )); + + return array( + array($inner, array('/^A/'), array(), array('abc.dat', 'ab.dat', 'a.dat')), + array($inner, array('/^A\/B/'), array(), array('abc.dat', 'ab.dat')), + array($inner, array('/^A\/B\/C/'), array(), array('abc.dat')), + array($inner, array('/A\/B\/C/'), array(), array('abc.dat', 'abc.dat.copy')), + + array($inner, array('A'), array(), array('abc.dat', 'ab.dat', 'a.dat', 'abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')), + array($inner, array('A/B'), array(), array('abc.dat', 'ab.dat', 'abc.dat.copy', 'ab.dat.copy')), + array($inner, array('A/B/C'), array(), array('abc.dat', 'abc.dat.copy')), + + array($inner, array('copy/A'), array(), array('abc.dat.copy', 'ab.dat.copy', 'a.dat.copy')), + array($inner, array('copy/A/B'), array(), array('abc.dat.copy', 'ab.dat.copy')), + array($inner, array('copy/A/B/C'), array(), array('abc.dat.copy')), + ); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php b/vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php new file mode 100644 index 0000000..b0223b7 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/RealIteratorTestCase.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Finder\Tests\Iterator; + +abstract class RealIteratorTestCase extends IteratorTestCase +{ + protected static $tmpDir; + protected static $files; + + public static function setUpBeforeClass() + { + self::$tmpDir = realpath(sys_get_temp_dir()).\DIRECTORY_SEPARATOR.'symfony_finder'; + + self::$files = array( + '.git/', + '.foo/', + '.foo/.bar', + '.foo/bar', + '.bar', + 'test.py', + 'foo/', + 'foo/bar.tmp', + 'test.php', + 'toto/', + 'toto/.git/', + 'foo bar', + ); + + self::$files = self::toAbsolute(self::$files); + + if (is_dir(self::$tmpDir)) { + self::tearDownAfterClass(); + } else { + mkdir(self::$tmpDir); + } + + foreach (self::$files as $file) { + if (\DIRECTORY_SEPARATOR === $file[\strlen($file) - 1]) { + mkdir($file); + } else { + touch($file); + } + } + + file_put_contents(self::toAbsolute('test.php'), str_repeat(' ', 800)); + file_put_contents(self::toAbsolute('test.py'), str_repeat(' ', 2000)); + + touch(self::toAbsolute('foo/bar.tmp'), strtotime('2005-10-15')); + touch(self::toAbsolute('test.php'), strtotime('2005-10-15')); + } + + public static function tearDownAfterClass() + { + $paths = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator(self::$tmpDir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($paths as $path) { + if ($path->isDir()) { + if ($path->isLink()) { + @unlink($path); + } else { + @rmdir($path); + } + } else { + @unlink($path); + } + } + } + + protected static function toAbsolute($files = null) + { + /* + * Without the call to setUpBeforeClass() property can be null. + */ + if (!self::$tmpDir) { + self::$tmpDir = realpath(sys_get_temp_dir()).\DIRECTORY_SEPARATOR.'symfony_finder'; + } + + if (\is_array($files)) { + $f = array(); + foreach ($files as $file) { + if (\is_array($file)) { + $f[] = self::toAbsolute($file); + } else { + $f[] = self::$tmpDir.\DIRECTORY_SEPARATOR.str_replace('/', \DIRECTORY_SEPARATOR, $file); + } + } + + return $f; + } + + if (\is_string($files)) { + return self::$tmpDir.\DIRECTORY_SEPARATOR.str_replace('/', \DIRECTORY_SEPARATOR, $files); + } + + return self::$tmpDir; + } + + protected static function toAbsoluteFixtures($files) + { + $f = array(); + foreach ($files as $file) { + $f[] = realpath(__DIR__.\DIRECTORY_SEPARATOR.'..'.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR.$file); + } + + return $f; + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.php new file mode 100644 index 0000000..0c9aca2 --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/RecursiveDirectoryIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\RecursiveDirectoryIterator; + +class RecursiveDirectoryIteratorTest extends IteratorTestCase +{ + /** + * @group network + */ + public function testRewindOnFtp() + { + try { + $i = new RecursiveDirectoryIterator('ftp://speedtest.tele2.net/', \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped('Unsupported stream "ftp".'); + } + + $i->rewind(); + + $this->assertTrue(true); + } + + /** + * @group network + */ + public function testSeekOnFtp() + { + try { + $i = new RecursiveDirectoryIterator('ftp://speedtest.tele2.net/', \RecursiveDirectoryIterator::SKIP_DOTS); + } catch (\UnexpectedValueException $e) { + $this->markTestSkipped('Unsupported stream "ftp".'); + } + + $contains = array( + 'ftp://speedtest.tele2.net'.\DIRECTORY_SEPARATOR.'1000GB.zip', + 'ftp://speedtest.tele2.net'.\DIRECTORY_SEPARATOR.'100GB.zip', + ); + $actual = array(); + + $i->seek(0); + $actual[] = $i->getPathname(); + + $i->seek(1); + $actual[] = $i->getPathname(); + + $this->assertEquals($contains, $actual); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.php new file mode 100644 index 0000000..068fc7b --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/SizeRangeFilterIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Comparator\NumberComparator; +use Symfony\Component\Finder\Iterator\SizeRangeFilterIterator; + +class SizeRangeFilterIteratorTest extends RealIteratorTestCase +{ + /** + * @dataProvider getAcceptData + */ + public function testAccept($size, $expected) + { + $inner = new InnerSizeIterator(self::$files); + + $iterator = new SizeRangeFilterIterator($inner, $size); + + $this->assertIterator($expected, $iterator); + } + + public function getAcceptData() + { + $lessThan1KGreaterThan05K = array( + '.foo', + '.git', + 'foo', + 'test.php', + 'toto', + 'toto/.git', + ); + + return array( + array(array(new NumberComparator('< 1K'), new NumberComparator('> 0.5K')), $this->toAbsolute($lessThan1KGreaterThan05K)), + ); + } +} + +class InnerSizeIterator extends \ArrayIterator +{ + public function current() + { + return new \SplFileInfo(parent::current()); + } + + public function getFilename() + { + return parent::current(); + } + + public function isFile() + { + return $this->current()->isFile(); + } + + public function getSize() + { + return $this->current()->getSize(); + } +} diff --git a/vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php b/vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.php new file mode 100644 index 0000000..a35a12b --- /dev/null +++ b/vendor/symfony/finder/Tests/Iterator/SortableIteratorTest.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\Finder\Tests\Iterator; + +use Symfony\Component\Finder\Iterator\SortableIterator; + +class SortableIteratorTest extends RealIteratorTestCase +{ + public function testConstructor() + { + try { + new SortableIterator(new Iterator(array()), 'foobar'); + $this->fail('__construct() throws an \InvalidArgumentException exception if the mode is not valid'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '__construct() throws an \InvalidArgumentException exception if the mode is not valid'); + } + } + + /** + * @dataProvider getAcceptData + */ + public function testAccept($mode, $expected) + { + if (!\is_callable($mode)) { + switch ($mode) { + case SortableIterator::SORT_BY_ACCESSED_TIME: + if ('\\' === \DIRECTORY_SEPARATOR) { + touch(self::toAbsolute('.git')); + } else { + file_get_contents(self::toAbsolute('.git')); + } + sleep(1); + file_get_contents(self::toAbsolute('.bar')); + break; + case SortableIterator::SORT_BY_CHANGED_TIME: + file_put_contents(self::toAbsolute('test.php'), 'foo'); + sleep(1); + file_put_contents(self::toAbsolute('test.py'), 'foo'); + break; + case SortableIterator::SORT_BY_MODIFIED_TIME: + file_put_contents(self::toAbsolute('test.php'), 'foo'); + sleep(1); + file_put_contents(self::toAbsolute('test.py'), 'foo'); + break; + } + } + + $inner = new Iterator(self::$files); + + $iterator = new SortableIterator($inner, $mode); + + if (SortableIterator::SORT_BY_ACCESSED_TIME === $mode + || SortableIterator::SORT_BY_CHANGED_TIME === $mode + || SortableIterator::SORT_BY_MODIFIED_TIME === $mode + ) { + if ('\\' === \DIRECTORY_SEPARATOR && SortableIterator::SORT_BY_MODIFIED_TIME !== $mode) { + $this->markTestSkipped('Sorting by atime or ctime is not supported on Windows'); + } + $this->assertOrderedIteratorForGroups($expected, $iterator); + } else { + $this->assertOrderedIterator($expected, $iterator); + } + } + + public function getAcceptData() + { + $sortByName = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'foo', + 'foo bar', + 'foo/bar.tmp', + 'test.php', + 'test.py', + 'toto', + 'toto/.git', + ); + + $sortByType = array( + '.foo', + '.git', + 'foo', + 'toto', + 'toto/.git', + '.bar', + '.foo/.bar', + '.foo/bar', + 'foo bar', + 'foo/bar.tmp', + 'test.php', + 'test.py', + ); + + $customComparison = array( + '.bar', + '.foo', + '.foo/.bar', + '.foo/bar', + '.git', + 'foo', + 'foo bar', + 'foo/bar.tmp', + 'test.php', + 'test.py', + 'toto', + 'toto/.git', + ); + + $sortByAccessedTime = array( + // For these two files the access time was set to 2005-10-15 + array('foo/bar.tmp', 'test.php'), + // These files were created more or less at the same time + array( + '.git', + '.foo', + '.foo/.bar', + '.foo/bar', + 'test.py', + 'foo', + 'toto', + 'toto/.git', + 'foo bar', + ), + // This file was accessed after sleeping for 1 sec + array('.bar'), + ); + + $sortByChangedTime = array( + array( + '.git', + '.foo', + '.foo/.bar', + '.foo/bar', + '.bar', + 'foo', + 'foo/bar.tmp', + 'toto', + 'toto/.git', + 'foo bar', + ), + array('test.php'), + array('test.py'), + ); + + $sortByModifiedTime = array( + array( + '.git', + '.foo', + '.foo/.bar', + '.foo/bar', + '.bar', + 'foo', + 'foo/bar.tmp', + 'toto', + 'toto/.git', + 'foo bar', + ), + array('test.php'), + array('test.py'), + ); + + return array( + array(SortableIterator::SORT_BY_NAME, $this->toAbsolute($sortByName)), + array(SortableIterator::SORT_BY_TYPE, $this->toAbsolute($sortByType)), + array(SortableIterator::SORT_BY_ACCESSED_TIME, $this->toAbsolute($sortByAccessedTime)), + array(SortableIterator::SORT_BY_CHANGED_TIME, $this->toAbsolute($sortByChangedTime)), + array(SortableIterator::SORT_BY_MODIFIED_TIME, $this->toAbsolute($sortByModifiedTime)), + array(function (\SplFileInfo $a, \SplFileInfo $b) { return strcmp($a->getRealPath(), $b->getRealPath()); }, $this->toAbsolute($customComparison)), + ); + } +} diff --git a/vendor/symfony/finder/composer.json b/vendor/symfony/finder/composer.json new file mode 100644 index 0000000..dd37f2e --- /dev/null +++ b/vendor/symfony/finder/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/finder", + "type": "library", + "description": "Symfony Finder 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": "^7.1.3" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Finder\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/finder/phpunit.xml.dist b/vendor/symfony/finder/phpunit.xml.dist new file mode 100644 index 0000000..0e1a866 --- /dev/null +++ b/vendor/symfony/finder/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/framework-bundle/.gitignore b/vendor/symfony/framework-bundle/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/framework-bundle/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/framework-bundle/CHANGELOG.md b/vendor/symfony/framework-bundle/CHANGELOG.md new file mode 100644 index 0000000..9d63e08 --- /dev/null +++ b/vendor/symfony/framework-bundle/CHANGELOG.md @@ -0,0 +1,271 @@ +CHANGELOG +========= + +3.4.0 +----- + + * Added `translator.default_path` option and parameter + * Session `use_strict_mode` is now enabled by default and the corresponding option has been deprecated + * Made the `cache:clear` command to *not* clear "app" PSR-6 cache pools anymore, + but to still clear "system" ones; use the `cache:pool:clear` command to clear "app" pools instead + * Always register a minimalist logger that writes in `stderr` + * Deprecated `profiler.matcher` option + * Added support for `EventSubscriberInterface` on `MicroKernelTrait` + * Removed `doctrine/cache` from the list of required dependencies in `composer.json` + * Deprecated `validator.mapping.cache.doctrine.apc` service + * The `symfony/stopwatch` dependency has been removed, require it via `composer + require symfony/stopwatch` in your `dev` environment. + * Deprecated using the `KERNEL_DIR` environment variable with `KernelTestCase::getKernelClass()`. + * Deprecated the `KernelTestCase::getPhpUnitXmlDir()` and `KernelTestCase::getPhpUnitCliConfigArgument()` methods. + * Deprecated `AddCacheClearerPass`, use tagged iterator arguments instead. + * Deprecated `AddCacheWarmerPass`, use tagged iterator arguments instead. + * Deprecated `TranslationDumperPass`, use + `Symfony\Component\Translation\DependencyInjection\TranslationDumperPass` instead + * Deprecated `TranslationExtractorPass`, use + `Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass` instead + * Deprecated `TranslatorPass`, use + `Symfony\Component\Translation\DependencyInjection\TranslatorPass` instead + * Added `command` attribute to the `console.command` tag which takes the command + name as value, using it makes the command lazy + * Added `cache:pool:prune` command to allow manual stale cache item pruning of supported PSR-6 and PSR-16 cache pool + implementations + * Deprecated `Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader`, use + `Symfony\Component\Translation\Reader\TranslationReader` instead + * Deprecated `translation.loader` service, use `translation.reader` instead + * `AssetsInstallCommand::__construct()` now takes an instance of + `Symfony\Component\Filesystem\Filesystem` as first argument + * `CacheClearCommand::__construct()` now takes an instance of + `Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface` as + first argument + * `CachePoolClearCommand::__construct()` now takes an instance of + `Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer` as + first argument + * `EventDispatcherDebugCommand::__construct()` now takes an instance of + `Symfony\Component\EventDispatcher\EventDispatcherInterface` as + first argument + * `RouterDebugCommand::__construct()` now takes an instance of + `Symfony\Component\Routing\RouterInterface` as + first argument + * `RouterMatchCommand::__construct()` now takes an instance of + `Symfony\Component\Routing\RouterInterface` as + first argument + * `TranslationDebugCommand::__construct()` now takes an instance of + `Symfony\Component\Translation\TranslatorInterface` as + first argument + * `TranslationUpdateCommand::__construct()` now takes an instance of + `Symfony\Component\Translation\TranslatorInterface` as + first argument + * `AssetsInstallCommand`, `CacheClearCommand`, `CachePoolClearCommand`, + `EventDispatcherDebugCommand`, `RouterDebugCommand`, `RouterMatchCommand`, + `TranslationDebugCommand`, `TranslationUpdateCommand`, `XliffLintCommand` + and `YamlLintCommand` classes have been marked as final + * Added `asset.request_context.base_path` and `asset.request_context.secure` parameters + to provide a default request context in case the stack is empty (similar to `router.request_context.*` parameters) + * Display environment variables managed by `Dotenv` in `AboutCommand` + +3.3.0 +----- + + * Not defining the `type` option of the `framework.workflows.*` configuration entries is deprecated. + The default value will be `state_machine` in Symfony 4.0. + * Deprecated the `CompilerDebugDumpPass` class + * Deprecated the "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter + * Added a new new version strategy option called json_manifest_path + that allows you to use the `JsonManifestVersionStrategy`. + * Added `Symfony\Bundle\FrameworkBundle\Controller\AbstractController`. It provides + the same helpers as the `Controller` class, but does not allow accessing the dependency + injection container, in order to encourage explicit dependency declarations. + * Added support for the `controller.service_arguments` tag, for injecting services into controllers' actions + * Changed default configuration for + assets/forms/validation/translation/serialization/csrf from `canBeEnabled()` to + `canBeDisabled()` when Flex is used + * The server:* commands and their associated router files were moved to WebServerBundle + * Translation related services are not loaded anymore when the `framework.translator` option + is disabled. + * Added `GlobalVariables::getToken()` + * Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass`. Use `Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass` instead. + * Added configurable paths for validation files + * Deprecated `SerializerPass`, use `Symfony\Component\Serializer\DependencyInjection\SerializerPass` instead + * Deprecated `FormPass`, use `Symfony\Component\Form\DependencyInjection\FormPass` instead + * Deprecated `SessionListener` + * Deprecated `TestSessionListener` + * Deprecated `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass`. + Use tagged iterator arguments instead. + * Deprecated `PropertyInfoPass`, use `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` instead + * Deprecated `ControllerArgumentValueResolverPass`. Use + `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` instead + * Deprecated `RoutingResolverPass`, use `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` instead + * [BC BREAK] The `server:run`, `server:start`, `server:stop` and + `server:status` console commands have been moved to a dedicated bundle. + Require `symfony/web-server-bundle` in your composer.json and register + `Symfony\Bundle\WebServerBundle\WebServerBundle` in your AppKernel to use them. + * Added `$defaultLocale` as 3rd argument of `Translator::__construct()` + making `Translator` works with any PSR-11 container + * Added `framework.serializer.mapping` config option allowing to define custom + serialization mapping files and directories + * Deprecated `AddValidatorInitializersPass`, use + `Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass` instead + * Deprecated `AddConstraintValidatorsPass`, use + `Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass` instead + * Deprecated `ValidateWorkflowsPass`, use + `Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass` instead + * Deprecated `ConstraintValidatorFactory`, use + `Symfony\Component\Validator\ContainerConstraintValidatorFactory` instead. + * Deprecated `PhpStringTokenParser`, use + `Symfony\Component\Translation\Extractor\PhpStringTokenParser` instead. + * Deprecated `PhpExtractor`, use + `Symfony\Component\Translation\Extractor\PhpExtractor` instead. + +3.2.0 +----- + + * Removed `doctrine/annotations` from the list of required dependencies in `composer.json` + * Removed `symfony/security-core` and `symfony/security-csrf` from the list of required dependencies in `composer.json` + * Removed `symfony/templating` from the list of required dependencies in `composer.json` + * Removed `symfony/translation` from the list of required dependencies in `composer.json` + * Removed `symfony/asset` from the list of required dependencies in `composer.json` + * The `Resources/public/images/*` files have been removed. + * The `Resources/public/css/*.css` files have been removed (they are now inlined in TwigBundle). + * Added possibility to prioritize form type extensions with `'priority'` attribute on tags `form.type_extension` + +3.1.0 +----- + + * Added `Controller::json` to simplify creating JSON responses when using the Serializer component + * Deprecated absolute template paths support in the template name parser + * Deprecated using core form types without dependencies as services + * Added `Symfony\Component\HttpHernel\DataCollector\RequestDataCollector::onKernelResponse()` + * Added `Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector` + * The `framework.serializer.cache` option and the service `serializer.mapping.cache.apc` have been + deprecated. APCu should now be automatically used when available. + +3.0.0 +----- + + * removed `validator.api` parameter + * removed `alias` option of the `form.type` tag + +2.8.0 +----- + + * Deprecated the `alias` option of the `form.type_extension` tag in favor of the + `extended_type`/`extended-type` option + * Deprecated the `alias` option of the `form.type` tag + * Deprecated the Shell + +2.7.0 +----- + + * Added possibility to extract translation messages from a file or files besides extracting from a directory + * Added `TranslationsCacheWarmer` to create catalogues at warmup + +2.6.0 +----- + + * Added helper commands (`server:start`, `server:stop` and `server:status`) to control the built-in web + server in the background + * Added `Controller::isCsrfTokenValid` helper + * Added configuration for the PropertyAccess component + * Added `Controller::redirectToRoute` helper + * Added `Controller::addFlash` helper + * Added `Controller::isGranted` helper + * Added `Controller::denyAccessUnlessGranted` helper + * Deprecated `app.security` in twig as `app.user` and `is_granted()` are already available + +2.5.0 +----- + + * Added `translation:debug` command + * Added `--no-backup` option to `translation:update` command + * Added `config:debug` command + * Added `yaml:lint` command + * Deprecated the `RouterApacheDumperCommand` which will be removed in Symfony 3.0. + +2.4.0 +----- + + * allowed multiple IP addresses in profiler matcher settings + * added stopwatch helper to time templates with the WebProfilerBundle + * added service definition for "security.secure_random" service + * added service definitions for the new Security CSRF sub-component + +2.3.0 +----- + + * [BC BREAK] added a way to disable the profiler (when disabling the profiler, it is now completely removed) + To get the same "disabled" behavior as before, set `enabled` to `true` and `collect` to `false` + * [BC BREAK] the `Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass` was moved + to `Component\HttpKernel\DependencyInjection\RegisterListenersPass` + * added ControllerNameParser::build() which converts a controller short notation (a:b:c) to a class::method notation + * added possibility to run PHP built-in server in production environment + * added possibility to load the serializer component in the service container + * added route debug information when using the `router:match` command + * added `TimedPhpEngine` + * added `--clean` option to the `translation:update` command + * added `http_method_override` option + * added support for default templates per render tag + * added FormHelper::form(), FormHelper::start() and FormHelper::end() + * deprecated FormHelper::enctype() in favor of FormHelper::start() + * RedirectController actions now receive the Request instance via the method signature. + +2.2.0 +----- + + * added a new `uri_signer` service to help sign URIs + * deprecated `Symfony\Bundle\FrameworkBundle\HttpKernel::render()` and `Symfony\Bundle\FrameworkBundle\HttpKernel::forward()` + * deprecated the `Symfony\Bundle\FrameworkBundle\HttpKernel` class in favor of `Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel` + * added support for adding new HTTP content rendering strategies (like ESI and Hinclude) + in the DIC via the `kernel.fragment_renderer` tag + * [BC BREAK] restricted the `Symfony\Bundle\FrameworkBundle\HttpKernel::render()` method to only accept URIs or ControllerReference instances + * `Symfony\Bundle\FrameworkBundle\HttpKernel::render()` method signature changed and the first argument + must now be a URI or a ControllerReference instance (the `generateInternalUri()` method was removed) + * The internal routes (`Resources/config/routing/internal.xml`) have been removed and replaced with a listener (`Symfony\Component\HttpKernel\EventListener\FragmentListener`) + * The `render` method of the `actions` templating helper signature and arguments changed + * replaced Symfony\Bundle\FrameworkBundle\Controller\TraceableControllerResolver by Symfony\Component\HttpKernel\Controller\TraceableControllerResolver + * replaced Symfony\Component\HttpKernel\Debug\ContainerAwareTraceableEventDispatcher by Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher + * added Client::enableProfiler() + * a new parameter has been added to the DIC: `router.request_context.base_url` + You can customize it for your functional tests or for generating URLs with + the right base URL when your are in the CLI context. + * added support for default templates per render tag + +2.1.0 +----- + + * moved the translation files to the Form and Validator components + * changed the default extension for XLIFF files from .xliff to .xlf + * moved Symfony\Bundle\FrameworkBundle\ContainerAwareEventDispatcher to Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher + * moved Symfony\Bundle\FrameworkBundle\Debug\TraceableEventDispatcher to Symfony\Component\EventDispatcher\ContainerAwareTraceableEventDispatcher + * added a router:match command + * added a config:dump-reference command + * added a server:run command + * added kernel.event_subscriber tag + * added a way to create relative symlinks when running assets:install command (--relative option) + * added Controller::getUser() + * [BC BREAK] assets_base_urls and base_urls merging strategy has changed + * changed the default profiler storage to use the filesystem instead of SQLite + * added support for placeholders in route defaults and requirements (replaced + by the value set in the service container) + * added Filesystem component as a dependency + * added support for hinclude (use ``standalone: 'js'`` in render tag) + * session options: lifetime, path, domain, secure, httponly were deprecated. + Prefixed versions should now be used instead: cookie_lifetime, cookie_path, + cookie_domain, cookie_secure, cookie_httponly + * [BC BREAK] following session options: 'lifetime', 'path', 'domain', 'secure', + 'httponly' are now prefixed with cookie_ when dumped to the container + * Added `handler_id` configuration under `session` key to represent `session.handler` + service, defaults to `session.handler.native_file`. + * Added `gc_maxlifetime`, `gc_probability`, and `gc_divisor` to session + configuration. This means session garbage collection has a + `gc_probability`/`gc_divisor` chance of being run. The `gc_maxlifetime` defines + how long a session can idle for. It is different from cookie lifetime which + declares how long a cookie can be stored on the remote client. + * Removed 'auto_start' configuration parameter from session config. The session will + start on demand. + * [BC BREAK] TemplateNameParser::parseFromFilename() has been moved to a dedicated + parser: TemplateFilenameParser::parse(). + * [BC BREAK] Kernel parameters are replaced by their value wherever they appear + in Route patterns, requirements and defaults. Use '%%' as the escaped value for '%'. + * [BC BREAK] Switched behavior of flash messages to expire flash messages on retrieval + using Symfony\Component\HttpFoundation\Session\Flash\FlashBag as opposed to on + next pageload regardless of whether they are displayed or not. diff --git a/vendor/symfony/framework-bundle/CacheWarmer/AbstractPhpFileCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/AbstractPhpFileCacheWarmer.php new file mode 100644 index 0000000..25801a7 --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/AbstractPhpFileCacheWarmer.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\Bundle\FrameworkBundle\CacheWarmer; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +/** + * @internal + */ +abstract class AbstractPhpFileCacheWarmer implements CacheWarmerInterface +{ + private $phpArrayFile; + private $fallbackPool; + + /** + * @param string $phpArrayFile The PHP file where metadata are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached + */ + public function __construct($phpArrayFile, CacheItemPoolInterface $fallbackPool) + { + $this->phpArrayFile = $phpArrayFile; + if (!$fallbackPool instanceof AdapterInterface) { + $fallbackPool = new ProxyAdapter($fallbackPool); + } + $this->fallbackPool = $fallbackPool; + } + + /** + * {@inheritdoc} + */ + public function isOptional() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + $arrayAdapter = new ArrayAdapter(); + + spl_autoload_register(array(PhpArrayAdapter::class, 'throwOnRequiredClass')); + try { + if (!$this->doWarmUp($cacheDir, $arrayAdapter)) { + return; + } + } finally { + spl_autoload_unregister(array(PhpArrayAdapter::class, 'throwOnRequiredClass')); + } + + // the ArrayAdapter stores the values serialized + // to avoid mutation of the data after it was written to the cache + // so here we un-serialize the values first + $values = array_map(function ($val) { return null !== $val ? unserialize($val) : null; }, $arrayAdapter->getValues()); + + $this->warmUpPhpArrayAdapter(new PhpArrayAdapter($this->phpArrayFile, $this->fallbackPool), $values); + + foreach ($values as $k => $v) { + $item = $this->fallbackPool->getItem($k); + $this->fallbackPool->saveDeferred($item->set($v)); + } + $this->fallbackPool->commit(); + } + + protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values) + { + $phpArrayAdapter->warmUp($values); + } + + /** + * @param string $cacheDir + * @param ArrayAdapter $arrayAdapter + * + * @return bool false if there is nothing to warm-up + */ + abstract protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter); +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/AnnotationsCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/AnnotationsCacheWarmer.php new file mode 100644 index 0000000..3c32cb1 --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/AnnotationsCacheWarmer.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\Bundle\FrameworkBundle\CacheWarmer; + +use Doctrine\Common\Annotations\AnnotationException; +use Doctrine\Common\Annotations\CachedReader; +use Doctrine\Common\Annotations\Reader; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\DoctrineProvider; + +/** + * Warms up annotation caches for classes found in composer's autoload class map + * and declared in DI bundle extensions using the addAnnotatedClassesToCache method. + * + * @author Titouan Galopin + */ +class AnnotationsCacheWarmer extends AbstractPhpFileCacheWarmer +{ + private $annotationReader; + private $excludeRegexp; + private $debug; + + /** + * @param Reader $annotationReader + * @param string $phpArrayFile The PHP file where annotations are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered annotations are cached + * @param bool $debug Run in debug mode + */ + public function __construct(Reader $annotationReader, $phpArrayFile, CacheItemPoolInterface $fallbackPool, $excludeRegexp = null, $debug = false) + { + parent::__construct($phpArrayFile, $fallbackPool); + $this->annotationReader = $annotationReader; + $this->excludeRegexp = $excludeRegexp; + $this->debug = $debug; + } + + /** + * {@inheritdoc} + */ + protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter) + { + $annotatedClassPatterns = $cacheDir.'/annotations.map'; + + if (!is_file($annotatedClassPatterns)) { + return true; + } + + $annotatedClasses = include $annotatedClassPatterns; + $reader = new CachedReader($this->annotationReader, new DoctrineProvider($arrayAdapter), $this->debug); + + foreach ($annotatedClasses as $class) { + if (null !== $this->excludeRegexp && preg_match($this->excludeRegexp, $class)) { + continue; + } + try { + $this->readAllComponents($reader, $class); + } catch (\ReflectionException $e) { + // ignore failing reflection + } catch (AnnotationException $e) { + /* + * Ignore any AnnotationException to not break the cache warming process if an Annotation is badly + * configured or could not be found / read / etc. + * + * In particular cases, an Annotation in your code can be used and defined only for a specific + * environment but is always added to the annotations.map file by some Symfony default behaviors, + * and you always end up with a not found Annotation. + */ + } + } + + return true; + } + + private function readAllComponents(Reader $reader, $class) + { + $reflectionClass = new \ReflectionClass($class); + $reader->getClassAnnotations($reflectionClass); + + foreach ($reflectionClass->getMethods() as $reflectionMethod) { + $reader->getMethodAnnotations($reflectionMethod); + } + + foreach ($reflectionClass->getProperties() as $reflectionProperty) { + $reader->getPropertyAnnotations($reflectionProperty); + } + } +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/ClassCacheCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/ClassCacheCacheWarmer.php new file mode 100644 index 0000000..058d22b --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/ClassCacheCacheWarmer.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\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\ClassLoader\ClassCollectionLoader; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +/** + * Generates the Class Cache (classes.php) file. + * + * @author Tugdual Saunier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class ClassCacheCacheWarmer implements CacheWarmerInterface +{ + private $declaredClasses; + + public function __construct(array $declaredClasses = null) + { + if (\PHP_VERSION_ID >= 70000) { + @trigger_error('The '.__CLASS__.' class is deprecated since Symfony 3.3 and will be removed in 4.0.', E_USER_DEPRECATED); + } + + $this->declaredClasses = $declaredClasses; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + $classmap = $cacheDir.'/classes.map'; + + if (!is_file($classmap)) { + return; + } + + if (file_exists($cacheDir.'/classes.php')) { + return; + } + $declared = null !== $this->declaredClasses ? $this->declaredClasses : array_merge(get_declared_classes(), get_declared_interfaces(), get_declared_traits()); + + ClassCollectionLoader::inline(include($classmap), $cacheDir.'/classes.php', $declared); + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always true + */ + public function isOptional() + { + return true; + } +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/RouterCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/RouterCacheWarmer.php new file mode 100644 index 0000000..795fdbf --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/RouterCacheWarmer.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Routing\RouterInterface; + +/** + * Generates the router matcher and generator classes. + * + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class RouterCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface +{ + protected $router; + + /** + * @param ContainerInterface $container + */ + public function __construct($container) + { + // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. + if ($container instanceof ContainerInterface) { + $this->router = $container->get('router'); // For BC, the $router property must be populated in the constructor + } elseif ($container instanceof RouterInterface) { + $this->router = $container; + @trigger_error(sprintf('Using a "%s" as first argument of %s is deprecated since Symfony 3.4 and will be unsupported in version 4.0. Use a %s instead.', RouterInterface::class, __CLASS__, ContainerInterface::class), E_USER_DEPRECATED); + } else { + throw new \InvalidArgumentException(sprintf('%s only accepts instance of Psr\Container\ContainerInterface as first argument.', __CLASS__)); + } + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + if ($this->router instanceof WarmableInterface) { + $this->router->warmUp($cacheDir); + } + } + + /** + * Checks whether this warmer is optional or not. + * + * @return bool always true + */ + public function isOptional() + { + return true; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'router' => RouterInterface::class, + ); + } +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/SerializerCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/SerializerCacheWarmer.php new file mode 100644 index 0000000..22d2bcf --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/SerializerCacheWarmer.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\Bundle\FrameworkBundle\CacheWarmer; + +use Doctrine\Common\Annotations\AnnotationException; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\Serializer\Mapping\Loader\LoaderChain; +use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface; +use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; +use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; + +/** + * Warms up XML and YAML serializer metadata. + * + * @author Titouan Galopin + */ +class SerializerCacheWarmer extends AbstractPhpFileCacheWarmer +{ + private $loaders; + + /** + * @param LoaderInterface[] $loaders The serializer metadata loaders + * @param string $phpArrayFile The PHP file where metadata are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached + */ + public function __construct(array $loaders, $phpArrayFile, CacheItemPoolInterface $fallbackPool) + { + parent::__construct($phpArrayFile, $fallbackPool); + $this->loaders = $loaders; + } + + /** + * {@inheritdoc} + */ + protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter) + { + if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) { + return false; + } + + $metadataFactory = new CacheClassMetadataFactory(new ClassMetadataFactory(new LoaderChain($this->loaders)), $arrayAdapter); + + foreach ($this->extractSupportedLoaders($this->loaders) as $loader) { + foreach ($loader->getMappedClasses() as $mappedClass) { + try { + $metadataFactory->getMetadataFor($mappedClass); + } catch (\ReflectionException $e) { + // ignore failing reflection + } catch (AnnotationException $e) { + // ignore failing annotations + } + } + } + + return true; + } + + /** + * @param LoaderInterface[] $loaders + * + * @return XmlFileLoader[]|YamlFileLoader[] + */ + private function extractSupportedLoaders(array $loaders) + { + $supportedLoaders = array(); + + foreach ($loaders as $loader) { + if ($loader instanceof XmlFileLoader || $loader instanceof YamlFileLoader) { + $supportedLoaders[] = $loader; + } elseif ($loader instanceof LoaderChain) { + $supportedLoaders = array_merge($supportedLoaders, $this->extractSupportedLoaders($loader->getLoaders())); + } + } + + return $supportedLoaders; + } +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/TemplateFinder.php b/vendor/symfony/framework-bundle/CacheWarmer/TemplateFinder.php new file mode 100644 index 0000000..4ceca5c --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/TemplateFinder.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\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * Finds all the templates. + * + * @author Victor Berchet + */ +class TemplateFinder implements TemplateFinderInterface +{ + private $kernel; + private $parser; + private $rootDir; + private $templates; + + /** + * @param KernelInterface $kernel A KernelInterface instance + * @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance + * @param string $rootDir The directory where global templates can be stored + */ + public function __construct(KernelInterface $kernel, TemplateNameParserInterface $parser, $rootDir) + { + $this->kernel = $kernel; + $this->parser = $parser; + $this->rootDir = $rootDir; + } + + /** + * Find all the templates in the bundle and in the kernel Resources folder. + * + * @return TemplateReferenceInterface[] + */ + public function findAllTemplates() + { + if (null !== $this->templates) { + return $this->templates; + } + + $templates = array(); + + foreach ($this->kernel->getBundles() as $bundle) { + $templates = array_merge($templates, $this->findTemplatesInBundle($bundle)); + } + + $templates = array_merge($templates, $this->findTemplatesInFolder($this->rootDir.'/views')); + + return $this->templates = $templates; + } + + /** + * Find templates in the given directory. + * + * @param string $dir The folder where to look for templates + * + * @return TemplateReferenceInterface[] + */ + private function findTemplatesInFolder($dir) + { + $templates = array(); + + if (is_dir($dir)) { + $finder = new Finder(); + foreach ($finder->files()->followLinks()->in($dir) as $file) { + $template = $this->parser->parse($file->getRelativePathname()); + if (false !== $template) { + $templates[] = $template; + } + } + } + + return $templates; + } + + /** + * Find templates in the given bundle. + * + * @param BundleInterface $bundle The bundle where to look for templates + * + * @return TemplateReferenceInterface[] + */ + private function findTemplatesInBundle(BundleInterface $bundle) + { + $name = $bundle->getName(); + $templates = array_unique(array_merge( + $this->findTemplatesInFolder($bundle->getPath().'/Resources/views'), + $this->findTemplatesInFolder($this->rootDir.'/'.$name.'/views') + )); + + foreach ($templates as $i => $template) { + $templates[$i] = $template->set('bundle', $name); + } + + return $templates; + } +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/TemplateFinderInterface.php b/vendor/symfony/framework-bundle/CacheWarmer/TemplateFinderInterface.php new file mode 100644 index 0000000..433ed8f --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/TemplateFinderInterface.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\Bundle\FrameworkBundle\CacheWarmer; + +/** + * Interface for finding all the templates. + * + * @author Victor Berchet + */ +interface TemplateFinderInterface +{ + /** + * Find all the templates. + * + * @return array An array of templates of type TemplateReferenceInterface + */ + public function findAllTemplates(); +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/TemplatePathsCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/TemplatePathsCacheWarmer.php new file mode 100644 index 0000000..523d7ec --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/TemplatePathsCacheWarmer.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmer; + +/** + * Computes the association between template names and their paths on the disk. + * + * @author Fabien Potencier + */ +class TemplatePathsCacheWarmer extends CacheWarmer +{ + protected $finder; + protected $locator; + + public function __construct(TemplateFinderInterface $finder, TemplateLocator $locator) + { + $this->finder = $finder; + $this->locator = $locator; + } + + /** + * Warms up the cache. + * + * @param string $cacheDir The cache directory + */ + public function warmUp($cacheDir) + { + $filesystem = new Filesystem(); + $templates = array(); + + foreach ($this->finder->findAllTemplates() as $template) { + $templates[$template->getLogicalName()] = rtrim($filesystem->makePathRelative($this->locator->locate($template), $cacheDir), '/'); + } + + $templates = str_replace("' => '", "' => __DIR__.'/", var_export($templates, true)); + + $this->writeCacheFile($cacheDir.'/templates.php', sprintf(" + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * Generates the catalogues for translations. + * + * @author Xavier Leune + */ +class TranslationsCacheWarmer implements CacheWarmerInterface, ServiceSubscriberInterface +{ + private $container; + private $translator; + + /** + * TranslationsCacheWarmer constructor. + * + * @param ContainerInterface $container + */ + public function __construct($container) + { + // As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected. + if ($container instanceof ContainerInterface) { + $this->container = $container; + } elseif ($container instanceof TranslatorInterface) { + $this->translator = $container; + @trigger_error(sprintf('Using a "%s" as first argument of %s is deprecated since Symfony 3.4 and will be unsupported in version 4.0. Use a %s instead.', TranslatorInterface::class, __CLASS__, ContainerInterface::class), E_USER_DEPRECATED); + } else { + throw new \InvalidArgumentException(sprintf('%s only accepts instance of Psr\Container\ContainerInterface as first argument.', __CLASS__)); + } + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + if (null === $this->translator) { + $this->translator = $this->container->get('translator'); + } + + if ($this->translator instanceof WarmableInterface) { + $this->translator->warmUp($cacheDir); + } + } + + /** + * {@inheritdoc} + */ + public function isOptional() + { + return true; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'translator' => TranslatorInterface::class, + ); + } +} diff --git a/vendor/symfony/framework-bundle/CacheWarmer/ValidatorCacheWarmer.php b/vendor/symfony/framework-bundle/CacheWarmer/ValidatorCacheWarmer.php new file mode 100644 index 0000000..2fb4ace --- /dev/null +++ b/vendor/symfony/framework-bundle/CacheWarmer/ValidatorCacheWarmer.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\CacheWarmer; + +use Doctrine\Common\Annotations\AnnotationException; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; +use Symfony\Component\Validator\Mapping\Cache\Psr6Cache; +use Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory; +use Symfony\Component\Validator\Mapping\Loader\LoaderChain; +use Symfony\Component\Validator\Mapping\Loader\LoaderInterface; +use Symfony\Component\Validator\Mapping\Loader\XmlFileLoader; +use Symfony\Component\Validator\Mapping\Loader\YamlFileLoader; +use Symfony\Component\Validator\ValidatorBuilderInterface; + +/** + * Warms up XML and YAML validator metadata. + * + * @author Titouan Galopin + */ +class ValidatorCacheWarmer extends AbstractPhpFileCacheWarmer +{ + private $validatorBuilder; + + /** + * @param ValidatorBuilderInterface $validatorBuilder + * @param string $phpArrayFile The PHP file where metadata are cached + * @param CacheItemPoolInterface $fallbackPool The pool where runtime-discovered metadata are cached + */ + public function __construct(ValidatorBuilderInterface $validatorBuilder, $phpArrayFile, CacheItemPoolInterface $fallbackPool) + { + parent::__construct($phpArrayFile, $fallbackPool); + $this->validatorBuilder = $validatorBuilder; + } + + /** + * {@inheritdoc} + */ + protected function doWarmUp($cacheDir, ArrayAdapter $arrayAdapter) + { + if (!method_exists($this->validatorBuilder, 'getLoaders')) { + return false; + } + + $loaders = $this->validatorBuilder->getLoaders(); + $metadataFactory = new LazyLoadingMetadataFactory(new LoaderChain($loaders), new Psr6Cache($arrayAdapter)); + + foreach ($this->extractSupportedLoaders($loaders) as $loader) { + foreach ($loader->getMappedClasses() as $mappedClass) { + try { + if ($metadataFactory->hasMetadataFor($mappedClass)) { + $metadataFactory->getMetadataFor($mappedClass); + } + } catch (\ReflectionException $e) { + // ignore failing reflection + } catch (AnnotationException $e) { + // ignore failing annotations + } + } + } + + return true; + } + + protected function warmUpPhpArrayAdapter(PhpArrayAdapter $phpArrayAdapter, array $values) + { + // make sure we don't cache null values + parent::warmUpPhpArrayAdapter($phpArrayAdapter, array_filter($values)); + } + + /** + * @param LoaderInterface[] $loaders + * + * @return XmlFileLoader[]|YamlFileLoader[] + */ + private function extractSupportedLoaders(array $loaders) + { + $supportedLoaders = array(); + + foreach ($loaders as $loader) { + if ($loader instanceof XmlFileLoader || $loader instanceof YamlFileLoader) { + $supportedLoaders[] = $loader; + } elseif ($loader instanceof LoaderChain) { + $supportedLoaders = array_merge($supportedLoaders, $this->extractSupportedLoaders($loader->getLoaders())); + } + } + + return $supportedLoaders; + } +} diff --git a/vendor/symfony/framework-bundle/Client.php b/vendor/symfony/framework-bundle/Client.php new file mode 100644 index 0000000..c86a277 --- /dev/null +++ b/vendor/symfony/framework-bundle/Client.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\Bundle\FrameworkBundle; + +use Symfony\Component\BrowserKit\CookieJar; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Client as BaseClient; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Profiler\Profile as HttpProfile; + +/** + * Client simulates a browser and makes requests to a Kernel object. + * + * @author Fabien Potencier + */ +class Client extends BaseClient +{ + private $hasPerformedRequest = false; + private $profiler = false; + private $reboot = true; + + /** + * {@inheritdoc} + */ + public function __construct(KernelInterface $kernel, array $server = array(), History $history = null, CookieJar $cookieJar = null) + { + parent::__construct($kernel, $server, $history, $cookieJar); + } + + /** + * Returns the container. + * + * @return ContainerInterface|null Returns null when the Kernel has been shutdown or not started yet + */ + public function getContainer() + { + return $this->kernel->getContainer(); + } + + /** + * Returns the kernel. + * + * @return KernelInterface + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Gets the profile associated with the current Response. + * + * @return HttpProfile|false A Profile instance + */ + public function getProfile() + { + if (!$this->kernel->getContainer()->has('profiler')) { + return false; + } + + return $this->kernel->getContainer()->get('profiler')->loadProfileFromResponse($this->response); + } + + /** + * Enables the profiler for the very next request. + * + * If the profiler is not enabled, the call to this method does nothing. + */ + public function enableProfiler() + { + if ($this->kernel->getContainer()->has('profiler')) { + $this->profiler = true; + } + } + + /** + * Disables kernel reboot between requests. + * + * By default, the Client reboots the Kernel for each request. This method + * allows to keep the same kernel across requests. + */ + public function disableReboot() + { + $this->reboot = false; + } + + /** + * Enables kernel reboot between requests. + */ + public function enableReboot() + { + $this->reboot = true; + } + + /** + * {@inheritdoc} + * + * @param Request $request A Request instance + * + * @return Response A Response instance + */ + protected function doRequest($request) + { + // avoid shutting down the Kernel if no request has been performed yet + // WebTestCase::createClient() boots the Kernel but do not handle a request + if ($this->hasPerformedRequest && $this->reboot) { + $this->kernel->shutdown(); + } else { + $this->hasPerformedRequest = true; + } + + if ($this->profiler) { + $this->profiler = false; + + $this->kernel->boot(); + $this->kernel->getContainer()->get('profiler')->enable(); + } + + return parent::doRequest($request); + } + + /** + * {@inheritdoc} + * + * @param Request $request A Request instance + * + * @return Response A Response instance + */ + protected function doRequestInProcess($request) + { + $response = parent::doRequestInProcess($request); + + $this->profiler = false; + + return $response; + } + + /** + * Returns the script to execute when the request must be insulated. + * + * It assumes that the autoloader is named 'autoload.php' and that it is + * stored in the same directory as the kernel (this is the case for the + * Symfony Standard Edition). If this is not your case, create your own + * client and override this method. + * + * @param Request $request A Request instance + * + * @return string The script content + */ + protected function getScript($request) + { + $kernel = var_export(serialize($this->kernel), true); + $request = var_export(serialize($request), true); + $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 '.var_export($file, true).";\n"; + } + } + } + + if (!$requires) { + throw new \RuntimeException('Composer autoloader not found.'); + } + + $requires .= 'require_once '.var_export((new \ReflectionObject($this->kernel))->getFileName(), true).";\n"; + + $profilerCode = ''; + if ($this->profiler) { + $profilerCode = '$kernel->getContainer()->get(\'profiler\')->enable();'; + } + + $code = <<boot(); +$profilerCode + +\$request = unserialize($request); +EOF; + + return $code.$this->getHandleScript(); + } +} diff --git a/vendor/symfony/framework-bundle/Command/AboutCommand.php b/vendor/symfony/framework-bundle/Command/AboutCommand.php new file mode 100644 index 0000000..05b3230 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/AboutCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Helper\Helper; +use Symfony\Component\Console\Helper\TableSeparator; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * A console command to display information about the current installation. + * + * @author Roland Franssen + * + * @final since version 3.4 + */ +class AboutCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'about'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDescription('Displays information about the current project') + ->setHelp(<<<'EOT' +The %command.name% command displays information about the current Symfony project. + +The PHP section displays important configuration that could affect your application. The values might +be different between web and CLI. + +The Environment section displays the current environment variables managed by Symfony Dotenv. It will not +be shown if no variables were found. The values might be different between web and CLI. +EOT + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + /** @var $kernel KernelInterface */ + $kernel = $this->getApplication()->getKernel(); + + $rows = array( + array('Symfony'), + new TableSeparator(), + array('Version', Kernel::VERSION), + array('End of maintenance', Kernel::END_OF_MAINTENANCE.(self::isExpired(Kernel::END_OF_MAINTENANCE) ? ' Expired' : '')), + array('End of life', Kernel::END_OF_LIFE.(self::isExpired(Kernel::END_OF_LIFE) ? ' Expired' : '')), + new TableSeparator(), + array('Kernel'), + new TableSeparator(), + array('Type', \get_class($kernel)), + array('Name', $kernel->getName()), + array('Environment', $kernel->getEnvironment()), + array('Debug', $kernel->isDebug() ? 'true' : 'false'), + array('Charset', $kernel->getCharset()), + array('Root directory', self::formatPath($kernel->getRootDir(), $kernel->getProjectDir())), + array('Cache directory', self::formatPath($kernel->getCacheDir(), $kernel->getProjectDir()).' ('.self::formatFileSize($kernel->getCacheDir()).')'), + array('Log directory', self::formatPath($kernel->getLogDir(), $kernel->getProjectDir()).' ('.self::formatFileSize($kernel->getLogDir()).')'), + new TableSeparator(), + array('PHP'), + new TableSeparator(), + array('Version', PHP_VERSION), + array('Architecture', (PHP_INT_SIZE * 8).' bits'), + array('Intl locale', class_exists('Locale', false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a'), + array('Timezone', date_default_timezone_get().' ('.(new \DateTime())->format(\DateTime::W3C).')'), + array('OPcache', \extension_loaded('Zend OPcache') && ini_get('opcache.enable') ? 'true' : 'false'), + array('APCu', \extension_loaded('apcu') && ini_get('apc.enabled') ? 'true' : 'false'), + array('Xdebug', \extension_loaded('xdebug') ? 'true' : 'false'), + ); + + if ($dotenv = self::getDotenvVars()) { + $rows = array_merge($rows, array( + new TableSeparator(), + array('Environment (.env)'), + new TableSeparator(), + ), array_map(function ($value, $name) { + return array($name, $value); + }, $dotenv, array_keys($dotenv))); + } + + $io->table(array(), $rows); + } + + private static function formatPath($path, $baseDir = null) + { + return null !== $baseDir ? preg_replace('~^'.preg_quote($baseDir, '~').'~', '.', $path) : $path; + } + + private static function formatFileSize($path) + { + if (is_file($path)) { + $size = filesize($path) ?: 0; + } else { + $size = 0; + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \RecursiveDirectoryIterator::FOLLOW_SYMLINKS)) as $file) { + $size += $file->getSize(); + } + } + + return Helper::formatMemory($size); + } + + private static function isExpired($date) + { + $date = \DateTime::createFromFormat('m/Y', $date); + + return false !== $date && new \DateTime() > $date->modify('last day of this month 23:59:59'); + } + + private static function getDotenvVars() + { + $vars = array(); + foreach (explode(',', getenv('SYMFONY_DOTENV_VARS')) as $name) { + if ('' !== $name && false !== $value = getenv($name)) { + $vars[$name] = $value; + } + } + + return $vars; + } +} diff --git a/vendor/symfony/framework-bundle/Command/AbstractConfigCommand.php b/vendor/symfony/framework-bundle/Command/AbstractConfigCommand.php new file mode 100644 index 0000000..6d8960c --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/AbstractConfigCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Style\StyleInterface; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * A console command for dumping available configuration reference. + * + * @author Kevin Bond + * @author Wouter J + * @author Grégoire Pineau + */ +abstract class AbstractConfigCommand extends ContainerDebugCommand +{ + protected function listBundles($output) + { + $title = 'Available registered bundles with their extension alias if available'; + $headers = array('Bundle name', 'Extension alias'); + $rows = array(); + + $bundles = $this->getApplication()->getKernel()->getBundles(); + usort($bundles, function ($bundleA, $bundleB) { + return strcmp($bundleA->getName(), $bundleB->getName()); + }); + + foreach ($bundles as $bundle) { + $extension = $bundle->getContainerExtension(); + $rows[] = array($bundle->getName(), $extension ? $extension->getAlias() : ''); + } + + if ($output instanceof StyleInterface) { + $output->title($title); + $output->table($headers, $rows); + } else { + $output->writeln($title); + $table = new Table($output); + $table->setHeaders($headers)->setRows($rows)->render(); + } + } + + protected function findExtension($name) + { + $bundles = $this->initializeBundles(); + $minScore = INF; + + foreach ($bundles as $bundle) { + if ($name === $bundle->getName()) { + if (!$bundle->getContainerExtension()) { + throw new \LogicException(sprintf('Bundle "%s" does not have a container extension.', $name)); + } + + return $bundle->getContainerExtension(); + } + + $distance = levenshtein($name, $bundle->getName()); + + if ($distance < $minScore) { + $guess = $bundle->getName(); + $minScore = $distance; + } + + $extension = $bundle->getContainerExtension(); + + if ($extension) { + if ($name === $extension->getAlias()) { + return $extension; + } + + $distance = levenshtein($name, $extension->getAlias()); + + if ($distance < $minScore) { + $guess = $extension->getAlias(); + $minScore = $distance; + } + } + } + + if ('Bundle' !== substr($name, -6)) { + $message = sprintf('No extensions with configuration available for "%s".', $name); + } else { + $message = sprintf('No extension with alias "%s" is enabled.', $name); + } + + if (isset($guess) && $minScore < 3) { + $message .= sprintf("\n\nDid you mean \"%s\"?", $guess); + } + + throw new LogicException($message); + } + + public function validateConfiguration(ExtensionInterface $extension, $configuration) + { + if (!$configuration) { + throw new \LogicException(sprintf('The extension with alias "%s" does not have its getConfiguration() method setup', $extension->getAlias())); + } + + if (!$configuration instanceof ConfigurationInterface) { + throw new \LogicException(sprintf('Configuration class "%s" should implement ConfigurationInterface in order to be dumpable', \get_class($configuration))); + } + } + + private function initializeBundles() + { + // Re-build bundle manually to initialize DI extensions that can be extended by other bundles in their build() method + // as this method is not called when the container is loaded from the cache. + $container = $this->getContainerBuilder(); + $bundles = $this->getApplication()->getKernel()->getBundles(); + foreach ($bundles as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $container->registerExtension($extension); + } + } + + foreach ($bundles as $bundle) { + $bundle->build($container); + } + + return $bundles; + } +} diff --git a/vendor/symfony/framework-bundle/Command/AssetsInstallCommand.php b/vendor/symfony/framework-bundle/Command/AssetsInstallCommand.php new file mode 100644 index 0000000..a52fe67 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/AssetsInstallCommand.php @@ -0,0 +1,291 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * Command that places bundle web assets into a given directory. + * + * @author Fabien Potencier + * @author Gábor Egyed + * + * @final since version 3.4 + */ +class AssetsInstallCommand extends ContainerAwareCommand +{ + const METHOD_COPY = 'copy'; + const METHOD_ABSOLUTE_SYMLINK = 'absolute symlink'; + const METHOD_RELATIVE_SYMLINK = 'relative symlink'; + + protected static $defaultName = 'assets:install'; + + private $filesystem; + + /** + * @param Filesystem $filesystem + */ + public function __construct($filesystem = null) + { + if (!$filesystem instanceof Filesystem) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, Filesystem::class), E_USER_DEPRECATED); + + parent::__construct($filesystem); + + return; + } + + parent::__construct(); + + $this->filesystem = $filesystem; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('target', InputArgument::OPTIONAL, 'The target directory', 'public'), + )) + ->addOption('symlink', null, InputOption::VALUE_NONE, 'Symlinks the assets instead of copying it') + ->addOption('relative', null, InputOption::VALUE_NONE, 'Make relative symlinks') + ->setDescription('Installs bundles web assets under a public directory') + ->setHelp(<<<'EOT' +The %command.name% command installs bundle assets into a given +directory (e.g. the public directory). + + php %command.full_name% public + +A "bundles" directory will be created inside the target directory and the +"Resources/public" directory of each bundle will be copied into it. + +To create a symlink to each bundle instead of copying its assets, use the +--symlink option (will fall back to hard copies when symbolic links aren't possible: + + php %command.full_name% public --symlink + +To make symlink relative, add the --relative option: + + php %command.full_name% public --symlink --relative + +EOT + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->filesystem) { + $this->filesystem = $this->getContainer()->get('filesystem'); + $baseDir = $this->getContainer()->getParameter('kernel.project_dir'); + } + + $kernel = $this->getApplication()->getKernel(); + $targetArg = rtrim($input->getArgument('target'), '/'); + + if (!is_dir($targetArg)) { + $targetArg = (isset($baseDir) ? $baseDir : $kernel->getContainer()->getParameter('kernel.project_dir')).'/'.$targetArg; + + if (!is_dir($targetArg)) { + // deprecated, logic to be removed in 4.0 + // this allows the commands to work out of the box with web/ and public/ + if (is_dir(\dirname($targetArg).'/web')) { + $targetArg = \dirname($targetArg).'/web'; + } else { + throw new InvalidArgumentException(sprintf('The target directory "%s" does not exist.', $input->getArgument('target'))); + } + } + } + + $bundlesDir = $targetArg.'/bundles/'; + + $io = new SymfonyStyle($input, $output); + $io->newLine(); + + if ($input->getOption('relative')) { + $expectedMethod = self::METHOD_RELATIVE_SYMLINK; + $io->text('Trying to install assets as relative symbolic links.'); + } elseif ($input->getOption('symlink')) { + $expectedMethod = self::METHOD_ABSOLUTE_SYMLINK; + $io->text('Trying to install assets as absolute symbolic links.'); + } else { + $expectedMethod = self::METHOD_COPY; + $io->text('Installing assets as hard copies.'); + } + + $io->newLine(); + + $rows = array(); + $copyUsed = false; + $exitCode = 0; + $validAssetDirs = array(); + /** @var BundleInterface $bundle */ + foreach ($kernel->getBundles() as $bundle) { + if (!is_dir($originDir = $bundle->getPath().'/Resources/public')) { + continue; + } + + $assetDir = preg_replace('/bundle$/', '', strtolower($bundle->getName())); + $targetDir = $bundlesDir.$assetDir; + $validAssetDirs[] = $assetDir; + + if (OutputInterface::VERBOSITY_VERBOSE <= $output->getVerbosity()) { + $message = sprintf("%s\n-> %s", $bundle->getName(), $targetDir); + } else { + $message = $bundle->getName(); + } + + try { + $this->filesystem->remove($targetDir); + + if (self::METHOD_RELATIVE_SYMLINK === $expectedMethod) { + $method = $this->relativeSymlinkWithFallback($originDir, $targetDir); + } elseif (self::METHOD_ABSOLUTE_SYMLINK === $expectedMethod) { + $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir); + } else { + $method = $this->hardCopy($originDir, $targetDir); + } + + if (self::METHOD_COPY === $method) { + $copyUsed = true; + } + + if ($method === $expectedMethod) { + $rows[] = array(sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'OK' : "\xE2\x9C\x94" /* HEAVY CHECK MARK (U+2714) */), $message, $method); + } else { + $rows[] = array(sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'WARNING' : '!'), $message, $method); + } + } catch (\Exception $e) { + $exitCode = 1; + $rows[] = array(sprintf('%s', '\\' === \DIRECTORY_SEPARATOR ? 'ERROR' : "\xE2\x9C\x98" /* HEAVY BALLOT X (U+2718) */), $message, $e->getMessage()); + } + } + // remove the assets of the bundles that no longer exist + if (is_dir($bundlesDir)) { + $dirsToRemove = Finder::create()->depth(0)->directories()->exclude($validAssetDirs)->in($bundlesDir); + $this->filesystem->remove($dirsToRemove); + } + + if ($rows) { + $io->table(array('', 'Bundle', 'Method / Error'), $rows); + } + + if (0 !== $exitCode) { + $io->error('Some errors occurred while installing assets.'); + } else { + if ($copyUsed) { + $io->note('Some assets were installed via copy. If you make changes to these assets you have to run this command again.'); + } + $io->success($rows ? 'All assets were successfully installed.' : 'No assets were provided by any bundle.'); + } + + return $exitCode; + } + + /** + * Try to create relative symlink. + * + * Falling back to absolute symlink and finally hard copy. + * + * @param string $originDir + * @param string $targetDir + * + * @return string + */ + private function relativeSymlinkWithFallback($originDir, $targetDir) + { + try { + $this->symlink($originDir, $targetDir, true); + $method = self::METHOD_RELATIVE_SYMLINK; + } catch (IOException $e) { + $method = $this->absoluteSymlinkWithFallback($originDir, $targetDir); + } + + return $method; + } + + /** + * Try to create absolute symlink. + * + * Falling back to hard copy. + * + * @param string $originDir + * @param string $targetDir + * + * @return string + */ + private function absoluteSymlinkWithFallback($originDir, $targetDir) + { + try { + $this->symlink($originDir, $targetDir); + $method = self::METHOD_ABSOLUTE_SYMLINK; + } catch (IOException $e) { + // fall back to copy + $method = $this->hardCopy($originDir, $targetDir); + } + + return $method; + } + + /** + * Creates symbolic link. + * + * @param string $originDir + * @param string $targetDir + * @param bool $relative + * + * @throws IOException if link can not be created + */ + private function symlink($originDir, $targetDir, $relative = false) + { + if ($relative) { + $this->filesystem->mkdir(\dirname($targetDir)); + $originDir = $this->filesystem->makePathRelative($originDir, realpath(\dirname($targetDir))); + } + $this->filesystem->symlink($originDir, $targetDir); + if (!file_exists($targetDir)) { + throw new IOException(sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir); + } + } + + /** + * Copies origin to target. + * + * @param string $originDir + * @param string $targetDir + * + * @return string + */ + private function hardCopy($originDir, $targetDir) + { + $this->filesystem->mkdir($targetDir, 0777); + // We use a custom iterator to ignore VCS files + $this->filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir)); + + return self::METHOD_COPY; + } +} diff --git a/vendor/symfony/framework-bundle/Command/CacheClearCommand.php b/vendor/symfony/framework-bundle/Command/CacheClearCommand.php new file mode 100644 index 0000000..6c5f0e1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/CacheClearCommand.php @@ -0,0 +1,362 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\RebootableInterface; + +/** + * Clear and Warmup the cache. + * + * @author Francis Besset + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class CacheClearCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'cache:clear'; + + private $cacheClearer; + private $filesystem; + private $warning; + + /** + * @param CacheClearerInterface $cacheClearer + * @param Filesystem|null $filesystem + */ + public function __construct($cacheClearer = null, Filesystem $filesystem = null) + { + if (!$cacheClearer instanceof CacheClearerInterface) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, CacheClearerInterface::class), E_USER_DEPRECATED); + + parent::__construct($cacheClearer); + + return; + } + + parent::__construct(); + + $this->cacheClearer = $cacheClearer; + $this->filesystem = $filesystem ?: new Filesystem(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputOption('no-warmup', '', InputOption::VALUE_NONE, 'Do not warm up the cache'), + new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'), + )) + ->setDescription('Clears the cache') + ->setHelp(<<<'EOF' +The %command.name% command clears the application cache for a given environment +and debug mode: + + php %command.full_name% --env=dev + php %command.full_name% --env=prod --no-debug +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->cacheClearer) { + $this->cacheClearer = $this->getContainer()->get('cache_clearer'); + $this->filesystem = $this->getContainer()->get('filesystem'); + $realCacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); + } + + $fs = $this->filesystem; + $io = new SymfonyStyle($input, $output); + + $kernel = $this->getApplication()->getKernel(); + $realCacheDir = isset($realCacheDir) ? $realCacheDir : $kernel->getContainer()->getParameter('kernel.cache_dir'); + // the old cache dir name must not be longer than the real one to avoid exceeding + // the maximum length of a directory or file path within it (esp. Windows MAX_PATH) + $oldCacheDir = substr($realCacheDir, 0, -1).('~' === substr($realCacheDir, -1) ? '+' : '~'); + $fs->remove($oldCacheDir); + + if (!is_writable($realCacheDir)) { + throw new RuntimeException(sprintf('Unable to write in the "%s" directory', $realCacheDir)); + } + + $io->comment(sprintf('Clearing the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + $this->cacheClearer->clear($realCacheDir); + + // The current event dispatcher is stale, let's not use it anymore + $this->getApplication()->setDispatcher(new EventDispatcher()); + + $containerDir = new \ReflectionObject($kernel->getContainer()); + $containerDir = basename(\dirname($containerDir->getFileName())); + + // the warmup cache dir name must have the same length as the real one + // to avoid the many problems in serialized resources files + $warmupDir = substr($realCacheDir, 0, -1).('_' === substr($realCacheDir, -1) ? '-' : '_'); + + if ($output->isVerbose() && $fs->exists($warmupDir)) { + $io->comment('Clearing outdated warmup directory...'); + } + $fs->remove($warmupDir); + $fs->mkdir($warmupDir); + + if (!$input->getOption('no-warmup')) { + if ($output->isVerbose()) { + $io->comment('Warming up cache...'); + } + $this->warmup($warmupDir, $realCacheDir, !$input->getOption('no-optional-warmers')); + + if ($this->warning) { + @trigger_error($this->warning, E_USER_DEPRECATED); + $io->warning($this->warning); + $this->warning = null; + } + } + + if (!$fs->exists($warmupDir.'/'.$containerDir)) { + $fs->rename($realCacheDir.'/'.$containerDir, $warmupDir.'/'.$containerDir); + touch($warmupDir.'/'.$containerDir.'.legacy'); + } + + if ('/' === \DIRECTORY_SEPARATOR && $mounts = @file('/proc/mounts')) { + foreach ($mounts as $mount) { + $mount = \array_slice(explode(' ', $mount), 1, -3); + if (!\in_array(array_pop($mount), array('vboxsf', 'nfs'))) { + continue; + } + $mount = implode(' ', $mount).'/'; + + if (0 === strpos($realCacheDir, $mount)) { + $io->note('For better performances, you should move the cache and log directories to a non-shared folder of the VM.'); + $oldCacheDir = false; + break; + } + } + } + + if ($oldCacheDir) { + $fs->rename($realCacheDir, $oldCacheDir); + } else { + $fs->remove($realCacheDir); + } + $fs->rename($warmupDir, $realCacheDir); + + if ($output->isVerbose()) { + $io->comment('Removing old cache directory...'); + } + + try { + $fs->remove($oldCacheDir); + } catch (IOException $e) { + if ($output->isVerbose()) { + $io->warning($e->getMessage()); + } + } + + if ($output->isVerbose()) { + $io->comment('Finished'); + } + + $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully cleared.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + } + + /** + * @param string $warmupDir + * @param string $realCacheDir + * @param bool $enableOptionalWarmers + */ + protected function warmup($warmupDir, $realCacheDir, $enableOptionalWarmers = true) + { + // create a temporary kernel + $realKernel = $this->getApplication()->getKernel(); + if ($realKernel instanceof RebootableInterface) { + $realKernel->reboot($warmupDir); + $tempKernel = $realKernel; + } else { + $this->warning = 'Calling "cache:clear" with a kernel that does not implement "Symfony\Component\HttpKernel\RebootableInterface" is deprecated since Symfony 3.4 and will be unsupported in 4.0.'; + $realKernelClass = \get_class($realKernel); + $namespace = ''; + if (false !== $pos = strrpos($realKernelClass, '\\')) { + $namespace = substr($realKernelClass, 0, $pos); + $realKernelClass = substr($realKernelClass, $pos + 1); + } + $tempKernel = $this->getTempKernel($realKernel, $namespace, $realKernelClass, $warmupDir); + $tempKernel->boot(); + + $tempKernelReflection = new \ReflectionObject($tempKernel); + $tempKernelFile = $tempKernelReflection->getFileName(); + } + + // warmup temporary dir + $warmer = $tempKernel->getContainer()->get('cache_warmer'); + if ($enableOptionalWarmers) { + $warmer->enableOptionalWarmers(); + } + $warmer->warmUp($warmupDir); + + // fix references to cached files with the real cache directory name + $search = array($warmupDir, str_replace('\\', '\\\\', $warmupDir)); + $replace = str_replace('\\', '/', $realCacheDir); + foreach (Finder::create()->files()->in($warmupDir) as $file) { + $content = str_replace($search, $replace, file_get_contents($file), $count); + if ($count) { + file_put_contents($file, $content); + } + } + + if ($realKernel instanceof RebootableInterface) { + return; + } + + // fix references to the Kernel in .meta files + $safeTempKernel = str_replace('\\', '\\\\', \get_class($tempKernel)); + $realKernelFQN = \get_class($realKernel); + + foreach (Finder::create()->files()->depth('<3')->name('*.meta')->in($warmupDir) as $file) { + file_put_contents($file, preg_replace( + '/(C\:\d+\:)"'.$safeTempKernel.'"/', + sprintf('$1"%s"', $realKernelFQN), + file_get_contents($file) + )); + } + + // fix references to container's class + $tempContainerClass = $tempKernel->getContainerClass(); + $realContainerClass = $tempKernel->getRealContainerClass(); + foreach (Finder::create()->files()->depth('<2')->name($tempContainerClass.'*')->in($warmupDir) as $file) { + $content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file)); + file_put_contents($file, $content); + rename($file, str_replace(\DIRECTORY_SEPARATOR.$tempContainerClass, \DIRECTORY_SEPARATOR.$realContainerClass, $file)); + } + if (is_dir($tempContainerDir = $warmupDir.'/'.\get_class($tempKernel->getContainer()))) { + foreach (Finder::create()->files()->in($tempContainerDir) as $file) { + $content = str_replace($tempContainerClass, $realContainerClass, file_get_contents($file)); + file_put_contents($file, $content); + } + } + + // remove temp kernel file after cache warmed up + @unlink($tempKernelFile); + } + + /** + * @param KernelInterface $parent + * @param string $namespace + * @param string $parentClass + * @param string $warmupDir + * + * @return KernelInterface + */ + protected function getTempKernel(KernelInterface $parent, $namespace, $parentClass, $warmupDir) + { + $projectDir = ''; + $cacheDir = var_export($warmupDir, true); + $rootDir = var_export(realpath($parent->getRootDir()), true); + $logDir = var_export(realpath($parent->getLogDir()), true); + // the temp kernel class name must have the same length than the real one + // to avoid the many problems in serialized resources files + $class = substr($parentClass, 0, -1).'_'; + // the temp container class must be changed too + $container = $parent->getContainer(); + $realContainerClass = var_export($container->hasParameter('kernel.container_class') ? $container->getParameter('kernel.container_class') : \get_class($parent->getContainer()), true); + $containerClass = substr_replace($realContainerClass, '_', -2, 1); + + if (method_exists($parent, 'getProjectDir')) { + $projectDir = var_export(realpath($parent->getProjectDir()), true); + $projectDir = <<getResources(); + \$filteredResources = array(); + foreach (\$resources as \$resource) { + if ((string) \$resource !== __FILE__) { + \$filteredResources[] = \$resource; + } + } + + \$container->setResources(\$filteredResources); + + return \$container; + } + } +} +EOF; + $this->filesystem->mkdir($warmupDir); + file_put_contents($file = $warmupDir.'/kernel.tmp', $code); + require_once $file; + $class = "$namespace\\$class"; + + return new $class($parent->getEnvironment(), $parent->isDebug()); + } +} diff --git a/vendor/symfony/framework-bundle/Command/CachePoolClearCommand.php b/vendor/symfony/framework-bundle/Command/CachePoolClearCommand.php new file mode 100644 index 0000000..43b27e6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/CachePoolClearCommand.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; + +/** + * Clear cache pools. + * + * @author Nicolas Grekas + */ +final class CachePoolClearCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'cache:pool:clear'; + + private $poolClearer; + + /** + * @param Psr6CacheClearer $poolClearer + */ + public function __construct($poolClearer = null) + { + if (!$poolClearer instanceof Psr6CacheClearer) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, Psr6CacheClearer::class), E_USER_DEPRECATED); + + parent::__construct($poolClearer); + + return; + } + + parent::__construct(); + + $this->poolClearer = $poolClearer; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('pools', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of cache pools or cache pool clearers'), + )) + ->setDescription('Clears cache pools') + ->setHelp(<<<'EOF' +The %command.name% command clears the given cache pools or cache pool clearers. + + %command.full_name% [...] +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->poolClearer) { + $this->poolClearer = $this->getContainer()->get('cache.global_clearer'); + $cacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); + } + + $io = new SymfonyStyle($input, $output); + $kernel = $this->getApplication()->getKernel(); + $pools = array(); + $clearers = array(); + + foreach ($input->getArgument('pools') as $id) { + if ($this->poolClearer->hasPool($id)) { + $pools[$id] = $id; + } else { + $pool = $kernel->getContainer()->get($id); + + if ($pool instanceof CacheItemPoolInterface) { + $pools[$id] = $pool; + } elseif ($pool instanceof Psr6CacheClearer) { + $clearers[$id] = $pool; + } else { + throw new InvalidArgumentException(sprintf('"%s" is not a cache pool nor a cache clearer.', $id)); + } + } + } + + foreach ($clearers as $id => $clearer) { + $io->comment(sprintf('Calling cache clearer: %s', $id)); + $clearer->clear(isset($cacheDir) ? $cacheDir : $kernel->getContainer()->getParameter('kernel.cache_dir')); + } + + foreach ($pools as $id => $pool) { + $io->comment(sprintf('Clearing cache pool: %s', $id)); + + if ($pool instanceof CacheItemPoolInterface) { + $pool->clear(); + } else { + $this->poolClearer->clearPool($id); + } + } + + $io->success('Cache was successfully cleared.'); + } +} diff --git a/vendor/symfony/framework-bundle/Command/CachePoolPruneCommand.php b/vendor/symfony/framework-bundle/Command/CachePoolPruneCommand.php new file mode 100644 index 0000000..deee376 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/CachePoolPruneCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * Cache pool pruner command. + * + * @author Rob Frawley 2nd + */ +final class CachePoolPruneCommand extends Command +{ + protected static $defaultName = 'cache:pool:prune'; + + private $pools; + + /** + * @param iterable|PruneableInterface[] $pools + */ + public function __construct($pools) + { + parent::__construct(); + + $this->pools = $pools; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDescription('Prunes cache pools') + ->setHelp(<<<'EOF' +The %command.name% command deletes all expired items from all pruneable pools. + + %command.full_name% +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + foreach ($this->pools as $name => $pool) { + $io->comment(sprintf('Pruning cache pool: %s', $name)); + $pool->prune(); + } + + $io->success('Successfully pruned cache pool(s).'); + } +} diff --git a/vendor/symfony/framework-bundle/Command/CacheWarmupCommand.php b/vendor/symfony/framework-bundle/Command/CacheWarmupCommand.php new file mode 100644 index 0000000..593b934 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/CacheWarmupCommand.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; + +/** + * Warmup the cache. + * + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class CacheWarmupCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'cache:warmup'; + + private $cacheWarmer; + + /** + * @param CacheWarmerAggregate $cacheWarmer + */ + public function __construct($cacheWarmer = null) + { + if (!$cacheWarmer instanceof CacheWarmerAggregate) { + @trigger_error(sprintf('Passing a command name as the first argument of "%s()" is deprecated since Symfony 3.4 and support for it will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); + + parent::__construct($cacheWarmer); + + return; + } + + parent::__construct(); + + $this->cacheWarmer = $cacheWarmer; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputOption('no-optional-warmers', '', InputOption::VALUE_NONE, 'Skip optional cache warmers (faster)'), + )) + ->setDescription('Warms up an empty cache') + ->setHelp(<<<'EOF' +The %command.name% command warms up the cache. + +Before running this command, the cache must be empty. + +This command does not generate the classes cache (as when executing this +command, too many classes that should be part of the cache are already loaded +in memory). Use curl or any other similar tool to warm up +the classes cache if you want. + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->cacheWarmer) { + $this->cacheWarmer = $this->getContainer()->get('cache_warmer'); + $cacheDir = $this->getContainer()->getParameter('kernel.cache_dir'); + } + + $io = new SymfonyStyle($input, $output); + + $kernel = $this->getApplication()->getKernel(); + $io->comment(sprintf('Warming up the cache for the %s environment with debug %s', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + + if (!$input->getOption('no-optional-warmers')) { + $this->cacheWarmer->enableOptionalWarmers(); + } + + $this->cacheWarmer->warmUp(isset($cacheDir) ? $cacheDir : $kernel->getContainer()->getParameter('kernel.cache_dir')); + + $io->success(sprintf('Cache for the "%s" environment (debug=%s) was successfully warmed.', $kernel->getEnvironment(), var_export($kernel->isDebug(), true))); + } +} diff --git a/vendor/symfony/framework-bundle/Command/ConfigDebugCommand.php b/vendor/symfony/framework-bundle/Command/ConfigDebugCommand.php new file mode 100644 index 0000000..658f1f9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/ConfigDebugCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Console\Exception\LogicException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Yaml\Yaml; + +/** + * A console command for dumping available configuration reference. + * + * @author Grégoire Pineau + * + * @final since version 3.4 + */ +class ConfigDebugCommand extends AbstractConfigCommand +{ + protected static $defaultName = 'debug:config'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'The bundle name or the extension alias'), + new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'), + )) + ->setDescription('Dumps the current configuration for an extension') + ->setHelp(<<<'EOF' +The %command.name% command dumps the current configuration for an +extension/bundle. + +Either the extension alias or bundle name can be used: + + php %command.full_name% framework + php %command.full_name% FrameworkBundle + +For dumping a specific option, add its path as second argument: + + php %command.full_name% framework serializer.enabled + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + if (null === $name = $input->getArgument('name')) { + $this->listBundles($errorIo); + $errorIo->comment('Provide the name of a bundle as the first argument of this command to dump its configuration. (e.g. debug:config FrameworkBundle)'); + $errorIo->comment('For dumping a specific option, add its path as the second argument of this command. (e.g. debug:config FrameworkBundle serializer to dump the framework.serializer configuration)'); + + return; + } + + $extension = $this->findExtension($name); + $container = $this->compileContainer(); + + $extensionAlias = $extension->getAlias(); + $configs = $container->getExtensionConfig($extensionAlias); + $configuration = $extension->getConfiguration($configs, $container); + + $this->validateConfiguration($extension, $configuration); + + $configs = $container->resolveEnvPlaceholders($container->getParameterBag()->resolveValue($configs)); + + $processor = new Processor(); + $config = $container->resolveEnvPlaceholders($container->getParameterBag()->resolveValue($processor->processConfiguration($configuration, $configs))); + + if (null === $path = $input->getArgument('path')) { + $io->title( + sprintf('Current configuration for %s', ($name === $extensionAlias ? sprintf('extension with alias "%s"', $extensionAlias) : sprintf('"%s"', $name))) + ); + + $io->writeln(Yaml::dump(array($extensionAlias => $config), 10)); + + return; + } + + try { + $config = $this->getConfigForPath($config, $path, $extensionAlias); + } catch (LogicException $e) { + $errorIo->error($e->getMessage()); + + return; + } + + $io->title(sprintf('Current configuration for "%s.%s"', $extensionAlias, $path)); + + $io->writeln(Yaml::dump($config, 10)); + } + + private function compileContainer() + { + $kernel = clone $this->getApplication()->getKernel(); + $kernel->boot(); + + $method = new \ReflectionMethod($kernel, 'buildContainer'); + $method->setAccessible(true); + $container = $method->invoke($kernel); + $container->getCompiler()->compile($container); + + return $container; + } + + /** + * Iterate over configuration until the last step of the given path. + * + * @param array $config A bundle configuration + * + * @throws LogicException If the configuration does not exist + * + * @return mixed + */ + private function getConfigForPath(array $config, $path, $alias) + { + $steps = explode('.', $path); + + foreach ($steps as $step) { + if (!array_key_exists($step, $config)) { + throw new LogicException(sprintf('Unable to find configuration for "%s.%s"', $alias, $path)); + } + + $config = $config[$step]; + } + + return $config; + } +} diff --git a/vendor/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php b/vendor/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.php new file mode 100644 index 0000000..bdcf13e --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/ConfigDumpReferenceCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Config\Definition\Dumper\XmlReferenceDumper; +use Symfony\Component\Config\Definition\Dumper\YamlReferenceDumper; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * A console command for dumping available configuration reference. + * + * @author Kevin Bond + * @author Wouter J + * @author Grégoire Pineau + * + * @final since version 3.4 + */ +class ConfigDumpReferenceCommand extends AbstractConfigCommand +{ + protected static $defaultName = 'config:dump-reference'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'The Bundle name or the extension alias'), + new InputArgument('path', InputArgument::OPTIONAL, 'The configuration option path'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (yaml or xml)', 'yaml'), + )) + ->setDescription('Dumps the default configuration for an extension') + ->setHelp(<<<'EOF' +The %command.name% command dumps the default configuration for an +extension/bundle. + +Either the extension alias or bundle name can be used: + + php %command.full_name% framework + php %command.full_name% FrameworkBundle + +With the --format option specifies the format of the configuration, +this is either yaml or xml. +When the option is not provided, yaml is used. + + php %command.full_name% FrameworkBundle --format=xml + +For dumping a specific option, add its path as second argument (only available for the yaml format): + + php %command.full_name% framework profiler.matcher + +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + if (null === $name = $input->getArgument('name')) { + $this->listBundles($errorIo); + $errorIo->comment(array( + 'Provide the name of a bundle as the first argument of this command to dump its default configuration. (e.g. config:dump-reference FrameworkBundle)', + 'For dumping a specific option, add its path as the second argument of this command. (e.g. config:dump-reference FrameworkBundle profiler.matcher to dump the framework.profiler.matcher configuration)', + )); + + return; + } + + $extension = $this->findExtension($name); + + $configuration = $extension->getConfiguration(array(), $this->getContainerBuilder()); + + $this->validateConfiguration($extension, $configuration); + + $format = $input->getOption('format'); + $path = $input->getArgument('path'); + + if (null !== $path && 'yaml' !== $format) { + $errorIo->error('The "path" option is only available for the "yaml" format.'); + + return 1; + } + + if ($name === $extension->getAlias()) { + $message = sprintf('Default configuration for extension with alias: "%s"', $name); + } else { + $message = sprintf('Default configuration for "%s"', $name); + } + + if (null !== $path) { + $message .= sprintf(' at path "%s"', $path); + } + + switch ($format) { + case 'yaml': + $io->writeln(sprintf('# %s', $message)); + $dumper = new YamlReferenceDumper(); + break; + case 'xml': + $io->writeln(sprintf('', $message)); + $dumper = new XmlReferenceDumper(); + break; + default: + $io->writeln($message); + throw new InvalidArgumentException('Only the yaml and xml formats are supported.'); + } + + $io->writeln(null === $path ? $dumper->dump($configuration, $extension->getNamespace()) : $dumper->dumpAtPath($configuration, $path)); + } +} diff --git a/vendor/symfony/framework-bundle/Command/ContainerAwareCommand.php b/vendor/symfony/framework-bundle/Command/ContainerAwareCommand.php new file mode 100644 index 0000000..2c84e7f --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/ContainerAwareCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * Command. + * + * @author Fabien Potencier + */ +abstract class ContainerAwareCommand extends Command implements ContainerAwareInterface +{ + /** + * @var ContainerInterface|null + */ + private $container; + + /** + * @return ContainerInterface + * + * @throws \LogicException + */ + protected function getContainer() + { + if (null === $this->container) { + $application = $this->getApplication(); + if (null === $application) { + throw new \LogicException('The container cannot be retrieved as the application instance is not yet set.'); + } + + $this->container = $application->getKernel()->getContainer(); + } + + return $this->container; + } + + /** + * {@inheritdoc} + */ + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } +} diff --git a/vendor/symfony/framework-bundle/Command/ContainerDebugCommand.php b/vendor/symfony/framework-bundle/Command/ContainerDebugCommand.php new file mode 100644 index 0000000..ad7affa --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/ContainerDebugCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; + +/** + * A console command for retrieving information about services. + * + * @author Ryan Weaver + * + * @internal since version 3.4 + */ +class ContainerDebugCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'debug:container'; + + /** + * @var ContainerBuilder|null + */ + protected $containerBuilder; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A service name (foo)'), + new InputOption('show-private', null, InputOption::VALUE_NONE, 'Used to show public *and* private services'), + new InputOption('show-arguments', null, InputOption::VALUE_NONE, 'Used to show arguments in services'), + new InputOption('tag', null, InputOption::VALUE_REQUIRED, 'Shows all services with a specific tag'), + new InputOption('tags', null, InputOption::VALUE_NONE, 'Displays tagged services for an application'), + new InputOption('parameter', null, InputOption::VALUE_REQUIRED, 'Displays a specific parameter for an application'), + new InputOption('parameters', null, InputOption::VALUE_NONE, 'Displays parameters for an application'), + new InputOption('types', null, InputOption::VALUE_NONE, 'Displays types (classes/interfaces) available in the container'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), + )) + ->setDescription('Displays current services for an application') + ->setHelp(<<<'EOF' +The %command.name% command displays all configured public services: + + php %command.full_name% + +To get specific information about a service, specify its name: + + php %command.full_name% validator + +To see available types that can be used for autowiring, use the --types flag: + + php %command.full_name% --types + +By default, private services are hidden. You can display all services by +using the --show-private flag: + + php %command.full_name% --show-private + +Use the --tags option to display tagged public services grouped by tag: + + php %command.full_name% --tags + +Find all services with a specific tag by specifying the tag name with the --tag option: + + php %command.full_name% --tag=form.type + +Use the --parameters option to display all parameters: + + php %command.full_name% --parameters + +Display a specific parameter by specifying its name with the --parameter option: + + php %command.full_name% --parameter=kernel.debug + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + $this->validateInput($input); + $object = $this->getContainerBuilder(); + + if ($input->getOption('types')) { + $options = array('show_private' => true); + $options['filter'] = array($this, 'filterToServiceTypes'); + } elseif ($input->getOption('parameters')) { + $parameters = array(); + foreach ($object->getParameterBag()->all() as $k => $v) { + $parameters[$k] = $object->resolveEnvPlaceholders($v); + } + $object = new ParameterBag($parameters); + $options = array(); + } elseif ($parameter = $input->getOption('parameter')) { + $options = array('parameter' => $parameter); + } elseif ($input->getOption('tags')) { + $options = array('group_by' => 'tags', 'show_private' => $input->getOption('show-private')); + } elseif ($tag = $input->getOption('tag')) { + $options = array('tag' => $tag, 'show_private' => $input->getOption('show-private')); + } elseif ($name = $input->getArgument('name')) { + $name = $this->findProperServiceName($input, $errorIo, $object, $name); + $options = array('id' => $name); + } else { + $options = array('show_private' => $input->getOption('show-private')); + } + + $helper = new DescriptorHelper(); + $options['format'] = $input->getOption('format'); + $options['show_arguments'] = $input->getOption('show-arguments'); + $options['raw_text'] = $input->getOption('raw'); + $options['output'] = $io; + $helper->describe($io, $object, $options); + + if (!$input->getArgument('name') && !$input->getOption('tag') && !$input->getOption('parameter') && $input->isInteractive()) { + if ($input->getOption('tags')) { + $errorIo->comment('To search for a specific tag, re-run this command with a search term. (e.g. debug:container --tag=form.type)'); + } elseif ($input->getOption('parameters')) { + $errorIo->comment('To search for a specific parameter, re-run this command with a search term. (e.g. debug:container --parameter=kernel.debug)'); + } else { + $errorIo->comment('To search for a specific service, re-run this command with a search term. (e.g. debug:container log)'); + } + } + } + + /** + * Validates input arguments and options. + * + * @throws \InvalidArgumentException + */ + protected function validateInput(InputInterface $input) + { + $options = array('tags', 'tag', 'parameters', 'parameter'); + + $optionsCount = 0; + foreach ($options as $option) { + if ($input->getOption($option)) { + ++$optionsCount; + } + } + + $name = $input->getArgument('name'); + if ((null !== $name) && ($optionsCount > 0)) { + throw new InvalidArgumentException('The options tags, tag, parameters & parameter can not be combined with the service name argument.'); + } elseif ((null === $name) && $optionsCount > 1) { + throw new InvalidArgumentException('The options tags, tag, parameters & parameter can not be combined together.'); + } + } + + /** + * Loads the ContainerBuilder from the cache. + * + * @return ContainerBuilder + * + * @throws \LogicException + */ + protected function getContainerBuilder() + { + if ($this->containerBuilder) { + return $this->containerBuilder; + } + + $kernel = $this->getApplication()->getKernel(); + + if (!$kernel->isDebug() || !(new ConfigCache($kernel->getContainer()->getParameter('debug.container.dump'), true))->isFresh()) { + $buildContainer = \Closure::bind(function () { return $this->buildContainer(); }, $kernel, \get_class($kernel)); + $container = $buildContainer(); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + } else { + (new XmlFileLoader($container = new ContainerBuilder(), new FileLocator()))->load($kernel->getContainer()->getParameter('debug.container.dump')); + } + + return $this->containerBuilder = $container; + } + + private function findProperServiceName(InputInterface $input, SymfonyStyle $io, ContainerBuilder $builder, $name) + { + if ($builder->has($name) || !$input->isInteractive()) { + return $name; + } + + $matchingServices = $this->findServiceIdsContaining($builder, $name); + if (empty($matchingServices)) { + throw new InvalidArgumentException(sprintf('No services found that match "%s".', $name)); + } + + $default = 1 === \count($matchingServices) ? $matchingServices[0] : null; + + return $io->choice('Select one of the following services to display its information', $matchingServices, $default); + } + + private function findServiceIdsContaining(ContainerBuilder $builder, $name) + { + $serviceIds = $builder->getServiceIds(); + $foundServiceIds = array(); + foreach ($serviceIds as $serviceId) { + if (false === stripos($serviceId, $name)) { + continue; + } + $foundServiceIds[] = $serviceId; + } + + return $foundServiceIds; + } + + /** + * @internal + */ + public function filterToServiceTypes($serviceId) + { + // filter out things that could not be valid class names + if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+$/', $serviceId)) { + return false; + } + + // if the id has a \, assume it is a class + if (false !== strpos($serviceId, '\\')) { + return true; + } + + try { + new \ReflectionClass($serviceId); + + return true; + } catch (\ReflectionException $e) { + // the service id is not a valid class/interface + return false; + } + } +} diff --git a/vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.php b/vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.php new file mode 100644 index 0000000..23d6884 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/DebugAutowiringCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * A console command for autowiring information. + * + * @author Ryan Weaver + * + * @internal + */ +class DebugAutowiringCommand extends ContainerDebugCommand +{ + protected static $defaultName = 'debug:autowiring'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('search', InputArgument::OPTIONAL, 'A search filter'), + )) + ->setDescription('Lists classes/interfaces you can use for autowiring') + ->setHelp(<<<'EOF' +The %command.name% command displays all classes and interfaces that +you can use as type-hints for autowiring: + + php %command.full_name% + +You can also pass a search term to filter the list: + + php %command.full_name% log + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + $builder = $this->getContainerBuilder(); + $serviceIds = $builder->getServiceIds(); + $serviceIds = array_filter($serviceIds, array($this, 'filterToServiceTypes')); + + if ($search = $input->getArgument('search')) { + $serviceIds = array_filter($serviceIds, function ($serviceId) use ($search) { + return false !== stripos($serviceId, $search); + }); + + if (empty($serviceIds)) { + $errorIo->error(sprintf('No autowirable classes or interfaces found matching "%s"', $search)); + + return 1; + } + } + + asort($serviceIds); + + $io->title('Autowirable Services'); + $io->text('The following classes & interfaces can be used as type-hints when autowiring:'); + if ($search) { + $io->text(sprintf('(only showing classes/interfaces matching %s)', $search)); + } + $io->newLine(); + $tableRows = array(); + foreach ($serviceIds as $serviceId) { + $tableRows[] = array(sprintf('%s', $serviceId)); + if ($builder->hasAlias($serviceId)) { + $tableRows[] = array(sprintf(' alias to %s', $builder->getAlias($serviceId))); + } + } + + $io->table(array(), $tableRows); + } +} diff --git a/vendor/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php b/vendor/symfony/framework-bundle/Command/EventDispatcherDebugCommand.php new file mode 100644 index 0000000..9f7a916 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/EventDispatcherDebugCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * A console command for retrieving information about event dispatcher. + * + * @author Matthieu Auger + * + * @final since version 3.4 + */ +class EventDispatcherDebugCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'debug:event-dispatcher'; + private $dispatcher; + + /** + * @param EventDispatcherInterface $dispatcher + */ + public function __construct($dispatcher = null) + { + if (!$dispatcher instanceof EventDispatcherInterface) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, EventDispatcherInterface::class), E_USER_DEPRECATED); + + parent::__construct($dispatcher); + + return; + } + + parent::__construct(); + + $this->dispatcher = $dispatcher; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('event', InputArgument::OPTIONAL, 'An event name'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw description'), + )) + ->setDescription('Displays configured listeners for an application') + ->setHelp(<<<'EOF' +The %command.name% command displays all configured listeners: + + php %command.full_name% + +To get specific listeners for an event, specify its name: + + php %command.full_name% kernel.request +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \LogicException + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->dispatcher) { + $this->dispatcher = $this->getEventDispatcher(); + } + + $io = new SymfonyStyle($input, $output); + + $options = array(); + if ($event = $input->getArgument('event')) { + if (!$this->dispatcher->hasListeners($event)) { + $io->getErrorStyle()->warning(sprintf('The event "%s" does not have any registered listeners.', $event)); + + return; + } + + $options = array('event' => $event); + } + + $helper = new DescriptorHelper(); + $options['format'] = $input->getOption('format'); + $options['raw_text'] = $input->getOption('raw'); + $options['output'] = $io; + $helper->describe($io, $this->dispatcher, $options); + } + + /** + * Loads the Event Dispatcher from the container. + * + * BC to removed in 4.0 + * + * @return EventDispatcherInterface + */ + protected function getEventDispatcher() + { + return $this->getContainer()->get('event_dispatcher'); + } +} diff --git a/vendor/symfony/framework-bundle/Command/RouterDebugCommand.php b/vendor/symfony/framework-bundle/Command/RouterDebugCommand.php new file mode 100644 index 0000000..fbf7ba9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/RouterDebugCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Bundle\FrameworkBundle\Console\Helper\DescriptorHelper; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouterInterface; + +/** + * A console command for retrieving information about routes. + * + * @author Fabien Potencier + * @author Tobias Schultze + * + * @final since version 3.4 + */ +class RouterDebugCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'debug:router'; + private $router; + + /** + * @param RouterInterface $router + */ + public function __construct($router = null) + { + if (!$router instanceof RouterInterface) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, RouterInterface::class), E_USER_DEPRECATED); + + parent::__construct($router); + + return; + } + + parent::__construct(); + + $this->router = $router; + } + + /** + * {@inheritdoc} + * + * BC to be removed in 4.0 + */ + public function isEnabled() + { + if (null !== $this->router) { + return parent::isEnabled(); + } + if (!$this->getContainer()->has('router')) { + return false; + } + $router = $this->getContainer()->get('router'); + if (!$router instanceof RouterInterface) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('name', InputArgument::OPTIONAL, 'A route name'), + new InputOption('show-controllers', null, InputOption::VALUE_NONE, 'Show assigned controllers in overview'), + new InputOption('format', null, InputOption::VALUE_REQUIRED, 'The output format (txt, xml, json, or md)', 'txt'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw route(s)'), + )) + ->setDescription('Displays current routes for an application') + ->setHelp(<<<'EOF' +The %command.name% displays the configured routes: + + php %command.full_name% + +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException When route does not exist + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->router) { + $this->router = $this->getContainer()->get('router'); + } + + $io = new SymfonyStyle($input, $output); + $name = $input->getArgument('name'); + $helper = new DescriptorHelper(); + $routes = $this->router->getRouteCollection(); + + if ($name) { + if (!$route = $routes->get($name)) { + throw new InvalidArgumentException(sprintf('The route "%s" does not exist.', $name)); + } + + $callable = $this->extractCallable($route); + + $helper->describe($io, $route, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'name' => $name, + 'output' => $io, + 'callable' => $callable, + )); + } else { + foreach ($routes as $route) { + $this->convertController($route); + } + + $helper->describe($io, $routes, array( + 'format' => $input->getOption('format'), + 'raw_text' => $input->getOption('raw'), + 'show_controllers' => $input->getOption('show-controllers'), + 'output' => $io, + )); + } + } + + private function convertController(Route $route) + { + if ($route->hasDefault('_controller')) { + $nameParser = new ControllerNameParser($this->getApplication()->getKernel()); + try { + $route->setDefault('_controller', $nameParser->build($route->getDefault('_controller'))); + } catch (\InvalidArgumentException $e) { + } + } + } + + private function extractCallable(Route $route) + { + if (!$route->hasDefault('_controller')) { + return; + } + + $controller = $route->getDefault('_controller'); + + if (1 === substr_count($controller, ':')) { + list($service, $method) = explode(':', $controller); + try { + return sprintf('%s::%s', \get_class($this->getApplication()->getKernel()->getContainer()->get($service)), $method); + } catch (ServiceNotFoundException $e) { + } + } + + $nameParser = new ControllerNameParser($this->getApplication()->getKernel()); + try { + $shortNotation = $nameParser->build($controller); + $route->setDefault('_controller', $shortNotation); + + return $controller; + } catch (\InvalidArgumentException $e) { + } + } +} diff --git a/vendor/symfony/framework-bundle/Command/RouterMatchCommand.php b/vendor/symfony/framework-bundle/Command/RouterMatchCommand.php new file mode 100644 index 0000000..7ea1af6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/RouterMatchCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; +use Symfony\Component\Routing\RouterInterface; + +/** + * A console command to test route matching. + * + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class RouterMatchCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'router:match'; + + private $router; + + /** + * @param RouterInterface $router + */ + public function __construct($router = null) + { + if (!$router instanceof RouterInterface) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, RouterInterface::class), E_USER_DEPRECATED); + + parent::__construct($router); + + return; + } + + parent::__construct(); + + $this->router = $router; + } + + /** + * {@inheritdoc} + * + * BC to be removed in 4.0 + */ + public function isEnabled() + { + if (null !== $this->router) { + return parent::isEnabled(); + } + if (!$this->getContainer()->has('router')) { + return false; + } + $router = $this->getContainer()->get('router'); + if (!$router instanceof RouterInterface) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('path_info', InputArgument::REQUIRED, 'A path info'), + new InputOption('method', null, InputOption::VALUE_REQUIRED, 'Sets the HTTP method'), + new InputOption('scheme', null, InputOption::VALUE_REQUIRED, 'Sets the URI scheme (usually http or https)'), + new InputOption('host', null, InputOption::VALUE_REQUIRED, 'Sets the URI host'), + )) + ->setDescription('Helps debug routes by simulating a path info match') + ->setHelp(<<<'EOF' +The %command.name% shows which routes match a given request and which don't and for what reason: + + php %command.full_name% /foo + +or + + php %command.full_name% /foo --method POST --scheme https --host symfony.com --verbose + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->router) { + $this->router = $this->getContainer()->get('router'); + } + + $io = new SymfonyStyle($input, $output); + + $context = $this->router->getContext(); + if (null !== $method = $input->getOption('method')) { + $context->setMethod($method); + } + if (null !== $scheme = $input->getOption('scheme')) { + $context->setScheme($scheme); + } + if (null !== $host = $input->getOption('host')) { + $context->setHost($host); + } + + $matcher = new TraceableUrlMatcher($this->router->getRouteCollection(), $context); + + $traces = $matcher->getTraces($input->getArgument('path_info')); + + $io->newLine(); + + $matches = false; + foreach ($traces as $trace) { + if (TraceableUrlMatcher::ROUTE_ALMOST_MATCHES == $trace['level']) { + $io->text(sprintf('Route "%s" almost matches but %s', $trace['name'], lcfirst($trace['log']))); + } elseif (TraceableUrlMatcher::ROUTE_MATCHES == $trace['level']) { + $io->success(sprintf('Route "%s" matches', $trace['name'])); + + $routerDebugCommand = $this->getApplication()->find('debug:router'); + $routerDebugCommand->run(new ArrayInput(array('name' => $trace['name'])), $output); + + $matches = true; + } elseif ($input->getOption('verbose')) { + $io->text(sprintf('Route "%s" does not match: %s', $trace['name'], $trace['log'])); + } + } + + if (!$matches) { + $io->error(sprintf('None of the routes match the path "%s"', $input->getArgument('path_info'))); + + return 1; + } + } +} diff --git a/vendor/symfony/framework-bundle/Command/TranslationDebugCommand.php b/vendor/symfony/framework-bundle/Command/TranslationDebugCommand.php new file mode 100644 index 0000000..82ec353 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/TranslationDebugCommand.php @@ -0,0 +1,388 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Translation\DataCollectorTranslator; +use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\LoggingTranslator; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\Translator; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * Helps finding unused or missing translation messages in a given locale + * and comparing them with the fallback ones. + * + * @author Florian Voutzinos + * + * @final since version 3.4 + */ +class TranslationDebugCommand extends ContainerAwareCommand +{ + const MESSAGE_MISSING = 0; + const MESSAGE_UNUSED = 1; + const MESSAGE_EQUALS_FALLBACK = 2; + + protected static $defaultName = 'debug:translation'; + + private $translator; + private $reader; + private $extractor; + private $defaultTransPath; + private $defaultViewsPath; + + public function __construct($translator = null, TranslationReaderInterface $reader = null, ExtractorInterface $extractor = null, $defaultTransPath = null, $defaultViewsPath = null) + { + if (!$translator instanceof TranslatorInterface) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, TranslatorInterface::class), E_USER_DEPRECATED); + + parent::__construct($translator); + + return; + } + + parent::__construct(); + + $this->translator = $translator; + $this->reader = $reader; + $this->extractor = $extractor; + $this->defaultTransPath = $defaultTransPath; + $this->defaultViewsPath = $defaultViewsPath; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), + new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages, defaults to app/Resources folder'), + new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'The messages domain'), + new InputOption('only-missing', null, InputOption::VALUE_NONE, 'Displays only missing messages'), + new InputOption('only-unused', null, InputOption::VALUE_NONE, 'Displays only unused messages'), + new InputOption('all', null, InputOption::VALUE_NONE, 'Load messages from all registered bundles'), + )) + ->setDescription('Displays translation messages information') + ->setHelp(<<<'EOF' +The %command.name% command helps finding unused or missing translation +messages and comparing them with the fallback ones by inspecting the +templates and translation files of a given bundle or the app folder. + +You can display information about bundle translations in a specific locale: + + php %command.full_name% en AcmeDemoBundle + +You can also specify a translation domain for the search: + + php %command.full_name% --domain=messages en AcmeDemoBundle + +You can only display missing messages: + + php %command.full_name% --only-missing en AcmeDemoBundle + +You can only display unused messages: + + php %command.full_name% --only-unused en AcmeDemoBundle + +You can display information about app translations in a specific locale: + + php %command.full_name% en + +You can display information about translations in all registered bundles in a specific locale: + + php %command.full_name% --all en + +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * BC to be removed in 4.0 + */ + public function isEnabled() + { + if (null !== $this->translator) { + return parent::isEnabled(); + } + if (!class_exists('Symfony\Component\Translation\Translator')) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->translator) { + $this->translator = $this->getContainer()->get('translator'); + $this->reader = $this->getContainer()->get('translation.reader'); + $this->extractor = $this->getContainer()->get('translation.extractor'); + } + + $io = new SymfonyStyle($input, $output); + + $locale = $input->getArgument('locale'); + $domain = $input->getOption('domain'); + /** @var KernelInterface $kernel */ + $kernel = $this->getApplication()->getKernel(); + + // Define Root Paths + $transPaths = array($kernel->getRootDir().'/Resources/translations'); + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath; + } + $viewsPaths = array($kernel->getRootDir().'/Resources/views'); + if ($this->defaultViewsPath) { + $viewsPaths[] = $this->defaultViewsPath; + } + + // Override with provided Bundle info + if (null !== $input->getArgument('bundle')) { + try { + $bundle = $kernel->getBundle($input->getArgument('bundle')); + $transPaths = array($bundle->getPath().'/Resources/translations'); + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath.'/'.$bundle->getName(); + } + $transPaths[] = sprintf('%s/Resources/%s/translations', $kernel->getRootDir(), $bundle->getName()); + $viewsPaths = array($bundle->getPath().'/Resources/views'); + if ($this->defaultViewsPath) { + $viewsPaths[] = $this->defaultViewsPath.'/bundles/'.$bundle->getName(); + } + $viewsPaths[] = sprintf('%s/Resources/%s/views', $kernel->getRootDir(), $bundle->getName()); + } catch (\InvalidArgumentException $e) { + // such a bundle does not exist, so treat the argument as path + $transPaths = array($input->getArgument('bundle').'/Resources/translations'); + $viewsPaths = array($input->getArgument('bundle').'/Resources/views'); + + if (!is_dir($transPaths[0])) { + throw new InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + } + } + } elseif ($input->getOption('all')) { + foreach ($kernel->getBundles() as $bundle) { + $transPaths[] = $bundle->getPath().'/Resources/translations'; + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath.'/'.$bundle->getName(); + } + $transPaths[] = sprintf('%s/Resources/%s/translations', $kernel->getRootDir(), $bundle->getName()); + $viewsPaths[] = $bundle->getPath().'/Resources/views'; + if ($this->defaultViewsPath) { + $viewsPaths[] = $this->defaultViewsPath.'/bundles/'.$bundle->getName(); + } + $viewsPaths[] = sprintf('%s/Resources/%s/views', $kernel->getRootDir(), $bundle->getName()); + } + } + + // Extract used messages + $extractedCatalogue = $this->extractMessages($locale, $viewsPaths); + + // Load defined messages + $currentCatalogue = $this->loadCurrentMessages($locale, $transPaths); + + // Merge defined and extracted messages to get all message ids + $mergeOperation = new MergeOperation($extractedCatalogue, $currentCatalogue); + $allMessages = $mergeOperation->getResult()->all($domain); + if (null !== $domain) { + $allMessages = array($domain => $allMessages); + } + + // No defined or extracted messages + if (empty($allMessages) || null !== $domain && empty($allMessages[$domain])) { + $outputMessage = sprintf('No defined or extracted messages for locale "%s"', $locale); + + if (null !== $domain) { + $outputMessage .= sprintf(' and domain "%s"', $domain); + } + + $io->getErrorStyle()->warning($outputMessage); + + return; + } + + // Load the fallback catalogues + $fallbackCatalogues = $this->loadFallbackCatalogues($locale, $transPaths); + + // Display header line + $headers = array('State', 'Domain', 'Id', sprintf('Message Preview (%s)', $locale)); + foreach ($fallbackCatalogues as $fallbackCatalogue) { + $headers[] = sprintf('Fallback Message Preview (%s)', $fallbackCatalogue->getLocale()); + } + $rows = array(); + // Iterate all message ids and determine their state + foreach ($allMessages as $domain => $messages) { + foreach (array_keys($messages) as $messageId) { + $value = $currentCatalogue->get($messageId, $domain); + $states = array(); + + if ($extractedCatalogue->defines($messageId, $domain)) { + if (!$currentCatalogue->defines($messageId, $domain)) { + $states[] = self::MESSAGE_MISSING; + } + } elseif ($currentCatalogue->defines($messageId, $domain)) { + $states[] = self::MESSAGE_UNUSED; + } + + if (!\in_array(self::MESSAGE_UNUSED, $states) && true === $input->getOption('only-unused') + || !\in_array(self::MESSAGE_MISSING, $states) && true === $input->getOption('only-missing')) { + continue; + } + + foreach ($fallbackCatalogues as $fallbackCatalogue) { + if ($fallbackCatalogue->defines($messageId, $domain) && $value === $fallbackCatalogue->get($messageId, $domain)) { + $states[] = self::MESSAGE_EQUALS_FALLBACK; + + break; + } + } + + $row = array($this->formatStates($states), $domain, $this->formatId($messageId), $this->sanitizeString($value)); + foreach ($fallbackCatalogues as $fallbackCatalogue) { + $row[] = $this->sanitizeString($fallbackCatalogue->get($messageId, $domain)); + } + + $rows[] = $row; + } + } + + $io->table($headers, $rows); + } + + private function formatState($state) + { + if (self::MESSAGE_MISSING === $state) { + return ' missing '; + } + + if (self::MESSAGE_UNUSED === $state) { + return ' unused '; + } + + if (self::MESSAGE_EQUALS_FALLBACK === $state) { + return ' fallback '; + } + + return $state; + } + + private function formatStates(array $states) + { + $result = array(); + foreach ($states as $state) { + $result[] = $this->formatState($state); + } + + return implode(' ', $result); + } + + private function formatId($id) + { + return sprintf('%s', $id); + } + + private function sanitizeString($string, $length = 40) + { + $string = trim(preg_replace('/\s+/', ' ', $string)); + + if (false !== $encoding = mb_detect_encoding($string, null, true)) { + if (mb_strlen($string, $encoding) > $length) { + return mb_substr($string, 0, $length - 3, $encoding).'...'; + } + } elseif (\strlen($string) > $length) { + return substr($string, 0, $length - 3).'...'; + } + + return $string; + } + + /** + * @param string $locale + * @param array $transPaths + * + * @return MessageCatalogue + */ + private function extractMessages($locale, $transPaths) + { + $extractedCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + if (is_dir($path)) { + $this->extractor->extract($path, $extractedCatalogue); + } + } + + return $extractedCatalogue; + } + + /** + * @param string $locale + * @param array $transPaths + * + * @return MessageCatalogue + */ + private function loadCurrentMessages($locale, $transPaths) + { + $currentCatalogue = new MessageCatalogue($locale); + foreach ($transPaths as $path) { + if (is_dir($path)) { + $this->reader->read($path, $currentCatalogue); + } + } + + return $currentCatalogue; + } + + /** + * @param string $locale + * @param array $transPaths + * + * @return MessageCatalogue[] + */ + private function loadFallbackCatalogues($locale, $transPaths) + { + $fallbackCatalogues = array(); + if ($this->translator instanceof Translator || $this->translator instanceof DataCollectorTranslator || $this->translator instanceof LoggingTranslator) { + foreach ($this->translator->getFallbackLocales() as $fallbackLocale) { + if ($fallbackLocale === $locale) { + continue; + } + + $fallbackCatalogue = new MessageCatalogue($fallbackLocale); + foreach ($transPaths as $path) { + if (is_dir($path)) { + $this->reader->read($path, $fallbackCatalogue); + } + } + $fallbackCatalogues[] = $fallbackCatalogue; + } + } + + return $fallbackCatalogues; + } +} diff --git a/vendor/symfony/framework-bundle/Command/TranslationUpdateCommand.php b/vendor/symfony/framework-bundle/Command/TranslationUpdateCommand.php new file mode 100644 index 0000000..c7dff1c --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/TranslationUpdateCommand.php @@ -0,0 +1,328 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Translation\Catalogue\MergeOperation; +use Symfony\Component\Translation\Catalogue\TargetOperation; +use Symfony\Component\Translation\Extractor\ExtractorInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Reader\TranslationReaderInterface; +use Symfony\Component\Translation\Writer\TranslationWriterInterface; + +/** + * A command that parses templates to extract translation messages and adds them + * into the translation files. + * + * @author Michel Salib + * + * @final since version 3.4 + */ +class TranslationUpdateCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'translation:update'; + + private $writer; + private $reader; + private $extractor; + private $defaultLocale; + private $defaultTransPath; + private $defaultViewsPath; + + /** + * @param TranslationWriterInterface $writer + * @param TranslationReaderInterface $reader + * @param ExtractorInterface $extractor + * @param string $defaultLocale + * @param string $defaultTransPath + * @param string $defaultViewsPath + */ + public function __construct($writer = null, TranslationReaderInterface $reader = null, ExtractorInterface $extractor = null, $defaultLocale = null, $defaultTransPath = null, $defaultViewsPath = null) + { + if (!$writer instanceof TranslationWriterInterface) { + @trigger_error(sprintf('%s() expects an instance of "%s" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0.', __METHOD__, TranslationWriterInterface::class), E_USER_DEPRECATED); + + parent::__construct($writer); + + return; + } + + parent::__construct(); + + $this->writer = $writer; + $this->reader = $reader; + $this->extractor = $extractor; + $this->defaultLocale = $defaultLocale; + $this->defaultTransPath = $defaultTransPath; + $this->defaultViewsPath = $defaultViewsPath; + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('locale', InputArgument::REQUIRED, 'The locale'), + new InputArgument('bundle', InputArgument::OPTIONAL, 'The bundle name or directory where to load the messages, defaults to app/Resources folder'), + new InputOption('prefix', null, InputOption::VALUE_OPTIONAL, 'Override the default prefix', '__'), + new InputOption('no-prefix', null, InputOption::VALUE_NONE, '[DEPRECATED] If set, no prefix is added to the translations'), + new InputOption('output-format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format', 'yml'), + new InputOption('dump-messages', null, InputOption::VALUE_NONE, 'Should the messages be dumped in the console'), + new InputOption('force', null, InputOption::VALUE_NONE, 'Should the update be done'), + new InputOption('no-backup', null, InputOption::VALUE_NONE, 'Should backup be disabled'), + new InputOption('clean', null, InputOption::VALUE_NONE, 'Should clean not found messages'), + new InputOption('domain', null, InputOption::VALUE_OPTIONAL, 'Specify the domain to update'), + )) + ->setDescription('Updates the translation file') + ->setHelp(<<<'EOF' +The %command.name% command extracts translation strings from templates +of a given bundle or the app folder. It can display them or merge the new ones into the translation files. + +When new translation strings are found it can automatically add a prefix to the translation +message. + +Example running against a Bundle (AcmeBundle) + php %command.full_name% --dump-messages en AcmeBundle + php %command.full_name% --force --prefix="new_" fr AcmeBundle + +Example running against app messages (app/Resources folder) + php %command.full_name% --dump-messages en + php %command.full_name% --force --prefix="new_" fr +EOF + ) + ; + } + + /** + * {@inheritdoc} + * + * BC to be removed in 4.0 + */ + public function isEnabled() + { + if (null !== $this->writer) { + return parent::isEnabled(); + } + if (!class_exists('Symfony\Component\Translation\Translator')) { + return false; + } + + return parent::isEnabled(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + // BC to be removed in 4.0 + if (null === $this->writer) { + $this->writer = $this->getContainer()->get('translation.writer'); + $this->reader = $this->getContainer()->get('translation.reader'); + $this->extractor = $this->getContainer()->get('translation.extractor'); + $this->defaultLocale = $this->getContainer()->getParameter('kernel.default_locale'); + } + + $io = new SymfonyStyle($input, $output); + $errorIo = $io->getErrorStyle(); + + // check presence of force or dump-message + if (true !== $input->getOption('force') && true !== $input->getOption('dump-messages')) { + $errorIo->error('You must choose one of --force or --dump-messages'); + + return 1; + } + + // check format + $supportedFormats = $this->writer->getFormats(); + if (!\in_array($input->getOption('output-format'), $supportedFormats)) { + $errorIo->error(array('Wrong output format', 'Supported formats are: '.implode(', ', $supportedFormats).'.')); + + return 1; + } + /** @var KernelInterface $kernel */ + $kernel = $this->getApplication()->getKernel(); + + // Define Root Paths + $transPaths = array($kernel->getRootDir().'/Resources/translations'); + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath; + } + $viewsPaths = array($kernel->getRootDir().'/Resources/views'); + if ($this->defaultViewsPath) { + $viewsPaths[] = $this->defaultViewsPath; + } + $currentName = 'app folder'; + + // Override with provided Bundle info + if (null !== $input->getArgument('bundle')) { + try { + $foundBundle = $kernel->getBundle($input->getArgument('bundle')); + $transPaths = array($foundBundle->getPath().'/Resources/translations'); + if ($this->defaultTransPath) { + $transPaths[] = $this->defaultTransPath.'/'.$foundBundle->getName(); + } + $transPaths[] = sprintf('%s/Resources/%s/translations', $kernel->getRootDir(), $foundBundle->getName()); + $viewsPaths = array($foundBundle->getPath().'/Resources/views'); + if ($this->defaultViewsPath) { + $viewsPaths[] = $this->defaultViewsPath.'/bundles/'.$foundBundle->getName(); + } + $viewsPaths[] = sprintf('%s/Resources/%s/views', $kernel->getRootDir(), $foundBundle->getName()); + $currentName = $foundBundle->getName(); + } catch (\InvalidArgumentException $e) { + // such a bundle does not exist, so treat the argument as path + $transPaths = array($input->getArgument('bundle').'/Resources/translations'); + $viewsPaths = array($input->getArgument('bundle').'/Resources/views'); + $currentName = $transPaths[0]; + + if (!is_dir($transPaths[0])) { + throw new InvalidArgumentException(sprintf('"%s" is neither an enabled bundle nor a directory.', $transPaths[0])); + } + } + } + + $errorIo->title('Translation Messages Extractor and Dumper'); + $errorIo->comment(sprintf('Generating "%s" translation files for "%s"', $input->getArgument('locale'), $currentName)); + + // load any messages from templates + $extractedCatalogue = new MessageCatalogue($input->getArgument('locale')); + $errorIo->comment('Parsing templates...'); + $prefix = $input->getOption('prefix'); + // @deprecated since version 3.4, to be removed in 4.0 along with the --no-prefix option + if ($input->getOption('no-prefix')) { + @trigger_error('The "--no-prefix" option is deprecated since Symfony 3.4 and will be removed in 4.0. Use the "--prefix" option with an empty string as value instead.', E_USER_DEPRECATED); + $prefix = ''; + } + $this->extractor->setPrefix($prefix); + foreach ($viewsPaths as $path) { + if (is_dir($path)) { + $this->extractor->extract($path, $extractedCatalogue); + } + } + + // load any existing messages from the translation files + $currentCatalogue = new MessageCatalogue($input->getArgument('locale')); + $errorIo->comment('Loading translation files...'); + foreach ($transPaths as $path) { + if (is_dir($path)) { + $this->reader->read($path, $currentCatalogue); + } + } + + if (null !== $domain = $input->getOption('domain')) { + $currentCatalogue = $this->filterCatalogue($currentCatalogue, $domain); + $extractedCatalogue = $this->filterCatalogue($extractedCatalogue, $domain); + } + + // process catalogues + $operation = $input->getOption('clean') + ? new TargetOperation($currentCatalogue, $extractedCatalogue) + : new MergeOperation($currentCatalogue, $extractedCatalogue); + + // Exit if no messages found. + if (!\count($operation->getDomains())) { + $errorIo->warning('No translation messages were found.'); + + return; + } + + $resultMessage = 'Translation files were successfully updated'; + + // show compiled list of messages + if (true === $input->getOption('dump-messages')) { + $extractedMessagesCount = 0; + $io->newLine(); + foreach ($operation->getDomains() as $domain) { + $newKeys = array_keys($operation->getNewMessages($domain)); + $allKeys = array_keys($operation->getMessages($domain)); + + $list = array_merge( + array_diff($allKeys, $newKeys), + array_map(function ($id) { + return sprintf('%s', $id); + }, $newKeys), + array_map(function ($id) { + return sprintf('%s', $id); + }, array_keys($operation->getObsoleteMessages($domain))) + ); + + $domainMessagesCount = \count($list); + + $io->section(sprintf('Messages extracted for domain "%s" (%d message%s)', $domain, $domainMessagesCount, $domainMessagesCount > 1 ? 's' : '')); + $io->listing($list); + + $extractedMessagesCount += $domainMessagesCount; + } + + if ('xlf' == $input->getOption('output-format')) { + $errorIo->comment('Xliff output version is 1.2'); + } + + $resultMessage = sprintf('%d message%s successfully extracted', $extractedMessagesCount, $extractedMessagesCount > 1 ? 's were' : ' was'); + } + + if (true === $input->getOption('no-backup')) { + $this->writer->disableBackup(); + } + + // save the files + if (true === $input->getOption('force')) { + $errorIo->comment('Writing files...'); + + $bundleTransPath = false; + foreach ($transPaths as $path) { + if (is_dir($path)) { + $bundleTransPath = $path; + } + } + + if (!$bundleTransPath) { + $bundleTransPath = end($transPaths); + } + + $this->writer->write($operation->getResult(), $input->getOption('output-format'), array('path' => $bundleTransPath, 'default_locale' => $this->defaultLocale)); + + if (true === $input->getOption('dump-messages')) { + $resultMessage .= ' and translation files were updated'; + } + } + + $errorIo->success($resultMessage.'.'); + } + + private function filterCatalogue(MessageCatalogue $catalogue, $domain) + { + $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); + + if ($messages = $catalogue->all($domain)) { + $filteredCatalogue->add($messages, $domain); + } + foreach ($catalogue->getResources() as $resource) { + $filteredCatalogue->addResource($resource); + } + if ($metadata = $catalogue->getMetadata('', $domain)) { + foreach ($metadata as $k => $v) { + $filteredCatalogue->setMetadata($k, $v, $domain); + } + } + + return $filteredCatalogue; + } +} diff --git a/vendor/symfony/framework-bundle/Command/WorkflowDumpCommand.php b/vendor/symfony/framework-bundle/Command/WorkflowDumpCommand.php new file mode 100644 index 0000000..bdc5690 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/WorkflowDumpCommand.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Console\Exception\InvalidArgumentException; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Workflow\Dumper\GraphvizDumper; +use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper; +use Symfony\Component\Workflow\Marking; + +/** + * @author Grégoire Pineau + * + * @final since version 3.4 + */ +class WorkflowDumpCommand extends ContainerAwareCommand +{ + protected static $defaultName = 'workflow:dump'; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'), + new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'), + )) + ->setDescription('Dump a workflow') + ->setHelp(<<<'EOF' +The %command.name% command dumps the graphical representation of a +workflow in DOT format + + %command.full_name% | dot -Tpng > workflow.png + +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $container = $this->getApplication()->getKernel()->getContainer(); + $serviceId = $input->getArgument('name'); + if ($container->has('workflow.'.$serviceId)) { + $workflow = $container->get('workflow.'.$serviceId); + $dumper = new GraphvizDumper(); + } elseif ($container->has('state_machine.'.$serviceId)) { + $workflow = $container->get('state_machine.'.$serviceId); + $dumper = new StateMachineGraphvizDumper(); + } else { + throw new InvalidArgumentException(sprintf('No service found for "workflow.%1$s" nor "state_machine.%1$s".', $serviceId)); + } + + $marking = new Marking(); + + foreach ($input->getArgument('marking') as $place) { + $marking->mark($place); + } + + $output->writeln($dumper->dump($workflow->getDefinition(), $marking)); + } +} diff --git a/vendor/symfony/framework-bundle/Command/XliffLintCommand.php b/vendor/symfony/framework-bundle/Command/XliffLintCommand.php new file mode 100644 index 0000000..8baebbf --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/XliffLintCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Translation\Command\XliffLintCommand as BaseLintCommand; + +/** + * Validates XLIFF files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + * @author Javier Eguiluz + * + * @final since version 3.4 + */ +class XliffLintCommand extends BaseLintCommand +{ + protected static $defaultName = 'lint:xliff'; + + public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + { + if (\func_num_args()) { + @trigger_error(sprintf('Passing a constructor argument in "%s()" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); + } + + if (null === $directoryIteratorProvider) { + $directoryIteratorProvider = function ($directory, $default) { + if (!is_dir($directory)) { + $directory = $this->getApplication()->getKernel()->locateResource($directory); + } + + return $default($directory); + }; + } + + if (null === $isReadableProvider) { + $isReadableProvider = function ($fileOrDirectory, $default) { + return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); + }; + } + + parent::__construct($name, $directoryIteratorProvider, $isReadableProvider); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + parent::configure(); + + $this->setHelp($this->getHelp().<<<'EOF' + +Or find all files in a bundle: + + php %command.full_name% @AcmeDemoBundle + +EOF + ); + } +} diff --git a/vendor/symfony/framework-bundle/Command/YamlLintCommand.php b/vendor/symfony/framework-bundle/Command/YamlLintCommand.php new file mode 100644 index 0000000..73dcd05 --- /dev/null +++ b/vendor/symfony/framework-bundle/Command/YamlLintCommand.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\Bundle\FrameworkBundle\Command; + +use Symfony\Component\Yaml\Command\LintCommand as BaseLintCommand; + +/** + * Validates YAML files syntax and outputs encountered errors. + * + * @author Grégoire Pineau + * @author Robin Chalas + * + * @final since version 3.4 + */ +class YamlLintCommand extends BaseLintCommand +{ + protected static $defaultName = 'lint:yaml'; + + public function __construct($name = null, $directoryIteratorProvider = null, $isReadableProvider = null) + { + if (\func_num_args()) { + @trigger_error(sprintf('Passing a constructor argument in "%s()" is deprecated since Symfony 3.4 and will be removed in 4.0. If the command was registered by convention, make it a service instead.', __METHOD__), E_USER_DEPRECATED); + } + + if (null === $directoryIteratorProvider) { + $directoryIteratorProvider = function ($directory, $default) { + if (!is_dir($directory)) { + $directory = $this->getApplication()->getKernel()->locateResource($directory); + } + + return $default($directory); + }; + } + + if (null === $isReadableProvider) { + $isReadableProvider = function ($fileOrDirectory, $default) { + return 0 === strpos($fileOrDirectory, '@') || $default($fileOrDirectory); + }; + } + + parent::__construct($name, $directoryIteratorProvider, $isReadableProvider); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + parent::configure(); + + $this->setHelp($this->getHelp().<<<'EOF' + +Or find all files in a bundle: + + php %command.full_name% @AcmeDemoBundle + +EOF + ); + } +} diff --git a/vendor/symfony/framework-bundle/Console/Application.php b/vendor/symfony/framework-bundle/Console/Application.php new file mode 100644 index 0000000..10255a4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Application.php @@ -0,0 +1,198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console; + +use Symfony\Component\Console\Application as BaseApplication; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\Debug\Exception\FatalThrowableError; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * @author Fabien Potencier + */ +class Application extends BaseApplication +{ + private $kernel; + private $commandsRegistered = false; + private $registrationErrors = array(); + + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + + parent::__construct('Symfony', Kernel::VERSION); + + $inputDefinition = $this->getDefinition(); + $inputDefinition->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', $kernel->getEnvironment())); + $inputDefinition->addOption(new InputOption('--no-debug', null, InputOption::VALUE_NONE, 'Switches off debug mode.')); + } + + /** + * Gets the Kernel associated with this Console. + * + * @return KernelInterface A KernelInterface instance + */ + public function getKernel() + { + return $this->kernel; + } + + /** + * Runs the current application. + * + * @return int 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + $this->kernel->boot(); + + $this->setDispatcher($this->kernel->getContainer()->get('event_dispatcher')); + + $this->registerCommands(); + + if ($this->registrationErrors) { + $this->renderRegistrationErrors($input, $output); + } + + return parent::doRun($input, $output); + } + + /** + * {@inheritdoc} + */ + protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) + { + if ($this->registrationErrors) { + $this->renderRegistrationErrors($input, $output); + } + + return parent::doRunCommand($command, $input, $output); + } + + /** + * {@inheritdoc} + */ + public function find($name) + { + $this->registerCommands(); + + return parent::find($name); + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + $this->registerCommands(); + + $command = parent::get($name); + + if ($command instanceof ContainerAwareInterface) { + $command->setContainer($this->kernel->getContainer()); + } + + return $command; + } + + /** + * {@inheritdoc} + */ + public function all($namespace = null) + { + $this->registerCommands(); + + return parent::all($namespace); + } + + /** + * {@inheritdoc} + */ + public function getLongVersion() + { + return parent::getLongVersion().sprintf(' (kernel: %s, env: %s, debug: %s)', $this->kernel->getName(), $this->kernel->getEnvironment(), $this->kernel->isDebug() ? 'true' : 'false'); + } + + public function add(Command $command) + { + $this->registerCommands(); + + return parent::add($command); + } + + protected function registerCommands() + { + if ($this->commandsRegistered) { + return; + } + + $this->commandsRegistered = true; + + $this->kernel->boot(); + + $container = $this->kernel->getContainer(); + + foreach ($this->kernel->getBundles() as $bundle) { + if ($bundle instanceof Bundle) { + try { + $bundle->registerCommands($this); + } catch (\Exception $e) { + $this->registrationErrors[] = $e; + } catch (\Throwable $e) { + $this->registrationErrors[] = new FatalThrowableError($e); + } + } + } + + if ($container->has('console.command_loader')) { + $this->setCommandLoader($container->get('console.command_loader')); + } + + if ($container->hasParameter('console.command.ids')) { + $lazyCommandIds = $container->hasParameter('console.lazy_command.ids') ? $container->getParameter('console.lazy_command.ids') : array(); + foreach ($container->getParameter('console.command.ids') as $id) { + if (!isset($lazyCommandIds[$id])) { + try { + $this->add($container->get($id)); + } catch (\Exception $e) { + $this->registrationErrors[] = $e; + } catch (\Throwable $e) { + $this->registrationErrors[] = new FatalThrowableError($e); + } + } + } + } + } + + private function renderRegistrationErrors(InputInterface $input, OutputInterface $output) + { + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + (new SymfonyStyle($input, $output))->warning('Some commands could not be registered:'); + + foreach ($this->registrationErrors as $error) { + $this->doRenderException($error, $output); + } + + $this->registrationErrors = array(); + } +} diff --git a/vendor/symfony/framework-bundle/Console/Descriptor/Descriptor.php b/vendor/symfony/framework-bundle/Console/Descriptor/Descriptor.php new file mode 100644 index 0000000..be1ef79 --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Descriptor/Descriptor.php @@ -0,0 +1,282 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\Console\Descriptor\DescriptorInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +abstract class Descriptor implements DescriptorInterface +{ + /** + * @var OutputInterface + */ + protected $output; + + /** + * {@inheritdoc} + */ + public function describe(OutputInterface $output, $object, array $options = array()) + { + $this->output = $output; + + switch (true) { + case $object instanceof RouteCollection: + $this->describeRouteCollection($object, $options); + break; + case $object instanceof Route: + $this->describeRoute($object, $options); + break; + case $object instanceof ParameterBag: + $this->describeContainerParameters($object, $options); + break; + case $object instanceof ContainerBuilder && isset($options['group_by']) && 'tags' === $options['group_by']: + $this->describeContainerTags($object, $options); + break; + case $object instanceof ContainerBuilder && isset($options['id']): + $this->describeContainerService($this->resolveServiceDefinition($object, $options['id']), $options, $object); + break; + case $object instanceof ContainerBuilder && isset($options['parameter']): + $this->describeContainerParameter($object->resolveEnvPlaceholders($object->getParameter($options['parameter'])), $options); + break; + case $object instanceof ContainerBuilder: + $this->describeContainerServices($object, $options); + break; + case $object instanceof Definition: + $this->describeContainerDefinition($object, $options); + break; + case $object instanceof Alias: + $this->describeContainerAlias($object, $options); + break; + case $object instanceof EventDispatcherInterface: + $this->describeEventDispatcherListeners($object, $options); + break; + case \is_callable($object): + $this->describeCallable($object, $options); + break; + default: + throw new \InvalidArgumentException(sprintf('Object of type "%s" is not describable.', \get_class($object))); + } + } + + /** + * Returns the output. + * + * @return OutputInterface The output + */ + protected function getOutput() + { + return $this->output; + } + + /** + * Writes content to output. + * + * @param string $content + * @param bool $decorated + */ + protected function write($content, $decorated = false) + { + $this->output->write($content, false, $decorated ? OutputInterface::OUTPUT_NORMAL : OutputInterface::OUTPUT_RAW); + } + + /** + * Describes an InputArgument instance. + */ + abstract protected function describeRouteCollection(RouteCollection $routes, array $options = array()); + + /** + * Describes an InputOption instance. + */ + abstract protected function describeRoute(Route $route, array $options = array()); + + /** + * Describes container parameters. + */ + abstract protected function describeContainerParameters(ParameterBag $parameters, array $options = array()); + + /** + * Describes container tags. + */ + abstract protected function describeContainerTags(ContainerBuilder $builder, array $options = array()); + + /** + * Describes a container service by its name. + * + * Common options are: + * * name: name of described service + * + * @param Definition|Alias|object $service + * @param array $options + * @param ContainerBuilder|null $builder + */ + abstract protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null); + + /** + * Describes container services. + * + * Common options are: + * * tag: filters described services by given tag + */ + abstract protected function describeContainerServices(ContainerBuilder $builder, array $options = array()); + + /** + * Describes a service definition. + */ + abstract protected function describeContainerDefinition(Definition $definition, array $options = array()); + + /** + * Describes a service alias. + */ + abstract protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null); + + /** + * Describes a container parameter. + */ + abstract protected function describeContainerParameter($parameter, array $options = array()); + + /** + * Describes event dispatcher listeners. + * + * Common options are: + * * name: name of listened event + */ + abstract protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()); + + /** + * Describes a callable. + * + * @param callable $callable + * @param array $options + */ + abstract protected function describeCallable($callable, array $options = array()); + + /** + * Formats a value as string. + * + * @param mixed $value + * + * @return string + */ + protected function formatValue($value) + { + if (\is_object($value)) { + return sprintf('object(%s)', \get_class($value)); + } + + if (\is_string($value)) { + return $value; + } + + return preg_replace("/\n\s*/s", '', var_export($value, true)); + } + + /** + * Formats a parameter. + * + * @param mixed $value + * + * @return string + */ + protected function formatParameter($value) + { + if (\is_bool($value) || \is_array($value) || (null === $value)) { + $jsonString = json_encode($value); + + if (preg_match('/^(.{60})./us', $jsonString, $matches)) { + return $matches[1].'...'; + } + + return $jsonString; + } + + return (string) $value; + } + + /** + * @param ContainerBuilder $builder + * @param string $serviceId + * + * @return mixed + */ + protected function resolveServiceDefinition(ContainerBuilder $builder, $serviceId) + { + if ($builder->hasDefinition($serviceId)) { + return $builder->getDefinition($serviceId); + } + + // Some service IDs don't have a Definition, they're simply an Alias + if ($builder->hasAlias($serviceId)) { + return $builder->getAlias($serviceId); + } + + // the service has been injected in some special way, just return the service + return $builder->get($serviceId); + } + + /** + * @param ContainerBuilder $builder + * @param bool $showPrivate + * + * @return array + */ + protected function findDefinitionsByTag(ContainerBuilder $builder, $showPrivate) + { + $definitions = array(); + $tags = $builder->findTags(); + asort($tags); + + foreach ($tags as $tag) { + foreach ($builder->findTaggedServiceIds($tag) as $serviceId => $attributes) { + $definition = $this->resolveServiceDefinition($builder, $serviceId); + + if (!$definition instanceof Definition || !$showPrivate && !$definition->isPublic()) { + continue; + } + + if (!isset($definitions[$tag])) { + $definitions[$tag] = array(); + } + + $definitions[$tag][$serviceId] = $definition; + } + } + + return $definitions; + } + + protected function sortParameters(ParameterBag $parameters) + { + $parameters = $parameters->all(); + ksort($parameters); + + return $parameters; + } + + protected function sortServiceIds(array $serviceIds) + { + asort($serviceIds); + + return $serviceIds; + } +} diff --git a/vendor/symfony/framework-bundle/Console/Descriptor/JsonDescriptor.php b/vendor/symfony/framework-bundle/Console/Descriptor/JsonDescriptor.php new file mode 100644 index 0000000..e51c040 --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Descriptor/JsonDescriptor.php @@ -0,0 +1,416 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class JsonDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $data = array(); + foreach ($routes->all() as $name => $route) { + $data[$name] = $this->getRouteData($route); + } + + $this->writeData($data, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $this->writeData($this->getRouteData($route), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $this->writeData($this->sortParameters($parameters), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + $data = array(); + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $data[$tag] = array(); + foreach ($definitions as $definition) { + $data[$tag][] = $this->getContainerDefinitionData($definition, true); + } + } + + $this->writeData($data, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + if ($service instanceof Alias) { + $this->describeContainerAlias($service, $options, $builder); + } elseif ($service instanceof Definition) { + $this->writeData($this->getContainerDefinitionData($service, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']), $options); + } else { + $this->writeData(\get_class($service), $options); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $showPrivate = isset($options['show_private']) && $options['show_private']; + $omitTags = isset($options['omit_tags']) && $options['omit_tags']; + $showArguments = isset($options['show_arguments']) && $options['show_arguments']; + $data = array('definitions' => array(), 'aliases' => array(), 'services' => array()); + + if (isset($options['filter'])) { + $serviceIds = array_filter($serviceIds, $options['filter']); + } + + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $service = $this->resolveServiceDefinition($builder, $serviceId); + + if ($service instanceof Alias) { + if ($showPrivate || ($service->isPublic() && !$service->isPrivate())) { + $data['aliases'][$serviceId] = $this->getContainerAliasData($service); + } + } elseif ($service instanceof Definition) { + if (($showPrivate || ($service->isPublic() && !$service->isPrivate()))) { + $data['definitions'][$serviceId] = $this->getContainerDefinitionData($service, $omitTags, $showArguments); + } + } else { + $data['services'][$serviceId] = \get_class($service); + } + } + + $this->writeData($data, $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + $this->writeData($this->getContainerDefinitionData($definition, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments']), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) + { + if (!$builder) { + return $this->writeData($this->getContainerAliasData($alias), $options); + } + + $this->writeData( + array($this->getContainerAliasData($alias), $this->getContainerDefinitionData($builder->getDefinition((string) $alias), isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'])), + array_merge($options, array('id' => (string) $alias)) + ); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $this->writeData($this->getEventDispatcherListenersData($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $this->writeData($this->getCallableData($callable, $options), $options); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $key = isset($options['parameter']) ? $options['parameter'] : ''; + + $this->writeData(array($key => $parameter), $options); + } + + /** + * Writes data as json. + * + * @return array|string + */ + private function writeData(array $data, array $options) + { + $flags = isset($options['json_encoding']) ? $options['json_encoding'] : 0; + $this->write(json_encode($data, $flags | JSON_PRETTY_PRINT)."\n"); + } + + /** + * @return array + */ + protected function getRouteData(Route $route) + { + return array( + 'path' => $route->getPath(), + 'pathRegex' => $route->compile()->getRegex(), + 'host' => '' !== $route->getHost() ? $route->getHost() : 'ANY', + 'hostRegex' => '' !== $route->getHost() ? $route->compile()->getHostRegex() : '', + 'scheme' => $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY', + 'method' => $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY', + 'class' => \get_class($route), + 'defaults' => $route->getDefaults(), + 'requirements' => $route->getRequirements() ?: 'NO CUSTOM', + 'options' => $route->getOptions(), + ); + } + + /** + * @param Definition $definition + * @param bool $omitTags + * + * @return array + */ + private function getContainerDefinitionData(Definition $definition, $omitTags = false, $showArguments = false) + { + $data = array( + 'class' => (string) $definition->getClass(), + 'public' => $definition->isPublic() && !$definition->isPrivate(), + 'synthetic' => $definition->isSynthetic(), + 'lazy' => $definition->isLazy(), + 'shared' => $definition->isShared(), + 'abstract' => $definition->isAbstract(), + 'autowire' => $definition->isAutowired(), + 'autoconfigure' => $definition->isAutoconfigured(), + ); + + // forward compatibility with DependencyInjection component in version 4.0 + if (method_exists($definition, 'getAutowiringTypes')) { + foreach ($definition->getAutowiringTypes(false) as $autowiringType) { + $data['autowiring_types'][] = $autowiringType; + } + } + + if ($showArguments) { + $data['arguments'] = $this->describeValue($definition->getArguments(), $omitTags, $showArguments); + } + + $data['file'] = $definition->getFile(); + + if ($factory = $definition->getFactory()) { + if (\is_array($factory)) { + if ($factory[0] instanceof Reference) { + $data['factory_service'] = (string) $factory[0]; + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $data['factory_class'] = $factory[0]; + } + $data['factory_method'] = $factory[1]; + } else { + $data['factory_function'] = $factory; + } + } + + $calls = $definition->getMethodCalls(); + if (\count($calls) > 0) { + $data['calls'] = array(); + foreach ($calls as $callData) { + $data['calls'][] = $callData[0]; + } + } + + if (!$omitTags) { + $data['tags'] = array(); + foreach ($definition->getTags() as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $data['tags'][] = array('name' => $tagName, 'parameters' => $parameters); + } + } + } + + return $data; + } + + /** + * @return array + */ + private function getContainerAliasData(Alias $alias) + { + return array( + 'service' => (string) $alias, + 'public' => $alias->isPublic() && !$alias->isPrivate(), + ); + } + + /** + * @param EventDispatcherInterface $eventDispatcher + * @param string|null $event + * + * @return array + */ + private function getEventDispatcherListenersData(EventDispatcherInterface $eventDispatcher, $event = null) + { + $data = array(); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + foreach ($registeredListeners as $listener) { + $l = $this->getCallableData($listener); + $l['priority'] = $eventDispatcher->getListenerPriority($event, $listener); + $data[] = $l; + } + } else { + ksort($registeredListeners); + + foreach ($registeredListeners as $eventListened => $eventListeners) { + foreach ($eventListeners as $eventListener) { + $l = $this->getCallableData($eventListener); + $l['priority'] = $eventDispatcher->getListenerPriority($eventListened, $eventListener); + $data[$eventListened][] = $l; + } + } + } + + return $data; + } + + /** + * @param callable $callable + * @param array $options + * + * @return array + */ + private function getCallableData($callable, array $options = array()) + { + $data = array(); + + if (\is_array($callable)) { + $data['type'] = 'function'; + + if (\is_object($callable[0])) { + $data['name'] = $callable[1]; + $data['class'] = \get_class($callable[0]); + } else { + if (0 !== strpos($callable[1], 'parent::')) { + $data['name'] = $callable[1]; + $data['class'] = $callable[0]; + $data['static'] = true; + } else { + $data['name'] = substr($callable[1], 8); + $data['class'] = $callable[0]; + $data['static'] = true; + $data['parent'] = true; + } + } + + return $data; + } + + if (\is_string($callable)) { + $data['type'] = 'function'; + + if (false === strpos($callable, '::')) { + $data['name'] = $callable; + } else { + $callableParts = explode('::', $callable); + + $data['name'] = $callableParts[1]; + $data['class'] = $callableParts[0]; + $data['static'] = true; + } + + return $data; + } + + if ($callable instanceof \Closure) { + $data['type'] = 'closure'; + + return $data; + } + + if (method_exists($callable, '__invoke')) { + $data['type'] = 'object'; + $data['name'] = \get_class($callable); + + return $data; + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } + + private function describeValue($value, $omitTags, $showArguments) + { + if (\is_array($value)) { + $data = array(); + foreach ($value as $k => $v) { + $data[$k] = $this->describeValue($v, $omitTags, $showArguments); + } + + return $data; + } + + if ($value instanceof ServiceClosureArgument) { + $value = $value->getValues()[0]; + } + + if ($value instanceof Reference) { + return array( + 'type' => 'service', + 'id' => (string) $value, + ); + } + + if ($value instanceof ArgumentInterface) { + return $this->describeValue($value->getValues(), $omitTags, $showArguments); + } + + if ($value instanceof Definition) { + return $this->getContainerDefinitionData($value, $omitTags, $showArguments); + } + + return $value; + } +} diff --git a/vendor/symfony/framework-bundle/Console/Descriptor/MarkdownDescriptor.php b/vendor/symfony/framework-bundle/Console/Descriptor/MarkdownDescriptor.php new file mode 100644 index 0000000..339b2db --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Descriptor/MarkdownDescriptor.php @@ -0,0 +1,387 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class MarkdownDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $first = true; + foreach ($routes->all() as $name => $route) { + if ($first) { + $first = false; + } else { + $this->write("\n\n"); + } + $this->describeRoute($route, array('name' => $name)); + } + $this->write("\n"); + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $output = '- Path: '.$route->getPath() + ."\n".'- Path Regex: '.$route->compile()->getRegex() + ."\n".'- Host: '.('' !== $route->getHost() ? $route->getHost() : 'ANY') + ."\n".'- Host Regex: '.('' !== $route->getHost() ? $route->compile()->getHostRegex() : '') + ."\n".'- Scheme: '.($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY') + ."\n".'- Method: '.($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY') + ."\n".'- Class: '.\get_class($route) + ."\n".'- Defaults: '.$this->formatRouterConfig($route->getDefaults()) + ."\n".'- Requirements: '.($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM') + ."\n".'- Options: '.$this->formatRouterConfig($route->getOptions()); + + $this->write(isset($options['name']) + ? $options['name']."\n".str_repeat('-', \strlen($options['name']))."\n\n".$output + : $output); + $this->write("\n"); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $this->write("Container parameters\n====================\n"); + foreach ($this->sortParameters($parameters) as $key => $value) { + $this->write(sprintf("\n- `%s`: `%s`", $key, $this->formatParameter($value))); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + $this->write("Container tags\n=============="); + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $this->write("\n\n".$tag."\n".str_repeat('-', \strlen($tag))); + foreach ($definitions as $serviceId => $definition) { + $this->write("\n\n"); + $this->describeContainerDefinition($definition, array('omit_tags' => true, 'id' => $serviceId)); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + $childOptions = array_merge($options, array('id' => $options['id'], 'as_array' => true)); + + if ($service instanceof Alias) { + $this->describeContainerAlias($service, $childOptions, $builder); + } elseif ($service instanceof Definition) { + $this->describeContainerDefinition($service, $childOptions); + } else { + $this->write(sprintf('**`%s`:** `%s`', $options['id'], \get_class($service))); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + + $title = $showPrivate ? 'Public and private services' : 'Public services'; + if (isset($options['tag'])) { + $title .= ' with tag `'.$options['tag'].'`'; + } + $this->write($title."\n".str_repeat('=', \strlen($title))); + + $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $showArguments = isset($options['show_arguments']) && $options['show_arguments']; + $services = array('definitions' => array(), 'aliases' => array(), 'services' => array()); + + if (isset($options['filter'])) { + $serviceIds = array_filter($serviceIds, $options['filter']); + } + + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $service = $this->resolveServiceDefinition($builder, $serviceId); + + if ($service instanceof Alias) { + if ($showPrivate || ($service->isPublic() && !$service->isPrivate())) { + $services['aliases'][$serviceId] = $service; + } + } elseif ($service instanceof Definition) { + if (($showPrivate || ($service->isPublic() && !$service->isPrivate()))) { + $services['definitions'][$serviceId] = $service; + } + } else { + $services['services'][$serviceId] = $service; + } + } + + if (!empty($services['definitions'])) { + $this->write("\n\nDefinitions\n-----------\n"); + foreach ($services['definitions'] as $id => $service) { + $this->write("\n"); + $this->describeContainerDefinition($service, array('id' => $id, 'show_arguments' => $showArguments)); + } + } + + if (!empty($services['aliases'])) { + $this->write("\n\nAliases\n-------\n"); + foreach ($services['aliases'] as $id => $service) { + $this->write("\n"); + $this->describeContainerAlias($service, array('id' => $id)); + } + } + + if (!empty($services['services'])) { + $this->write("\n\nServices\n--------\n"); + foreach ($services['services'] as $id => $service) { + $this->write("\n"); + $this->write(sprintf('- `%s`: `%s`', $id, \get_class($service))); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + $output = '- Class: `'.$definition->getClass().'`' + ."\n".'- Public: '.($definition->isPublic() && !$definition->isPrivate() ? 'yes' : 'no') + ."\n".'- Synthetic: '.($definition->isSynthetic() ? 'yes' : 'no') + ."\n".'- Lazy: '.($definition->isLazy() ? 'yes' : 'no') + ."\n".'- Shared: '.($definition->isShared() ? 'yes' : 'no') + ."\n".'- Abstract: '.($definition->isAbstract() ? 'yes' : 'no') + ."\n".'- Autowired: '.($definition->isAutowired() ? 'yes' : 'no') + ."\n".'- Autoconfigured: '.($definition->isAutoconfigured() ? 'yes' : 'no') + ; + + // forward compatibility with DependencyInjection component in version 4.0 + if (method_exists($definition, 'getAutowiringTypes')) { + foreach ($definition->getAutowiringTypes(false) as $autowiringType) { + $output .= "\n".'- Autowiring Type: `'.$autowiringType.'`'; + } + } + + if (isset($options['show_arguments']) && $options['show_arguments']) { + $output .= "\n".'- Arguments: '.($definition->getArguments() ? 'yes' : 'no'); + } + + if ($definition->getFile()) { + $output .= "\n".'- File: `'.$definition->getFile().'`'; + } + + if ($factory = $definition->getFactory()) { + if (\is_array($factory)) { + if ($factory[0] instanceof Reference) { + $output .= "\n".'- Factory Service: `'.$factory[0].'`'; + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $output .= "\n".'- Factory Class: `'.$factory[0].'`'; + } + $output .= "\n".'- Factory Method: `'.$factory[1].'`'; + } else { + $output .= "\n".'- Factory Function: `'.$factory.'`'; + } + } + + $calls = $definition->getMethodCalls(); + foreach ($calls as $callData) { + $output .= "\n".'- Call: `'.$callData[0].'`'; + } + + if (!(isset($options['omit_tags']) && $options['omit_tags'])) { + foreach ($definition->getTags() as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $output .= "\n".'- Tag: `'.$tagName.'`'; + foreach ($parameters as $name => $value) { + $output .= "\n".' - '.ucfirst($name).': '.$value; + } + } + } + } + + $this->write(isset($options['id']) ? sprintf("### %s\n\n%s\n", $options['id'], $output) : $output); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) + { + $output = '- Service: `'.$alias.'`' + ."\n".'- Public: '.($alias->isPublic() && !$alias->isPrivate() ? 'yes' : 'no'); + + if (!isset($options['id'])) { + return $this->write($output); + } + + $this->write(sprintf("### %s\n\n%s\n", $options['id'], $output)); + + if (!$builder) { + return; + } + + $this->write("\n"); + $this->describeContainerDefinition($builder->getDefinition((string) $alias), array_merge($options, array('id' => (string) $alias))); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $this->write(isset($options['parameter']) ? sprintf("%s\n%s\n\n%s", $options['parameter'], str_repeat('=', \strlen($options['parameter'])), $this->formatParameter($parameter)) : $parameter); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $event = array_key_exists('event', $options) ? $options['event'] : null; + + $title = 'Registered listeners'; + if (null !== $event) { + $title .= sprintf(' for event `%s` ordered by descending priority', $event); + } + + $this->write(sprintf('# %s', $title)."\n"); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + foreach ($registeredListeners as $order => $listener) { + $this->write("\n".sprintf('## Listener %d', $order + 1)."\n"); + $this->describeCallable($listener); + $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($event, $listener))."\n"); + } + } else { + ksort($registeredListeners); + + foreach ($registeredListeners as $eventListened => $eventListeners) { + $this->write("\n".sprintf('## %s', $eventListened)."\n"); + + foreach ($eventListeners as $order => $eventListener) { + $this->write("\n".sprintf('### Listener %d', $order + 1)."\n"); + $this->describeCallable($eventListener); + $this->write(sprintf('- Priority: `%d`', $eventDispatcher->getListenerPriority($eventListened, $eventListener))."\n"); + } + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $string = ''; + + if (\is_array($callable)) { + $string .= "\n- Type: `function`"; + + if (\is_object($callable[0])) { + $string .= "\n".sprintf('- Name: `%s`', $callable[1]); + $string .= "\n".sprintf('- Class: `%s`', \get_class($callable[0])); + } else { + if (0 !== strpos($callable[1], 'parent::')) { + $string .= "\n".sprintf('- Name: `%s`', $callable[1]); + $string .= "\n".sprintf('- Class: `%s`', $callable[0]); + $string .= "\n- Static: yes"; + } else { + $string .= "\n".sprintf('- Name: `%s`', substr($callable[1], 8)); + $string .= "\n".sprintf('- Class: `%s`', $callable[0]); + $string .= "\n- Static: yes"; + $string .= "\n- Parent: yes"; + } + } + + return $this->write($string."\n"); + } + + if (\is_string($callable)) { + $string .= "\n- Type: `function`"; + + if (false === strpos($callable, '::')) { + $string .= "\n".sprintf('- Name: `%s`', $callable); + } else { + $callableParts = explode('::', $callable); + + $string .= "\n".sprintf('- Name: `%s`', $callableParts[1]); + $string .= "\n".sprintf('- Class: `%s`', $callableParts[0]); + $string .= "\n- Static: yes"; + } + + return $this->write($string."\n"); + } + + if ($callable instanceof \Closure) { + $string .= "\n- Type: `closure`"; + + return $this->write($string."\n"); + } + + if (method_exists($callable, '__invoke')) { + $string .= "\n- Type: `object`"; + $string .= "\n".sprintf('- Name: `%s`', \get_class($callable)); + + return $this->write($string."\n"); + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } + + /** + * @return string + */ + private function formatRouterConfig(array $array) + { + if (!$array) { + return 'NONE'; + } + + $string = ''; + ksort($array); + foreach ($array as $name => $value) { + $string .= "\n".' - `'.$name.'`: '.$this->formatValue($value); + } + + return $string; + } +} diff --git a/vendor/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php b/vendor/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php new file mode 100644 index 0000000..d0a2efd --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Descriptor/TextDescriptor.php @@ -0,0 +1,498 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class TextDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $showControllers = isset($options['show_controllers']) && $options['show_controllers']; + + $tableHeaders = array('Name', 'Method', 'Scheme', 'Host', 'Path'); + if ($showControllers) { + $tableHeaders[] = 'Controller'; + } + + $tableRows = array(); + foreach ($routes->all() as $name => $route) { + $row = array( + $name, + $route->getMethods() ? implode('|', $route->getMethods()) : 'ANY', + $route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY', + '' !== $route->getHost() ? $route->getHost() : 'ANY', + $route->getPath(), + ); + + if ($showControllers) { + $controller = $route->getDefault('_controller'); + if ($controller instanceof \Closure) { + $controller = 'Closure'; + } elseif (\is_object($controller)) { + $controller = \get_class($controller); + } + $row[] = $controller; + } + + $tableRows[] = $row; + } + + if (isset($options['output'])) { + $options['output']->table($tableHeaders, $tableRows); + } else { + $table = new Table($this->getOutput()); + $table->setHeaders($tableHeaders)->setRows($tableRows); + $table->render(); + } + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $tableHeaders = array('Property', 'Value'); + $tableRows = array( + array('Route Name', isset($options['name']) ? $options['name'] : ''), + array('Path', $route->getPath()), + array('Path Regex', $route->compile()->getRegex()), + array('Host', ('' !== $route->getHost() ? $route->getHost() : 'ANY')), + array('Host Regex', ('' !== $route->getHost() ? $route->compile()->getHostRegex() : '')), + array('Scheme', ($route->getSchemes() ? implode('|', $route->getSchemes()) : 'ANY')), + array('Method', ($route->getMethods() ? implode('|', $route->getMethods()) : 'ANY')), + array('Requirements', ($route->getRequirements() ? $this->formatRouterConfig($route->getRequirements()) : 'NO CUSTOM')), + array('Class', \get_class($route)), + array('Defaults', $this->formatRouterConfig($route->getDefaults())), + array('Options', $this->formatRouterConfig($route->getOptions())), + ); + if (isset($options['callable'])) { + $tableRows[] = array('Callable', $options['callable']); + } + + $table = new Table($this->getOutput()); + $table->setHeaders($tableHeaders)->setRows($tableRows); + $table->render(); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $tableHeaders = array('Parameter', 'Value'); + + $tableRows = array(); + foreach ($this->sortParameters($parameters) as $parameter => $value) { + $tableRows[] = array($parameter, $this->formatParameter($value)); + } + + $options['output']->title('Symfony Container Parameters'); + $options['output']->table($tableHeaders, $tableRows); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + + if ($showPrivate) { + $options['output']->title('Symfony Container Public and Private Tags'); + } else { + $options['output']->title('Symfony Container Public Tags'); + } + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $options['output']->section(sprintf('"%s" tag', $tag)); + $options['output']->listing(array_keys($definitions)); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + if ($service instanceof Alias) { + $this->describeContainerAlias($service, $options, $builder); + } elseif ($service instanceof Definition) { + $this->describeContainerDefinition($service, $options); + } else { + $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); + $options['output']->table( + array('Service ID', 'Class'), + array( + array(isset($options['id']) ? $options['id'] : '-', \get_class($service)), + ) + ); + } + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $showPrivate = isset($options['show_private']) && $options['show_private']; + $showTag = isset($options['tag']) ? $options['tag'] : null; + + if ($showPrivate) { + $title = 'Symfony Container Public and Private Services'; + } else { + $title = 'Symfony Container Public Services'; + } + + if ($showTag) { + $title .= sprintf(' Tagged with "%s" Tag', $options['tag']); + } + + $options['output']->title($title); + + $serviceIds = isset($options['tag']) && $options['tag'] ? array_keys($builder->findTaggedServiceIds($options['tag'])) : $builder->getServiceIds(); + $maxTags = array(); + + if (isset($options['filter'])) { + $serviceIds = array_filter($serviceIds, $options['filter']); + } + + foreach ($serviceIds as $key => $serviceId) { + $definition = $this->resolveServiceDefinition($builder, $serviceId); + if ($definition instanceof Definition) { + // filter out private services unless shown explicitly + if (!$showPrivate && (!$definition->isPublic() || $definition->isPrivate())) { + unset($serviceIds[$key]); + continue; + } + if ($showTag) { + $tags = $definition->getTag($showTag); + foreach ($tags as $tag) { + foreach ($tag as $key => $value) { + if (!isset($maxTags[$key])) { + $maxTags[$key] = \strlen($key); + } + if (\strlen($value) > $maxTags[$key]) { + $maxTags[$key] = \strlen($value); + } + } + } + } + } elseif ($definition instanceof Alias) { + if (!$showPrivate && (!$definition->isPublic() || $definition->isPrivate())) { + unset($serviceIds[$key]); + continue; + } + } + } + + $tagsCount = \count($maxTags); + $tagsNames = array_keys($maxTags); + + $tableHeaders = array_merge(array('Service ID'), $tagsNames, array('Class name')); + $tableRows = array(); + $rawOutput = isset($options['raw_text']) && $options['raw_text']; + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $definition = $this->resolveServiceDefinition($builder, $serviceId); + + $styledServiceId = $rawOutput ? $serviceId : sprintf('%s', OutputFormatter::escape($serviceId)); + if ($definition instanceof Definition) { + if ($showTag) { + foreach ($definition->getTag($showTag) as $key => $tag) { + $tagValues = array(); + foreach ($tagsNames as $tagName) { + $tagValues[] = isset($tag[$tagName]) ? $tag[$tagName] : ''; + } + if (0 === $key) { + $tableRows[] = array_merge(array($serviceId), $tagValues, array($definition->getClass())); + } else { + $tableRows[] = array_merge(array(' "'), $tagValues, array('')); + } + } + } else { + $tableRows[] = array($styledServiceId, $definition->getClass()); + } + } elseif ($definition instanceof Alias) { + $alias = $definition; + $tableRows[] = array_merge(array($styledServiceId, sprintf('alias for "%s"', $alias)), $tagsCount ? array_fill(0, $tagsCount, '') : array()); + } else { + $tableRows[] = array_merge(array($styledServiceId, \get_class($definition)), $tagsCount ? array_fill(0, $tagsCount, '') : array()); + } + } + + $options['output']->table($tableHeaders, $tableRows); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + if (isset($options['id'])) { + $options['output']->title(sprintf('Information for Service "%s"', $options['id'])); + } + + $tableHeaders = array('Option', 'Value'); + + $tableRows[] = array('Service ID', isset($options['id']) ? $options['id'] : '-'); + $tableRows[] = array('Class', $definition->getClass() ?: '-'); + + $omitTags = isset($options['omit_tags']) && $options['omit_tags']; + if (!$omitTags && ($tags = $definition->getTags())) { + $tagInformation = array(); + foreach ($tags as $tagName => $tagData) { + foreach ($tagData as $tagParameters) { + $parameters = array_map(function ($key, $value) { + return sprintf('%s: %s', $key, $value); + }, array_keys($tagParameters), array_values($tagParameters)); + $parameters = implode(', ', $parameters); + + if ('' === $parameters) { + $tagInformation[] = sprintf('%s', $tagName); + } else { + $tagInformation[] = sprintf('%s (%s)', $tagName, $parameters); + } + } + } + $tagInformation = implode("\n", $tagInformation); + } else { + $tagInformation = '-'; + } + $tableRows[] = array('Tags', $tagInformation); + + $calls = $definition->getMethodCalls(); + if (\count($calls) > 0) { + $callInformation = array(); + foreach ($calls as $call) { + $callInformation[] = $call[0]; + } + $tableRows[] = array('Calls', implode(', ', $callInformation)); + } + + $tableRows[] = array('Public', $definition->isPublic() && !$definition->isPrivate() ? 'yes' : 'no'); + $tableRows[] = array('Synthetic', $definition->isSynthetic() ? 'yes' : 'no'); + $tableRows[] = array('Lazy', $definition->isLazy() ? 'yes' : 'no'); + $tableRows[] = array('Shared', $definition->isShared() ? 'yes' : 'no'); + $tableRows[] = array('Abstract', $definition->isAbstract() ? 'yes' : 'no'); + $tableRows[] = array('Autowired', $definition->isAutowired() ? 'yes' : 'no'); + $tableRows[] = array('Autoconfigured', $definition->isAutoconfigured() ? 'yes' : 'no'); + + // forward compatibility with DependencyInjection component in version 4.0 + if (method_exists($definition, 'getAutowiringTypes') && $autowiringTypes = $definition->getAutowiringTypes(false)) { + $tableRows[] = array('Autowiring Types', implode(', ', $autowiringTypes)); + } + + if ($definition->getFile()) { + $tableRows[] = array('Required File', $definition->getFile() ?: '-'); + } + + if ($factory = $definition->getFactory()) { + if (\is_array($factory)) { + if ($factory[0] instanceof Reference) { + $tableRows[] = array('Factory Service', $factory[0]); + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $tableRows[] = array('Factory Class', $factory[0]); + } + $tableRows[] = array('Factory Method', $factory[1]); + } else { + $tableRows[] = array('Factory Function', $factory); + } + } + + $showArguments = isset($options['show_arguments']) && $options['show_arguments']; + $argumentsInformation = array(); + if ($showArguments && ($arguments = $definition->getArguments())) { + foreach ($arguments as $argument) { + if ($argument instanceof ServiceClosureArgument) { + $argument = $argument->getValues()[0]; + } + if ($argument instanceof Reference) { + $argumentsInformation[] = sprintf('Service(%s)', (string) $argument); + } elseif ($argument instanceof IteratorArgument) { + $argumentsInformation[] = sprintf('Iterator (%d element(s))', \count($argument->getValues())); + } elseif ($argument instanceof Definition) { + $argumentsInformation[] = 'Inlined Service'; + } else { + $argumentsInformation[] = \is_array($argument) ? sprintf('Array (%d element(s))', \count($argument)) : $argument; + } + } + + $tableRows[] = array('Arguments', implode("\n", $argumentsInformation)); + } + + $options['output']->table($tableHeaders, $tableRows); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) + { + $options['output']->comment(sprintf('This service is an alias for the service %s', (string) $alias)); + + if (!$builder) { + return; + } + + return $this->describeContainerDefinition($builder->getDefinition((string) $alias), array_merge($options, array('id' => (string) $alias))); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $options['output']->table( + array('Parameter', 'Value'), + array( + array($options['parameter'], $this->formatParameter($parameter), + ), + )); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $event = array_key_exists('event', $options) ? $options['event'] : null; + + if (null !== $event) { + $title = sprintf('Registered Listeners for "%s" Event', $event); + } else { + $title = 'Registered Listeners Grouped by Event'; + } + + $options['output']->title($title); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + $this->renderEventListenerTable($eventDispatcher, $event, $registeredListeners, $options['output']); + } else { + ksort($registeredListeners); + foreach ($registeredListeners as $eventListened => $eventListeners) { + $options['output']->section(sprintf('"%s" event', $eventListened)); + $this->renderEventListenerTable($eventDispatcher, $eventListened, $eventListeners, $options['output']); + } + } + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $this->writeText($this->formatCallable($callable), $options); + } + + private function renderEventListenerTable(EventDispatcherInterface $eventDispatcher, $event, array $eventListeners, SymfonyStyle $io) + { + $tableHeaders = array('Order', 'Callable', 'Priority'); + $tableRows = array(); + + $order = 1; + foreach ($eventListeners as $order => $listener) { + $tableRows[] = array(sprintf('#%d', $order + 1), $this->formatCallable($listener), $eventDispatcher->getListenerPriority($event, $listener)); + } + + $io->table($tableHeaders, $tableRows); + } + + /** + * @param array $config + * + * @return string + */ + private function formatRouterConfig(array $config) + { + if (empty($config)) { + return 'NONE'; + } + + ksort($config); + + $configAsString = ''; + foreach ($config as $key => $value) { + $configAsString .= sprintf("\n%s: %s", $key, $this->formatValue($value)); + } + + return trim($configAsString); + } + + /** + * @param callable $callable + * + * @return string + */ + private function formatCallable($callable) + { + if (\is_array($callable)) { + if (\is_object($callable[0])) { + return sprintf('%s::%s()', \get_class($callable[0]), $callable[1]); + } + + return sprintf('%s::%s()', $callable[0], $callable[1]); + } + + if (\is_string($callable)) { + return sprintf('%s()', $callable); + } + + if ($callable instanceof \Closure) { + return '\Closure()'; + } + + if (method_exists($callable, '__invoke')) { + return sprintf('%s::__invoke()', \get_class($callable)); + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } + + /** + * @param string $content + * @param array $options + */ + private function writeText($content, array $options = array()) + { + $this->write( + isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, + isset($options['raw_output']) ? !$options['raw_output'] : true + ); + } +} diff --git a/vendor/symfony/framework-bundle/Console/Descriptor/XmlDescriptor.php b/vendor/symfony/framework-bundle/Console/Descriptor/XmlDescriptor.php new file mode 100644 index 0000000..aa63315 --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Descriptor/XmlDescriptor.php @@ -0,0 +1,595 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Jean-François Simon + * + * @internal + */ +class XmlDescriptor extends Descriptor +{ + /** + * {@inheritdoc} + */ + protected function describeRouteCollection(RouteCollection $routes, array $options = array()) + { + $this->writeDocument($this->getRouteCollectionDocument($routes)); + } + + /** + * {@inheritdoc} + */ + protected function describeRoute(Route $route, array $options = array()) + { + $this->writeDocument($this->getRouteDocument($route, isset($options['name']) ? $options['name'] : null)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameters(ParameterBag $parameters, array $options = array()) + { + $this->writeDocument($this->getContainerParametersDocument($parameters)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerTags(ContainerBuilder $builder, array $options = array()) + { + $this->writeDocument($this->getContainerTagsDocument($builder, isset($options['show_private']) && $options['show_private'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerService($service, array $options = array(), ContainerBuilder $builder = null) + { + if (!isset($options['id'])) { + throw new \InvalidArgumentException('An "id" option must be provided.'); + } + + $this->writeDocument($this->getContainerServiceDocument($service, $options['id'], $builder, isset($options['show_arguments']) && $options['show_arguments'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerServices(ContainerBuilder $builder, array $options = array()) + { + $this->writeDocument($this->getContainerServicesDocument($builder, isset($options['tag']) ? $options['tag'] : null, isset($options['show_private']) && $options['show_private'], isset($options['show_arguments']) && $options['show_arguments'], isset($options['filter']) ? $options['filter'] : null)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerDefinition(Definition $definition, array $options = array()) + { + $this->writeDocument($this->getContainerDefinitionDocument($definition, isset($options['id']) ? $options['id'] : null, isset($options['omit_tags']) && $options['omit_tags'], isset($options['show_arguments']) && $options['show_arguments'])); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerAlias(Alias $alias, array $options = array(), ContainerBuilder $builder = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($dom->importNode($this->getContainerAliasDocument($alias, isset($options['id']) ? $options['id'] : null)->childNodes->item(0), true)); + + if (!$builder) { + return $this->writeDocument($dom); + } + + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($builder->getDefinition((string) $alias), (string) $alias)->childNodes->item(0), true)); + + $this->writeDocument($dom); + } + + /** + * {@inheritdoc} + */ + protected function describeEventDispatcherListeners(EventDispatcherInterface $eventDispatcher, array $options = array()) + { + $this->writeDocument($this->getEventDispatcherListenersDocument($eventDispatcher, array_key_exists('event', $options) ? $options['event'] : null)); + } + + /** + * {@inheritdoc} + */ + protected function describeCallable($callable, array $options = array()) + { + $this->writeDocument($this->getCallableDocument($callable)); + } + + /** + * {@inheritdoc} + */ + protected function describeContainerParameter($parameter, array $options = array()) + { + $this->writeDocument($this->getContainerParameterDocument($parameter, $options)); + } + + /** + * Writes DOM document. + * + * @return \DOMDocument|string + */ + private function writeDocument(\DOMDocument $dom) + { + $dom->formatOutput = true; + $this->write($dom->saveXML()); + } + + /** + * @return \DOMDocument + */ + private function getRouteCollectionDocument(RouteCollection $routes) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($routesXML = $dom->createElement('routes')); + + foreach ($routes->all() as $name => $route) { + $routeXML = $this->getRouteDocument($route, $name); + $routesXML->appendChild($routesXML->ownerDocument->importNode($routeXML->childNodes->item(0), true)); + } + + return $dom; + } + + /** + * @param Route $route + * @param string|null $name + * + * @return \DOMDocument + */ + private function getRouteDocument(Route $route, $name = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($routeXML = $dom->createElement('route')); + + if ($name) { + $routeXML->setAttribute('name', $name); + } + + $routeXML->setAttribute('class', \get_class($route)); + + $routeXML->appendChild($pathXML = $dom->createElement('path')); + $pathXML->setAttribute('regex', $route->compile()->getRegex()); + $pathXML->appendChild(new \DOMText($route->getPath())); + + if ('' !== $route->getHost()) { + $routeXML->appendChild($hostXML = $dom->createElement('host')); + $hostXML->setAttribute('regex', $route->compile()->getHostRegex()); + $hostXML->appendChild(new \DOMText($route->getHost())); + } + + foreach ($route->getSchemes() as $scheme) { + $routeXML->appendChild($schemeXML = $dom->createElement('scheme')); + $schemeXML->appendChild(new \DOMText($scheme)); + } + + foreach ($route->getMethods() as $method) { + $routeXML->appendChild($methodXML = $dom->createElement('method')); + $methodXML->appendChild(new \DOMText($method)); + } + + if ($route->getDefaults()) { + $routeXML->appendChild($defaultsXML = $dom->createElement('defaults')); + foreach ($route->getDefaults() as $attribute => $value) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->setAttribute('key', $attribute); + $defaultXML->appendChild(new \DOMText($this->formatValue($value))); + } + } + + $originRequirements = $requirements = $route->getRequirements(); + unset($requirements['_scheme'], $requirements['_method']); + if ($requirements) { + $routeXML->appendChild($requirementsXML = $dom->createElement('requirements')); + foreach ($originRequirements as $attribute => $pattern) { + $requirementsXML->appendChild($requirementXML = $dom->createElement('requirement')); + $requirementXML->setAttribute('key', $attribute); + $requirementXML->appendChild(new \DOMText($pattern)); + } + } + + if ($route->getOptions()) { + $routeXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($route->getOptions() as $name => $value) { + $optionsXML->appendChild($optionXML = $dom->createElement('option')); + $optionXML->setAttribute('key', $name); + $optionXML->appendChild(new \DOMText($this->formatValue($value))); + } + } + + return $dom; + } + + /** + * @return \DOMDocument + */ + private function getContainerParametersDocument(ParameterBag $parameters) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($parametersXML = $dom->createElement('parameters')); + + foreach ($this->sortParameters($parameters) as $key => $value) { + $parametersXML->appendChild($parameterXML = $dom->createElement('parameter')); + $parameterXML->setAttribute('key', $key); + $parameterXML->appendChild(new \DOMText($this->formatParameter($value))); + } + + return $dom; + } + + /** + * @param ContainerBuilder $builder + * @param bool $showPrivate + * + * @return \DOMDocument + */ + private function getContainerTagsDocument(ContainerBuilder $builder, $showPrivate = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($containerXML = $dom->createElement('container')); + + foreach ($this->findDefinitionsByTag($builder, $showPrivate) as $tag => $definitions) { + $containerXML->appendChild($tagXML = $dom->createElement('tag')); + $tagXML->setAttribute('name', $tag); + + foreach ($definitions as $serviceId => $definition) { + $definitionXML = $this->getContainerDefinitionDocument($definition, $serviceId, true); + $tagXML->appendChild($dom->importNode($definitionXML->childNodes->item(0), true)); + } + } + + return $dom; + } + + /** + * @param mixed $service + * @param string $id + * @param ContainerBuilder|null $builder + * @param bool $showArguments + * + * @return \DOMDocument + */ + private function getContainerServiceDocument($service, $id, ContainerBuilder $builder = null, $showArguments = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + + if ($service instanceof Alias) { + $dom->appendChild($dom->importNode($this->getContainerAliasDocument($service, $id)->childNodes->item(0), true)); + if ($builder) { + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($builder->getDefinition((string) $service), (string) $service, false, $showArguments)->childNodes->item(0), true)); + } + } elseif ($service instanceof Definition) { + $dom->appendChild($dom->importNode($this->getContainerDefinitionDocument($service, $id, false, $showArguments)->childNodes->item(0), true)); + } else { + $dom->appendChild($serviceXML = $dom->createElement('service')); + $serviceXML->setAttribute('id', $id); + $serviceXML->setAttribute('class', \get_class($service)); + } + + return $dom; + } + + /** + * @param ContainerBuilder $builder + * @param string|null $tag + * @param bool $showPrivate + * @param bool $showArguments + * @param callable $filter + * + * @return \DOMDocument + */ + private function getContainerServicesDocument(ContainerBuilder $builder, $tag = null, $showPrivate = false, $showArguments = false, $filter = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($containerXML = $dom->createElement('container')); + + $serviceIds = $tag ? array_keys($builder->findTaggedServiceIds($tag)) : $builder->getServiceIds(); + + if ($filter) { + $serviceIds = array_filter($serviceIds, $filter); + } + + foreach ($this->sortServiceIds($serviceIds) as $serviceId) { + $service = $this->resolveServiceDefinition($builder, $serviceId); + + if (($service instanceof Definition || $service instanceof Alias) && !($showPrivate || ($service->isPublic() && !$service->isPrivate()))) { + continue; + } + + $serviceXML = $this->getContainerServiceDocument($service, $serviceId, null, $showArguments); + $containerXML->appendChild($containerXML->ownerDocument->importNode($serviceXML->childNodes->item(0), true)); + } + + return $dom; + } + + /** + * @param Definition $definition + * @param string|null $id + * @param bool $omitTags + * + * @return \DOMDocument + */ + private function getContainerDefinitionDocument(Definition $definition, $id = null, $omitTags = false, $showArguments = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($serviceXML = $dom->createElement('definition')); + + if ($id) { + $serviceXML->setAttribute('id', $id); + } + + $serviceXML->setAttribute('class', $definition->getClass()); + + if ($factory = $definition->getFactory()) { + $serviceXML->appendChild($factoryXML = $dom->createElement('factory')); + + if (\is_array($factory)) { + if ($factory[0] instanceof Reference) { + $factoryXML->setAttribute('service', (string) $factory[0]); + } elseif ($factory[0] instanceof Definition) { + throw new \InvalidArgumentException('Factory is not describable.'); + } else { + $factoryXML->setAttribute('class', $factory[0]); + } + $factoryXML->setAttribute('method', $factory[1]); + } else { + $factoryXML->setAttribute('function', $factory); + } + } + + $serviceXML->setAttribute('public', $definition->isPublic() && !$definition->isPrivate() ? 'true' : 'false'); + $serviceXML->setAttribute('synthetic', $definition->isSynthetic() ? 'true' : 'false'); + $serviceXML->setAttribute('lazy', $definition->isLazy() ? 'true' : 'false'); + $serviceXML->setAttribute('shared', $definition->isShared() ? 'true' : 'false'); + $serviceXML->setAttribute('abstract', $definition->isAbstract() ? 'true' : 'false'); + $serviceXML->setAttribute('autowired', $definition->isAutowired() ? 'true' : 'false'); + $serviceXML->setAttribute('autoconfigured', $definition->isAutoconfigured() ? 'true' : 'false'); + $serviceXML->setAttribute('file', $definition->getFile()); + + $calls = $definition->getMethodCalls(); + if (\count($calls) > 0) { + $serviceXML->appendChild($callsXML = $dom->createElement('calls')); + foreach ($calls as $callData) { + $callsXML->appendChild($callXML = $dom->createElement('call')); + $callXML->setAttribute('method', $callData[0]); + } + } + + if ($showArguments) { + foreach ($this->getArgumentNodes($definition->getArguments(), $dom) as $node) { + $serviceXML->appendChild($node); + } + } + + if (!$omitTags) { + if ($tags = $definition->getTags()) { + $serviceXML->appendChild($tagsXML = $dom->createElement('tags')); + foreach ($tags as $tagName => $tagData) { + foreach ($tagData as $parameters) { + $tagsXML->appendChild($tagXML = $dom->createElement('tag')); + $tagXML->setAttribute('name', $tagName); + foreach ($parameters as $name => $value) { + $tagXML->appendChild($parameterXML = $dom->createElement('parameter')); + $parameterXML->setAttribute('name', $name); + $parameterXML->appendChild(new \DOMText($this->formatParameter($value))); + } + } + } + } + } + + return $dom; + } + + /** + * @return \DOMNode[] + */ + private function getArgumentNodes(array $arguments, \DOMDocument $dom) + { + $nodes = array(); + + foreach ($arguments as $argumentKey => $argument) { + $argumentXML = $dom->createElement('argument'); + + if (\is_string($argumentKey)) { + $argumentXML->setAttribute('key', $argumentKey); + } + + if ($argument instanceof ServiceClosureArgument) { + $argument = $argument->getValues()[0]; + } + + if ($argument instanceof Reference) { + $argumentXML->setAttribute('type', 'service'); + $argumentXML->setAttribute('id', (string) $argument); + } elseif ($argument instanceof IteratorArgument) { + $argumentXML->setAttribute('type', 'iterator'); + + foreach ($this->getArgumentNodes($argument->getValues(), $dom) as $childArgumentXML) { + $argumentXML->appendChild($childArgumentXML); + } + } elseif ($argument instanceof Definition) { + $argumentXML->appendChild($dom->importNode($this->getContainerDefinitionDocument($argument, null, false, true)->childNodes->item(0), true)); + } elseif (\is_array($argument)) { + $argumentXML->setAttribute('type', 'collection'); + + foreach ($this->getArgumentNodes($argument, $dom) as $childArgumenXML) { + $argumentXML->appendChild($childArgumenXML); + } + } else { + $argumentXML->appendChild(new \DOMText($argument)); + } + + $nodes[] = $argumentXML; + } + + return $nodes; + } + + /** + * @param Alias $alias + * @param string|null $id + * + * @return \DOMDocument + */ + private function getContainerAliasDocument(Alias $alias, $id = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($aliasXML = $dom->createElement('alias')); + + if ($id) { + $aliasXML->setAttribute('id', $id); + } + + $aliasXML->setAttribute('service', (string) $alias); + $aliasXML->setAttribute('public', $alias->isPublic() && !$alias->isPrivate() ? 'true' : 'false'); + + return $dom; + } + + /** + * @return \DOMDocument + */ + private function getContainerParameterDocument($parameter, $options = array()) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($parameterXML = $dom->createElement('parameter')); + + if (isset($options['parameter'])) { + $parameterXML->setAttribute('key', $options['parameter']); + } + + $parameterXML->appendChild(new \DOMText($this->formatParameter($parameter))); + + return $dom; + } + + /** + * @param EventDispatcherInterface $eventDispatcher + * @param string|null $event + * + * @return \DOMDocument + */ + private function getEventDispatcherListenersDocument(EventDispatcherInterface $eventDispatcher, $event = null) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($eventDispatcherXML = $dom->createElement('event-dispatcher')); + + $registeredListeners = $eventDispatcher->getListeners($event); + if (null !== $event) { + $this->appendEventListenerDocument($eventDispatcher, $event, $eventDispatcherXML, $registeredListeners); + } else { + ksort($registeredListeners); + + foreach ($registeredListeners as $eventListened => $eventListeners) { + $eventDispatcherXML->appendChild($eventXML = $dom->createElement('event')); + $eventXML->setAttribute('name', $eventListened); + + $this->appendEventListenerDocument($eventDispatcher, $eventListened, $eventXML, $eventListeners); + } + } + + return $dom; + } + + private function appendEventListenerDocument(EventDispatcherInterface $eventDispatcher, $event, \DOMElement $element, array $eventListeners) + { + foreach ($eventListeners as $listener) { + $callableXML = $this->getCallableDocument($listener); + $callableXML->childNodes->item(0)->setAttribute('priority', $eventDispatcher->getListenerPriority($event, $listener)); + + $element->appendChild($element->ownerDocument->importNode($callableXML->childNodes->item(0), true)); + } + } + + /** + * @param callable $callable + * + * @return \DOMDocument + */ + private function getCallableDocument($callable) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->appendChild($callableXML = $dom->createElement('callable')); + + if (\is_array($callable)) { + $callableXML->setAttribute('type', 'function'); + + if (\is_object($callable[0])) { + $callableXML->setAttribute('name', $callable[1]); + $callableXML->setAttribute('class', \get_class($callable[0])); + } else { + if (0 !== strpos($callable[1], 'parent::')) { + $callableXML->setAttribute('name', $callable[1]); + $callableXML->setAttribute('class', $callable[0]); + $callableXML->setAttribute('static', 'true'); + } else { + $callableXML->setAttribute('name', substr($callable[1], 8)); + $callableXML->setAttribute('class', $callable[0]); + $callableXML->setAttribute('static', 'true'); + $callableXML->setAttribute('parent', 'true'); + } + } + + return $dom; + } + + if (\is_string($callable)) { + $callableXML->setAttribute('type', 'function'); + + if (false === strpos($callable, '::')) { + $callableXML->setAttribute('name', $callable); + } else { + $callableParts = explode('::', $callable); + + $callableXML->setAttribute('name', $callableParts[1]); + $callableXML->setAttribute('class', $callableParts[0]); + $callableXML->setAttribute('static', 'true'); + } + + return $dom; + } + + if ($callable instanceof \Closure) { + $callableXML->setAttribute('type', 'closure'); + + return $dom; + } + + if (method_exists($callable, '__invoke')) { + $callableXML->setAttribute('type', 'object'); + $callableXML->setAttribute('name', \get_class($callable)); + + return $dom; + } + + throw new \InvalidArgumentException('Callable is not describable.'); + } +} diff --git a/vendor/symfony/framework-bundle/Console/Helper/DescriptorHelper.php b/vendor/symfony/framework-bundle/Console/Helper/DescriptorHelper.php new file mode 100644 index 0000000..475c22c --- /dev/null +++ b/vendor/symfony/framework-bundle/Console/Helper/DescriptorHelper.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\Bundle\FrameworkBundle\Console\Helper; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor; +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor; +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor; +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor; +use Symfony\Component\Console\Helper\DescriptorHelper as BaseDescriptorHelper; + +/** + * @author Jean-François Simon + * + * @internal + */ +class DescriptorHelper extends BaseDescriptorHelper +{ + public function __construct() + { + $this + ->register('txt', new TextDescriptor()) + ->register('xml', new XmlDescriptor()) + ->register('json', new JsonDescriptor()) + ->register('md', new MarkdownDescriptor()) + ; + } +} diff --git a/vendor/symfony/framework-bundle/Controller/AbstractController.php b/vendor/symfony/framework-bundle/Controller/AbstractController.php new file mode 100644 index 0000000..b2c72fa --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/AbstractController.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\Bundle\FrameworkBundle\Controller; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Templating\EngineInterface; +use Twig\Environment; + +/** + * Provides common features needed in controllers. + * + * @author Fabien Potencier + */ +abstract class AbstractController implements ServiceSubscriberInterface +{ + use ControllerTrait; + + /** + * @var ContainerInterface + */ + protected $container; + + /** + * @internal + * @required + */ + public function setContainer(ContainerInterface $container) + { + $previous = $this->container; + $this->container = $container; + + return $previous; + } + + public static function getSubscribedServices() + { + return array( + 'router' => '?'.RouterInterface::class, + 'request_stack' => '?'.RequestStack::class, + 'http_kernel' => '?'.HttpKernelInterface::class, + 'serializer' => '?'.SerializerInterface::class, + 'session' => '?'.SessionInterface::class, + 'security.authorization_checker' => '?'.AuthorizationCheckerInterface::class, + 'templating' => '?'.EngineInterface::class, + 'twig' => '?'.Environment::class, + 'doctrine' => '?'.ManagerRegistry::class, + 'form.factory' => '?'.FormFactoryInterface::class, + 'security.token_storage' => '?'.TokenStorageInterface::class, + 'security.csrf.token_manager' => '?'.CsrfTokenManagerInterface::class, + ); + } +} diff --git a/vendor/symfony/framework-bundle/Controller/Controller.php b/vendor/symfony/framework-bundle/Controller/Controller.php new file mode 100644 index 0000000..b9f15d7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/Controller.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\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; + +/** + * Controller is a simple implementation of a Controller. + * + * It provides methods to common features needed in controllers. + * + * @author Fabien Potencier + */ +abstract class Controller implements ContainerAwareInterface +{ + use ContainerAwareTrait; + use ControllerTrait; + + /** + * Gets a container configuration parameter by its name. + * + * @param string $name The parameter name + * + * @return mixed + * + * @final since version 3.4 + */ + protected function getParameter($name) + { + return $this->container->getParameter($name); + } +} diff --git a/vendor/symfony/framework-bundle/Controller/ControllerNameParser.php b/vendor/symfony/framework-bundle/Controller/ControllerNameParser.php new file mode 100644 index 0000000..cc988ba --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/ControllerNameParser.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\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * ControllerNameParser converts controller from the short notation a:b:c + * (BlogBundle:Post:index) to a fully-qualified class::method string + * (Bundle\BlogBundle\Controller\PostController::indexAction). + * + * @author Fabien Potencier + */ +class ControllerNameParser +{ + protected $kernel; + + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + /** + * Converts a short notation a:b:c to a class::method. + * + * @param string $controller A short notation controller (a:b:c) + * + * @return string A string in the class::method notation + * + * @throws \InvalidArgumentException when the specified bundle is not enabled + * or the controller cannot be found + */ + public function parse($controller) + { + $parts = explode(':', $controller); + if (3 !== \count($parts) || \in_array('', $parts, true)) { + throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid "a:b:c" controller string.', $controller)); + } + + $originalController = $controller; + list($bundle, $controller, $action) = $parts; + $controller = str_replace('/', '\\', $controller); + $bundles = array(); + + try { + // this throws an exception if there is no such bundle + $allBundles = $this->kernel->getBundle($bundle, false, true); + } catch (\InvalidArgumentException $e) { + $message = sprintf( + 'The "%s" (from the _controller value "%s") does not exist or is not enabled in your kernel!', + $bundle, + $originalController + ); + + if ($alternative = $this->findAlternative($bundle)) { + $message .= sprintf(' Did you mean "%s:%s:%s"?', $alternative, $controller, $action); + } + + throw new \InvalidArgumentException($message, 0, $e); + } + + if (!\is_array($allBundles)) { + // happens when HttpKernel is version 4+ + $allBundles = array($allBundles); + } + + foreach ($allBundles as $b) { + $try = $b->getNamespace().'\\Controller\\'.$controller.'Controller'; + if (class_exists($try)) { + return $try.'::'.$action.'Action'; + } + + $bundles[] = $b->getName(); + $msg = sprintf('The _controller value "%s:%s:%s" maps to a "%s" class, but this class was not found. Create this class or check the spelling of the class and its namespace.', $bundle, $controller, $action, $try); + } + + if (\count($bundles) > 1) { + $msg = sprintf('Unable to find controller "%s:%s" in bundles %s.', $bundle, $controller, implode(', ', $bundles)); + } + + throw new \InvalidArgumentException($msg); + } + + /** + * Converts a class::method notation to a short one (a:b:c). + * + * @param string $controller A string in the class::method notation + * + * @return string A short notation controller (a:b:c) + * + * @throws \InvalidArgumentException when the controller is not valid or cannot be found in any bundle + */ + public function build($controller) + { + if (0 === preg_match('#^(.*?\\\\Controller\\\\(.+)Controller)::(.+)Action$#', $controller, $match)) { + throw new \InvalidArgumentException(sprintf('The "%s" controller is not a valid "class::method" string.', $controller)); + } + + $className = $match[1]; + $controllerName = $match[2]; + $actionName = $match[3]; + foreach ($this->kernel->getBundles() as $name => $bundle) { + if (0 !== strpos($className, $bundle->getNamespace())) { + continue; + } + + return sprintf('%s:%s:%s', $name, $controllerName, $actionName); + } + + throw new \InvalidArgumentException(sprintf('Unable to find a bundle that defines controller "%s".', $controller)); + } + + /** + * Attempts to find a bundle that is *similar* to the given bundle name. + * + * @param string $nonExistentBundleName + * + * @return string + */ + private function findAlternative($nonExistentBundleName) + { + $bundleNames = array_map(function ($b) { + return $b->getName(); + }, $this->kernel->getBundles()); + + $alternative = null; + $shortest = null; + foreach ($bundleNames as $bundleName) { + // if there's a partial match, return it immediately + if (false !== strpos($bundleName, $nonExistentBundleName)) { + return $bundleName; + } + + $lev = levenshtein($nonExistentBundleName, $bundleName); + if ($lev <= \strlen($nonExistentBundleName) / 3 && (null === $alternative || $lev < $shortest)) { + $alternative = $bundleName; + $shortest = $lev; + } + } + + return $alternative; + } +} diff --git a/vendor/symfony/framework-bundle/Controller/ControllerResolver.php b/vendor/symfony/framework-bundle/Controller/ControllerResolver.php new file mode 100644 index 0000000..742a0dd --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/ControllerResolver.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Controller\ContainerControllerResolver; + +/** + * @author Fabien Potencier + */ +class ControllerResolver extends ContainerControllerResolver +{ + protected $parser; + + public function __construct(ContainerInterface $container, ControllerNameParser $parser, LoggerInterface $logger = null) + { + $this->parser = $parser; + + parent::__construct($container, $logger); + } + + /** + * {@inheritdoc} + */ + protected function createController($controller) + { + if (false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { + // controller in the a:b:c notation then + $controller = $this->parser->parse($controller); + } + + $resolvedController = parent::createController($controller); + + if (1 === substr_count($controller, ':') && \is_array($resolvedController)) { + $resolvedController[0] = $this->configureController($resolvedController[0]); + } + + return $resolvedController; + } + + /** + * {@inheritdoc} + */ + protected function instantiateController($class) + { + return $this->configureController(parent::instantiateController($class)); + } + + private function configureController($controller) + { + if ($controller instanceof ContainerAwareInterface) { + // @deprecated switch, to be removed in 4.0 where these classes + // won't implement ContainerAwareInterface anymore + switch (\get_class($controller)) { + case RedirectController::class: + case TemplateController::class: + return $controller; + } + $controller->setContainer($this->container); + } + if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) { + $controller->setContainer($previousContainer); + } + + return $controller; + } +} diff --git a/vendor/symfony/framework-bundle/Controller/ControllerTrait.php b/vendor/symfony/framework-bundle/Controller/ControllerTrait.php new file mode 100644 index 0000000..e9454f6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/ControllerTrait.php @@ -0,0 +1,478 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Doctrine\Common\Persistence\ManagerRegistry; +use Psr\Container\ContainerInterface; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\Security\Csrf\CsrfToken; + +/** + * Common features needed in controllers. + * + * @author Fabien Potencier + * + * @internal + * + * @property ContainerInterface $container + */ +trait ControllerTrait +{ + /** + * Returns true if the service id is defined. + * + * @param string $id The service id + * + * @return bool true if the service id is defined, false otherwise + * + * @final since version 3.4 + */ + protected function has($id) + { + return $this->container->has($id); + } + + /** + * Gets a container service by its id. + * + * @param string $id The service id + * + * @return object The service + * + * @final since version 3.4 + */ + protected function get($id) + { + return $this->container->get($id); + } + + /** + * Generates a URL from the given parameters. + * + * @param string $route The name of the route + * @param array $parameters An array of parameters + * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) + * + * @return string The generated URL + * + * @see UrlGeneratorInterface + * + * @final since version 3.4 + */ + protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) + { + return $this->container->get('router')->generate($route, $parameters, $referenceType); + } + + /** + * Forwards the request to another controller. + * + * @param string $controller The controller name (a string like BlogBundle:Post:index) + * @param array $path An array of path parameters + * @param array $query An array of query parameters + * + * @return Response A Response instance + * + * @final since version 3.4 + */ + protected function forward($controller, array $path = array(), array $query = array()) + { + $request = $this->container->get('request_stack')->getCurrentRequest(); + $path['_forwarded'] = $request->attributes; + $path['_controller'] = $controller; + $subRequest = $request->duplicate($query, null, $path); + + return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } + + /** + * Returns a RedirectResponse to the given URL. + * + * @param string $url The URL to redirect to + * @param int $status The status code to use for the Response + * + * @return RedirectResponse + * + * @final since version 3.4 + */ + protected function redirect($url, $status = 302) + { + return new RedirectResponse($url, $status); + } + + /** + * Returns a RedirectResponse to the given route with the given parameters. + * + * @param string $route The name of the route + * @param array $parameters An array of parameters + * @param int $status The status code to use for the Response + * + * @return RedirectResponse + * + * @final since version 3.4 + */ + protected function redirectToRoute($route, array $parameters = array(), $status = 302) + { + return $this->redirect($this->generateUrl($route, $parameters), $status); + } + + /** + * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. + * + * @param mixed $data The response data + * @param int $status The status code to use for the Response + * @param array $headers Array of extra headers to add + * @param array $context Context to pass to serializer when using serializer component + * + * @return JsonResponse + * + * @final since version 3.4 + */ + protected function json($data, $status = 200, $headers = array(), $context = array()) + { + if ($this->container->has('serializer')) { + $json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array( + 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, + ), $context)); + + return new JsonResponse($json, $status, $headers, true); + } + + return new JsonResponse($data, $status, $headers); + } + + /** + * Returns a BinaryFileResponse object with original or customized file name and disposition header. + * + * @param \SplFileInfo|string $file File object or path to file to be sent as response + * @param string|null $fileName File name to be sent to response or null (will use original file name) + * @param string $disposition Disposition of response ("attachment" is default, other type is "inline") + * + * @return BinaryFileResponse + * + * @final since version 3.4 + */ + protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) + { + $response = new BinaryFileResponse($file); + $response->setContentDisposition($disposition, null === $fileName ? $response->getFile()->getFilename() : $fileName); + + return $response; + } + + /** + * Adds a flash message to the current session for type. + * + * @param string $type The type + * @param string $message The message + * + * @throws \LogicException + * + * @final since version 3.4 + */ + protected function addFlash($type, $message) + { + if (!$this->container->has('session')) { + throw new \LogicException('You can not use the addFlash method if sessions are disabled. Enable them in "config/packages/framework.yaml".'); + } + + $this->container->get('session')->getFlashBag()->add($type, $message); + } + + /** + * Checks if the attributes are granted against the current authentication token and optionally supplied subject. + * + * @param mixed $attributes The attributes + * @param mixed $subject The subject + * + * @return bool + * + * @throws \LogicException + * + * @final since version 3.4 + */ + protected function isGranted($attributes, $subject = null) + { + if (!$this->container->has('security.authorization_checker')) { + throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); + } + + return $this->container->get('security.authorization_checker')->isGranted($attributes, $subject); + } + + /** + * Throws an exception unless the attributes are granted against the current authentication token and optionally + * supplied subject. + * + * @param mixed $attributes The attributes + * @param mixed $subject The subject + * @param string $message The message passed to the exception + * + * @throws AccessDeniedException + * + * @final since version 3.4 + */ + protected function denyAccessUnlessGranted($attributes, $subject = null, $message = 'Access Denied.') + { + if (!$this->isGranted($attributes, $subject)) { + $exception = $this->createAccessDeniedException($message); + $exception->setAttributes($attributes); + $exception->setSubject($subject); + + throw $exception; + } + } + + /** + * Returns a rendered view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * + * @return string The rendered view + * + * @final since version 3.4 + */ + protected function renderView($view, array $parameters = array()) + { + if ($this->container->has('templating')) { + return $this->container->get('templating')->render($view, $parameters); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".'); + } + + return $this->container->get('twig')->render($view, $parameters); + } + + /** + * Renders a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param Response $response A response instance + * + * @return Response A Response instance + * + * @final since version 3.4 + */ + protected function render($view, array $parameters = array(), Response $response = null) + { + if ($this->container->has('templating')) { + $content = $this->container->get('templating')->render($view, $parameters); + } elseif ($this->container->has('twig')) { + $content = $this->container->get('twig')->render($view, $parameters); + } else { + throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".'); + } + + if (null === $response) { + $response = new Response(); + } + + $response->setContent($content); + + return $response; + } + + /** + * Streams a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param StreamedResponse $response A response instance + * + * @return StreamedResponse A StreamedResponse instance + * + * @final since version 3.4 + */ + protected function stream($view, array $parameters = array(), StreamedResponse $response = null) + { + if ($this->container->has('templating')) { + $templating = $this->container->get('templating'); + + $callback = function () use ($templating, $view, $parameters) { + $templating->stream($view, $parameters); + }; + } elseif ($this->container->has('twig')) { + $twig = $this->container->get('twig'); + + $callback = function () use ($twig, $view, $parameters) { + $twig->display($view, $parameters); + }; + } else { + throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available. Try running "composer require symfony/twig-bundle".'); + } + + if (null === $response) { + return new StreamedResponse($callback); + } + + $response->setCallback($callback); + + return $response; + } + + /** + * Returns a NotFoundHttpException. + * + * This will result in a 404 response code. Usage example: + * + * throw $this->createNotFoundException('Page not found!'); + * + * @param string $message A message + * @param \Exception|null $previous The previous exception + * + * @return NotFoundHttpException + * + * @final since version 3.4 + */ + protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) + { + return new NotFoundHttpException($message, $previous); + } + + /** + * Returns an AccessDeniedException. + * + * This will result in a 403 response code. Usage example: + * + * throw $this->createAccessDeniedException('Unable to access this page!'); + * + * @param string $message A message + * @param \Exception|null $previous The previous exception + * + * @return AccessDeniedException + * + * @throws \LogicException If the Security component is not available + * + * @final since version 3.4 + */ + protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) + { + if (!class_exists(AccessDeniedException::class)) { + throw new \LogicException('You can not use the "createAccessDeniedException" method if the Security component is not available. Try running "composer require symfony/security-bundle".'); + } + + return new AccessDeniedException($message, $previous); + } + + /** + * Creates and returns a Form instance from the type of the form. + * + * @param string $type The fully qualified class name of the form type + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return FormInterface + * + * @final since version 3.4 + */ + protected function createForm($type, $data = null, array $options = array()) + { + return $this->container->get('form.factory')->create($type, $data, $options); + } + + /** + * Creates and returns a form builder instance. + * + * @param mixed $data The initial data for the form + * @param array $options Options for the form + * + * @return FormBuilderInterface + * + * @final since version 3.4 + */ + protected function createFormBuilder($data = null, array $options = array()) + { + return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); + } + + /** + * Shortcut to return the Doctrine Registry service. + * + * @return ManagerRegistry + * + * @throws \LogicException If DoctrineBundle is not available + * + * @final since version 3.4 + */ + protected function getDoctrine() + { + if (!$this->container->has('doctrine')) { + throw new \LogicException('The DoctrineBundle is not registered in your application. Try running "composer require symfony/orm-pack".'); + } + + return $this->container->get('doctrine'); + } + + /** + * Get a user from the Security Token Storage. + * + * @return mixed + * + * @throws \LogicException If SecurityBundle is not available + * + * @see TokenInterface::getUser() + * + * @final since version 3.4 + */ + protected function getUser() + { + if (!$this->container->has('security.token_storage')) { + throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".'); + } + + if (null === $token = $this->container->get('security.token_storage')->getToken()) { + return; + } + + if (!\is_object($user = $token->getUser())) { + // e.g. anonymous authentication + return; + } + + return $user; + } + + /** + * Checks the validity of a CSRF token. + * + * @param string $id The id used when generating the token + * @param string $token The actual token sent with the request that should be validated + * + * @return bool + * + * @final since version 3.4 + */ + protected function isCsrfTokenValid($id, $token) + { + if (!$this->container->has('security.csrf.token_manager')) { + throw new \LogicException('CSRF protection is not enabled in your application. Enable it with the "csrf_protection" key in "config/packages/framework.yaml".'); + } + + return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); + } +} diff --git a/vendor/symfony/framework-bundle/Controller/RedirectController.php b/vendor/symfony/framework-bundle/Controller/RedirectController.php new file mode 100644 index 0000000..ae6b6d7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/RedirectController.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Redirects a request to another URL. + * + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class RedirectController implements ContainerAwareInterface +{ + /** + * @deprecated since version 3.4, to be removed in 4.0 + */ + protected $container; + + private $router; + private $httpPort; + private $httpsPort; + + public function __construct(UrlGeneratorInterface $router = null, $httpPort = null, $httpsPort = null) + { + $this->router = $router; + $this->httpPort = $httpPort; + $this->httpsPort = $httpsPort; + } + + /** + * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. + */ + public function setContainer(ContainerInterface $container = null) + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Inject an UrlGeneratorInterface using the constructor instead.', __METHOD__), E_USER_DEPRECATED); + + $this->container = $container; + $this->router = $container->get('router'); + } + + /** + * Redirects to another route with the given name. + * + * The response status code is 302 if the permanent parameter is false (default), + * and 301 if the redirection is permanent. + * + * In case the route name is empty, the status code will be 404 when permanent is false + * and 410 otherwise. + * + * @param Request $request The request instance + * @param string $route The route name to redirect to + * @param bool $permanent Whether the redirection is permanent + * @param bool|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore + * + * @return Response A Response instance + * + * @throws HttpException In case the route name is empty + */ + public function redirectAction(Request $request, $route, $permanent = false, $ignoreAttributes = false) + { + if ('' == $route) { + throw new HttpException($permanent ? 410 : 404); + } + + $attributes = array(); + if (false === $ignoreAttributes || \is_array($ignoreAttributes)) { + $attributes = $request->attributes->get('_route_params'); + unset($attributes['route'], $attributes['permanent'], $attributes['ignoreAttributes']); + if ($ignoreAttributes) { + $attributes = array_diff_key($attributes, array_flip($ignoreAttributes)); + } + } + + return new RedirectResponse($this->router->generate($route, $attributes, UrlGeneratorInterface::ABSOLUTE_URL), $permanent ? 301 : 302); + } + + /** + * Redirects to a URL. + * + * The response status code is 302 if the permanent parameter is false (default), + * and 301 if the redirection is permanent. + * + * In case the path is empty, the status code will be 404 when permanent is false + * and 410 otherwise. + * + * @param Request $request The request instance + * @param string $path The absolute path or URL to redirect to + * @param bool $permanent Whether the redirect is permanent or not + * @param string|null $scheme The URL scheme (null to keep the current one) + * @param int|null $httpPort The HTTP port (null to keep the current one for the same scheme or the configured port in the container) + * @param int|null $httpsPort The HTTPS port (null to keep the current one for the same scheme or the configured port in the container) + * + * @return Response A Response instance + * + * @throws HttpException In case the path is empty + */ + public function urlRedirectAction(Request $request, $path, $permanent = false, $scheme = null, $httpPort = null, $httpsPort = null) + { + if ('' == $path) { + throw new HttpException($permanent ? 410 : 404); + } + + $statusCode = $permanent ? 301 : 302; + + // redirect if the path is a full URL + if (parse_url($path, PHP_URL_SCHEME)) { + return new RedirectResponse($path, $statusCode); + } + + if (null === $scheme) { + $scheme = $request->getScheme(); + } + + $qs = $request->getQueryString(); + if ($qs) { + if (false === strpos($path, '?')) { + $qs = '?'.$qs; + } else { + $qs = '&'.$qs; + } + } + + $port = ''; + if ('http' === $scheme) { + if (null === $httpPort) { + if ('http' === $request->getScheme()) { + $httpPort = $request->getPort(); + } elseif ($this->container && $this->container->hasParameter('request_listener.http_port')) { + @trigger_error(sprintf('Passing the http port as a container parameter is deprecated since Symfony 3.4 and won\'t be possible in 4.0. Pass it to the constructor of the "%s" class instead.', __CLASS__), E_USER_DEPRECATED); + $httpPort = $this->container->getParameter('request_listener.http_port'); + } else { + $httpPort = $this->httpPort; + } + } + + if (null !== $httpPort && 80 != $httpPort) { + $port = ":$httpPort"; + } + } elseif ('https' === $scheme) { + if (null === $httpsPort) { + if ('https' === $request->getScheme()) { + $httpsPort = $request->getPort(); + } elseif ($this->container && $this->container->hasParameter('request_listener.https_port')) { + @trigger_error(sprintf('Passing the https port as a container parameter is deprecated since Symfony 3.4 and won\'t be possible in 4.0. Pass it to the constructor of the "%s" class instead.', __CLASS__), E_USER_DEPRECATED); + $httpsPort = $this->container->getParameter('request_listener.https_port'); + } else { + $httpsPort = $this->httpsPort; + } + } + + if (null !== $httpsPort && 443 != $httpsPort) { + $port = ":$httpsPort"; + } + } + + $url = $scheme.'://'.$request->getHost().$port.$request->getBaseUrl().$path.$qs; + + return new RedirectResponse($url, $statusCode); + } +} diff --git a/vendor/symfony/framework-bundle/Controller/TemplateController.php b/vendor/symfony/framework-bundle/Controller/TemplateController.php new file mode 100644 index 0000000..f72d556 --- /dev/null +++ b/vendor/symfony/framework-bundle/Controller/TemplateController.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\Bundle\FrameworkBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\EngineInterface; +use Twig\Environment; + +/** + * TemplateController. + * + * @author Fabien Potencier + * + * @final since version 3.4 + */ +class TemplateController implements ContainerAwareInterface +{ + /** + * @deprecated since version 3.4, to be removed in 4.0 + */ + protected $container; + + private $twig; + private $templating; + + public function __construct(Environment $twig = null, EngineInterface $templating = null) + { + $this->twig = $twig; + $this->templating = $templating; + } + + /** + * @deprecated since version 3.4, to be removed in 4.0 alongside with the ContainerAwareInterface type. + */ + public function setContainer(ContainerInterface $container = null) + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.4 and will be removed in 4.0. Inject a Twig Environment or an EngineInterface using the constructor instead.', __METHOD__), E_USER_DEPRECATED); + + if ($container->has('templating')) { + $this->templating = $container->get('templating'); + } elseif ($container->has('twig')) { + $this->twig = $container->get('twig'); + } + $this->container = $container; + } + + /** + * Renders a template. + * + * @param string $template The template name + * @param int|null $maxAge Max age for client caching + * @param int|null $sharedAge Max age for shared (proxy) caching + * @param bool|null $private Whether or not caching should apply for client caches only + * + * @return Response A Response instance + */ + public function templateAction($template, $maxAge = null, $sharedAge = null, $private = null) + { + if ($this->templating) { + $response = new Response($this->templating->render($template)); + } elseif ($this->twig) { + $response = new Response($this->twig->render($template)); + } else { + throw new \LogicException('You can not use the TemplateController if the Templating Component or the Twig Bundle are not available.'); + } + + if ($maxAge) { + $response->setMaxAge($maxAge); + } + + if ($sharedAge) { + $response->setSharedMaxAge($sharedAge); + } + + if ($private) { + $response->setPrivate(); + } elseif (false === $private || (null === $private && ($maxAge || $sharedAge))) { + $response->setPublic(); + } + + return $response; + } +} diff --git a/vendor/symfony/framework-bundle/DataCollector/RequestDataCollector.php b/vendor/symfony/framework-bundle/DataCollector/RequestDataCollector.php new file mode 100644 index 0000000..f71fc83 --- /dev/null +++ b/vendor/symfony/framework-bundle/DataCollector/RequestDataCollector.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\Bundle\FrameworkBundle\DataCollector; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector as BaseRequestCollector; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; + +/** + * RequestDataCollector. + * + * @author Jules Pietri + */ +class RequestDataCollector extends BaseRequestCollector implements EventSubscriberInterface +{ + /** + * {@inheritdoc} + */ + public function collect(Request $request, Response $response, \Exception $exception = null) + { + parent::collect($request, $response, $exception); + + if ($parentRequestAttributes = $request->attributes->get('_forwarded')) { + if ($parentRequestAttributes instanceof ParameterBag) { + $parentRequestAttributes->set('_forward_token', $response->headers->get('x-debug-token')); + } + } + if ($request->attributes->has('_forward_controller')) { + $this->data['forward'] = array( + 'token' => $request->attributes->get('_forward_token'), + 'controller' => $this->parseController($request->attributes->get('_forward_controller')), + ); + } + } + + /** + * Gets the parsed forward controller. + * + * @return array|bool An array with keys 'token' the forward profile token, and + * 'controller' the parsed forward controller, false otherwise + */ + public function getForward() + { + return isset($this->data['forward']) ? $this->data['forward'] : false; + } + + public function onKernelController(FilterControllerEvent $event) + { + $this->controllers[$event->getRequest()] = $event->getController(); + + if ($parentRequestAttributes = $event->getRequest()->attributes->get('_forwarded')) { + if ($parentRequestAttributes instanceof ParameterBag) { + $parentRequestAttributes->set('_forward_controller', $event->getController()); + } + } + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'request'; + } +} diff --git a/vendor/symfony/framework-bundle/DataCollector/RouterDataCollector.php b/vendor/symfony/framework-bundle/DataCollector/RouterDataCollector.php new file mode 100644 index 0000000..90a88ca --- /dev/null +++ b/vendor/symfony/framework-bundle/DataCollector/RouterDataCollector.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\Bundle\FrameworkBundle\DataCollector; + +use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\DataCollector\RouterDataCollector as BaseRouterDataCollector; + +/** + * RouterDataCollector. + * + * @author Fabien Potencier + */ +class RouterDataCollector extends BaseRouterDataCollector +{ + public function guessRoute(Request $request, $controller) + { + if (\is_array($controller)) { + $controller = $controller[0]; + } + + if ($controller instanceof RedirectController) { + return $request->attributes->get('_route'); + } + + return parent::guessRoute($request, $controller); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.php new file mode 100644 index 0000000..4f09e52 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddAnnotationsCachedReaderPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @internal + */ +class AddAnnotationsCachedReaderPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + // "annotations.cached_reader" is wired late so that any passes using + // "annotation_reader" at build time don't get any cache + foreach ($container->findTaggedServiceIds('annotations.cached_reader') as $id => $tags) { + $reader = $container->getDefinition($id); + $properties = $reader->getProperties(); + + if (isset($properties['cacheProviderBackup'])) { + $provider = $properties['cacheProviderBackup']->getValues()[0]; + unset($properties['cacheProviderBackup']); + $reader->setProperties($properties); + $container->set($id, null); + $container->setDefinition($id, $reader->replaceArgument(1, $provider)); + } + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheClearerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheClearerPass.php new file mode 100644 index 0000000..deaa6f4 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheClearerPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', AddCacheClearerPass::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers the cache clearers. + * + * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments. + * + * @author Dustin Dobervich + */ +class AddCacheClearerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('cache_clearer')) { + return; + } + + $clearers = array(); + foreach ($container->findTaggedServiceIds('kernel.cache_clearer', true) as $id => $attributes) { + $clearers[] = new Reference($id); + } + + $container->getDefinition('cache_clearer')->replaceArgument(0, $clearers); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheWarmerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheWarmerPass.php new file mode 100644 index 0000000..0301f55 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddCacheWarmerPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use tagged iterator arguments instead.', AddCacheWarmerPass::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Registers the cache warmers. + * + * @deprecated since version 3.4, to be removed in 4.0. Use tagged iterator arguments instead. + * + * @author Fabien Potencier + */ +class AddCacheWarmerPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('cache_warmer')) { + return; + } + + $warmers = $this->findAndSortTaggedServices('kernel.cache_warmer', $container); + + if (empty($warmers)) { + return; + } + + $container->getDefinition('cache_warmer')->replaceArgument(0, $warmers); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConsoleCommandPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConsoleCommandPass.php new file mode 100644 index 0000000..7e261bb --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConsoleCommandPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('%s is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass instead.', AddConsoleCommandPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass as BaseAddConsoleCommandPass; + +/** + * Registers console commands. + * + * @author Grégoire Pineau + * + * @deprecated since version 3.3, to be removed in 4.0. Use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass instead. + */ +class AddConsoleCommandPass extends BaseAddConsoleCommandPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.php new file mode 100644 index 0000000..a118afb --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddConstraintValidatorsPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass as BaseAddConstraintValidatorsPass; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', AddConstraintValidatorsPass::class, BaseAddConstraintValidatorsPass::class), E_USER_DEPRECATED); + +/** + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseAddConstraintValidatorsPass} instead + */ +class AddConstraintValidatorsPass extends BaseAddConstraintValidatorsPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddDebugLogProcessorPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddDebugLogProcessorPass.php new file mode 100644 index 0000000..69038cf --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddDebugLogProcessorPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class AddDebugLogProcessorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('profiler')) { + return; + } + if (!$container->hasDefinition('monolog.logger_prototype')) { + return; + } + if (!$container->hasDefinition('debug.log_processor')) { + return; + } + + $definition = $container->getDefinition('monolog.logger_prototype'); + $definition->addMethodCall('pushProcessor', array(new Reference('debug.log_processor'))); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.php new file mode 100644 index 0000000..a5bdfd6 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddExpressionLanguageProvidersPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers the expression language providers. + * + * @author Fabien Potencier + */ +class AddExpressionLanguageProvidersPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + // routing + if ($container->has('router')) { + $definition = $container->findDefinition('router'); + foreach ($container->findTaggedServiceIds('routing.expression_language_provider', true) as $id => $attributes) { + $definition->addMethodCall('addExpressionLanguageProvider', array(new Reference($id))); + } + } + + // security + if ($container->has('security.expression_language')) { + $definition = $container->findDefinition('security.expression_language'); + foreach ($container->findTaggedServiceIds('security.expression_language_provider', true) as $id => $attributes) { + $definition->addMethodCall('registerProvider', array(new Reference($id))); + } + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddValidatorInitializersPass.php new file mode 100644 index 0000000..5824de4 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/AddValidatorInitializersPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass as BaseAddValidatorsInitializerPass; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', AddValidatorInitializersPass::class, BaseAddValidatorsInitializerPass::class), E_USER_DEPRECATED); + +/** + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseAddValidatorInitializersPass} instead + */ +class AddValidatorInitializersPass extends BaseAddValidatorsInitializerPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CacheCollectorPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CacheCollectorPass.php new file mode 100644 index 0000000..60cdadb --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CacheCollectorPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Inject a data collector to all the cache services to be able to get detailed statistics. + * + * @author Tobias Nyholm + */ +class CacheCollectorPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('data_collector.cache')) { + return; + } + + $collectorDefinition = $container->getDefinition('data_collector.cache'); + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $attributes) { + $definition = $container->getDefinition($id); + if ($definition->isAbstract()) { + continue; + } + + $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class); + $recorder->setTags($definition->getTags()); + $recorder->setPublic($definition->isPublic()); + $recorder->setArguments(array(new Reference($innerId = $id.'.recorder_inner'))); + + $definition->setTags(array()); + $definition->setPublic(false); + + if (method_exists($definition, 'getAutowiringTypes') && $types = $definition->getAutowiringTypes(false)) { + $recorder->setAutowiringTypes($types); + $definition->setAutowiringTypes(array()); + } + + $container->setDefinition($innerId, $definition); + $container->setDefinition($id, $recorder); + + // Tell the collector to add the new instance + $collectorDefinition->addMethodCall('addInstance', array($id, new Reference($id))); + $collectorDefinition->setPublic(false); + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolClearerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolClearerPass.php new file mode 100644 index 0000000..bd6908b --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolClearerPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +final class CachePoolClearerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + $container->getParameterBag()->remove('cache.prefix.seed'); + + foreach ($container->findTaggedServiceIds('cache.pool.clearer') as $id => $attr) { + $clearer = $container->getDefinition($id); + $pools = array(); + foreach ($clearer->getArgument(0) as $id => $ref) { + if ($container->hasDefinition($id)) { + $pools[$id] = new Reference($id); + } + } + $clearer->replaceArgument(0, $pools); + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPass.php new file mode 100644 index 0000000..e4c55a9 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Nicolas Grekas + */ +class CachePoolPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if ($container->hasParameter('cache.prefix.seed')) { + $seed = '.'.$container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed')); + } else { + $seed = '_'.$container->getParameter('kernel.root_dir'); + } + $seed .= '.'.$container->getParameter('kernel.name').'.'.$container->getParameter('kernel.environment'); + + $pools = array(); + $clearers = array(); + $attributes = array( + 'provider', + 'namespace', + 'default_lifetime', + 'reset', + ); + foreach ($container->findTaggedServiceIds('cache.pool') as $id => $tags) { + $adapter = $pool = $container->getDefinition($id); + if ($pool->isAbstract()) { + continue; + } + while ($adapter instanceof ChildDefinition) { + $adapter = $container->findDefinition($adapter->getParent()); + if ($t = $adapter->getTag('cache.pool')) { + $tags[0] += $t[0]; + } + } + if (!isset($tags[0]['namespace'])) { + $tags[0]['namespace'] = $this->getNamespace($seed, $id); + } + if (isset($tags[0]['clearer'])) { + $clearer = $tags[0]['clearer']; + while ($container->hasAlias($clearer)) { + $clearer = (string) $container->getAlias($clearer); + } + } else { + $clearer = null; + } + unset($tags[0]['clearer']); + + if (isset($tags[0]['provider'])) { + $tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider'])); + } + $i = 0; + foreach ($attributes as $attr) { + if (!isset($tags[0][$attr])) { + // no-op + } elseif ('reset' === $attr) { + if ($tags[0][$attr]) { + $pool->addTag('kernel.reset', array('method' => $tags[0][$attr])); + } + } elseif ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass()) { + $pool->replaceArgument($i++, $tags[0][$attr]); + } + unset($tags[0][$attr]); + } + if (!empty($tags[0])) { + throw new InvalidArgumentException(sprintf('Invalid "cache.pool" tag for service "%s": accepted attributes are "clearer", "provider", "namespace", "default_lifetime" and "reset", found "%s".', $id, implode('", "', array_keys($tags[0])))); + } + + if (null !== $clearer) { + $clearers[$clearer][$id] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + } + + $pools[$id] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE); + } + + $clearer = 'cache.global_clearer'; + while ($container->hasAlias($clearer)) { + $clearer = (string) $container->getAlias($clearer); + } + if ($container->hasDefinition($clearer)) { + $clearers['cache.global_clearer'] = $pools; + } + + foreach ($clearers as $id => $pools) { + $clearer = $container->getDefinition($id); + if ($clearer instanceof ChildDefinition) { + $clearer->replaceArgument(0, $pools); + } else { + $clearer->setArgument(0, $pools); + } + $clearer->addTag('cache.pool.clearer'); + + if ('cache.system_clearer' === $id) { + $clearer->addTag('kernel.cache_clearer'); + } + } + } + + private function getNamespace($seed, $id) + { + return substr(str_replace('/', '-', base64_encode(hash('sha256', $id.$seed, true))), 0, 10); + } + + /** + * @internal + */ + public static function getServiceProvider(ContainerBuilder $container, $name) + { + $container->resolveEnvPlaceholders($name, null, $usedEnvs); + + if ($usedEnvs || preg_match('#^[a-z]++://#', $name)) { + $dsn = $name; + + if (!$container->hasDefinition($name = 'cache_connection.'.ContainerBuilder::hash($dsn))) { + $definition = new Definition(AbstractAdapter::class); + $definition->setPublic(false); + $definition->setFactory(array(AbstractAdapter::class, 'createConnection')); + $definition->setArguments(array($dsn, array('lazy' => true))); + $container->setDefinition($name, $definition); + } + } + + return $name; + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPrunerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPrunerPass.php new file mode 100644 index 0000000..728a382 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CachePoolPrunerPass.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @author Rob Frawley 2nd + */ +class CachePoolPrunerPass implements CompilerPassInterface +{ + private $cacheCommandServiceId; + private $cachePoolTag; + + public function __construct($cacheCommandServiceId = 'console.command.cache_pool_prune', $cachePoolTag = 'cache.pool') + { + $this->cacheCommandServiceId = $cacheCommandServiceId; + $this->cachePoolTag = $cachePoolTag; + } + + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->cacheCommandServiceId)) { + return; + } + + $services = array(); + + foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) { + $class = $container->getParameterBag()->resolveValue($container->getDefinition($id)->getClass()); + + if (!$reflection = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + + if ($reflection->implementsInterface(PruneableInterface::class)) { + $services[$id] = new Reference($id); + } + } + + $container->getDefinition($this->cacheCommandServiceId)->replaceArgument(0, new IteratorArgument($services)); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CompilerDebugDumpPass.php new file mode 100644 index 0000000..4572320 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/CompilerDebugDumpPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', CompilerDebugDumpPass::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * @deprecated since version 3.3, to be removed in 4.0. + */ +class CompilerDebugDumpPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $filename = self::getCompilerLogFilename($container); + + $filesystem = new Filesystem(); + $filesystem->dumpFile($filename, implode("\n", $container->getCompiler()->getLog()), null); + try { + $filesystem->chmod($filename, 0666, umask()); + } catch (IOException $e) { + // discard chmod failure (some filesystem may not support it) + } + } + + public static function getCompilerLogFilename(ContainerInterface $container) + { + $class = $container->getParameter('kernel.container_class'); + + return $container->getParameter('kernel.cache_dir').'/'.$class.'Compiler.log'; + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ConfigCachePass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ConfigCachePass.php new file mode 100644 index 0000000..567ec19 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ConfigCachePass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Config\DependencyInjection\ConfigCachePass as BaseConfigCachePass; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use tagged iterator arguments instead.', ConfigCachePass::class), E_USER_DEPRECATED); + +/** + * Adds services tagged config_cache.resource_checker to the config_cache_factory service, ordering them by priority. + * + * @deprecated since version 3.3, to be removed in 4.0. Use tagged iterator arguments instead. + * + * @author Matthias Pigulla + * @author Benjamin Klotz + */ +class ConfigCachePass extends BaseConfigCachePass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.php new file mode 100644 index 0000000..0df5420 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ContainerBuilderDebugDumpPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Dumper\XmlDumper; + +/** + * Dumps the ContainerBuilder to a cache file so that it can be used by + * debugging tools such as the debug:container console command. + * + * @author Ryan Weaver + * @author Fabien Potencier + */ +class ContainerBuilderDebugDumpPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $cache = new ConfigCache($container->getParameter('debug.container.dump'), true); + if (!$cache->isFresh()) { + $cache->write((new XmlDumper($container))->dump(), $container->getResources()); + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.php new file mode 100644 index 0000000..11ba453 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ControllerArgumentValueResolverPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass as BaseControllerArgumentValueResolverPass; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', ControllerArgumentValueResolverPass::class, BaseControllerArgumentValueResolverPass::class), E_USER_DEPRECATED); + +/** + * Gathers and configures the argument value resolvers. + * + * @author Iltar van der Berg + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseControllerArgumentValueResolverPass} + */ +class ControllerArgumentValueResolverPass extends BaseControllerArgumentValueResolverPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.php new file mode 100644 index 0000000..ee2bbb6 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/DataCollectorTranslatorPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * @author Christian Flothmann + */ +class DataCollectorTranslatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->has('translator')) { + return; + } + + $translatorClass = $container->getParameterBag()->resolveValue($container->findDefinition('translator')->getClass()); + + if (!is_subclass_of($translatorClass, 'Symfony\Component\Translation\TranslatorBagInterface')) { + $container->removeDefinition('translator.data_collector'); + $container->removeDefinition('data_collector.translation'); + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/FormPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/FormPass.php new file mode 100644 index 0000000..a63ece1 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/FormPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\Form\DependencyInjection\FormPass instead.', FormPass::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; + +/** + * Adds all services with the tags "form.type" and "form.type_guesser" as + * arguments of the "form.extension" service. + * + * @author Bernhard Schussek + * + * @deprecated since version 3.3, to be removed in 4.0. Use FormPass in the Form component instead. + */ +class FormPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('form.extension')) { + return; + } + + $definition = $container->getDefinition('form.extension'); + + // Builds an array with fully-qualified type class names as keys and service IDs as values + $types = array(); + + foreach ($container->findTaggedServiceIds('form.type') as $serviceId => $tag) { + $serviceDefinition = $container->getDefinition($serviceId); + if (!$serviceDefinition->isPublic()) { + $serviceDefinition->setPublic(true); + } + + // Support type access by FQCN + $types[$serviceDefinition->getClass()] = $serviceId; + } + + $definition->replaceArgument(1, $types); + + $typeExtensions = array(); + + foreach ($this->findAndSortTaggedServices('form.type_extension', $container) as $reference) { + $serviceId = (string) $reference; + $serviceDefinition = $container->getDefinition($serviceId); + if (!$serviceDefinition->isPublic()) { + $serviceDefinition->setPublic(true); + } + + $tag = $serviceDefinition->getTag('form.type_extension'); + if (isset($tag[0]['extended_type'])) { + $extendedType = $tag[0]['extended_type']; + } else { + throw new InvalidArgumentException(sprintf('Tagged form type extension must have the extended type configured using the extended_type/extended-type attribute, none was configured for the "%s" service.', $serviceId)); + } + + $typeExtensions[$extendedType][] = $serviceId; + } + + $definition->replaceArgument(2, $typeExtensions); + + // Find all services annotated with "form.type_guesser" + $guessers = array_keys($container->findTaggedServiceIds('form.type_guesser')); + foreach ($guessers as $serviceId) { + $serviceDefinition = $container->getDefinition($serviceId); + if (!$serviceDefinition->isPublic()) { + $serviceDefinition->setPublic(true); + } + } + + $definition->replaceArgument(3, $guessers); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/LoggingTranslatorPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/LoggingTranslatorPass.php new file mode 100644 index 0000000..4945e1c --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/LoggingTranslatorPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\Translation\TranslatorBagInterface; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Abdellatif Ait boudad + */ +class LoggingTranslatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasAlias('logger') || !$container->hasAlias('translator')) { + return; + } + + if ($container->hasParameter('translator.logging') && $container->getParameter('translator.logging')) { + $translatorAlias = $container->getAlias('translator'); + $definition = $container->getDefinition((string) $translatorAlias); + $class = $container->getParameterBag()->resolveValue($definition->getClass()); + + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $translatorAlias)); + } + if ($r->isSubclassOf(TranslatorInterface::class) && $r->isSubclassOf(TranslatorBagInterface::class)) { + $container->getDefinition('translator.logging')->setDecoratedService('translator'); + $warmer = $container->getDefinition('translation.warmer'); + $subscriberAttributes = $warmer->getTag('container.service_subscriber'); + $warmer->clearTag('container.service_subscriber'); + + foreach ($subscriberAttributes as $k => $v) { + if ((!isset($v['id']) || 'translator' !== $v['id']) && (!isset($v['key']) || 'translator' !== $v['key'])) { + $warmer->addTag('container.service_subscriber', $v); + } + } + $warmer->addTag('container.service_subscriber', array('key' => 'translator', 'id' => 'translator.logging.inner')); + } + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ProfilerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ProfilerPass.php new file mode 100644 index 0000000..8e15979 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ProfilerPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Adds tagged data_collector services to profiler service. + * + * @author Fabien Potencier + */ +class ProfilerPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (false === $container->hasDefinition('profiler')) { + return; + } + + $definition = $container->getDefinition('profiler'); + + $collectors = new \SplPriorityQueue(); + $order = PHP_INT_MAX; + foreach ($container->findTaggedServiceIds('data_collector', true) as $id => $attributes) { + $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; + $template = null; + + if (isset($attributes[0]['template'])) { + if (!isset($attributes[0]['id'])) { + throw new InvalidArgumentException(sprintf('Data collector service "%s" must have an id attribute in order to specify a template', $id)); + } + $template = array($attributes[0]['id'], $attributes[0]['template']); + } + + $collectors->insert(array($id, $template), array($priority, --$order)); + } + + $templates = array(); + foreach ($collectors as $collector) { + $definition->addMethodCall('add', array(new Reference($collector[0]))); + $templates[$collector[0]] = $collector[1]; + } + + $container->setParameter('data_collector.templates', $templates); + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/PropertyInfoPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/PropertyInfoPass.php new file mode 100644 index 0000000..aa1cd6e --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/PropertyInfoPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass instead.', PropertyInfoPass::class), E_USER_DEPRECATED); + +use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass as BasePropertyInfoPass; + +/** + * Adds extractors to the property_info service. + * + * @author Kévin Dunglas + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BasePropertyInfoPass instead}. + */ +class PropertyInfoPass extends BasePropertyInfoPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/RoutingResolverPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/RoutingResolverPass.php new file mode 100644 index 0000000..87af59b --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/RoutingResolverPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass as BaseRoutingResolverPass; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', RoutingResolverPass::class, BaseRoutingResolverPass::class), E_USER_DEPRECATED); + +/** + * Adds tagged routing.loader services to routing.resolver service. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseRoutingResolverPass} + */ +class RoutingResolverPass extends BaseRoutingResolverPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/SerializerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/SerializerPass.php new file mode 100644 index 0000000..44b5466 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/SerializerPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\Serializer\DependencyInjection\SerializerPass instead.', SerializerPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Serializer\DependencyInjection\SerializerPass as BaseSerializerPass; + +/** + * Adds all services with the tags "serializer.encoder" and "serializer.normalizer" as + * encoders and normalizers to the Serializer service. + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseSerializerPass} instead. + * + * @author Javier Lopez + */ +class SerializerPass extends BaseSerializerPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TemplatingPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TemplatingPass.php new file mode 100644 index 0000000..8de5121 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TemplatingPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface as FrameworkBundleEngineInterface; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Templating\EngineInterface as ComponentEngineInterface; + +class TemplatingPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if ($container->hasDefinition('templating')) { + return; + } + + if ($container->hasAlias('templating')) { + $container->setAlias(ComponentEngineInterface::class, new Alias('templating', false)); + $container->setAlias(FrameworkBundleEngineInterface::class, new Alias('templating', false)); + } + + if ($container->hasDefinition('templating.engine.php')) { + $refs = array(); + $helpers = array(); + foreach ($container->findTaggedServiceIds('templating.helper', true) as $id => $attributes) { + if (isset($attributes[0]['alias'])) { + $helpers[$attributes[0]['alias']] = $id; + $refs[$id] = new Reference($id); + } + } + + if (\count($helpers) > 0) { + $definition = $container->getDefinition('templating.engine.php'); + $definition->addMethodCall('setHelpers', array($helpers)); + + if ($container->hasDefinition('templating.engine.php.helpers_locator')) { + $container->getDefinition('templating.engine.php.helpers_locator')->replaceArgument(0, $refs); + } + } + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationDumperPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationDumperPass.php new file mode 100644 index 0000000..9ddf312 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationDumperPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass instead.', TranslationDumperPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass as BaseTranslationDumperPass; + +/** + * Adds tagged translation.formatter services to translation writer. + * + * @deprecated since version 3.4, to be removed in 4.0. Use {@link BaseTranslationDumperPass instead}. + */ +class TranslationDumperPass extends BaseTranslationDumperPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationExtractorPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationExtractorPass.php new file mode 100644 index 0000000..8de309d --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslationExtractorPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass instead.', TranslationExtractorPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass as BaseTranslationExtractorPass; + +/** + * Adds tagged translation.formatter services to translation writer. + * + * @deprecated since version 3.4, to be removed in 4.0. Use {@link BaseTranslationDumperPass instead}. + */ +class TranslationExtractorPass extends BaseTranslationExtractorPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslatorPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslatorPass.php new file mode 100644 index 0000000..9f6a0ab --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/TranslatorPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\DependencyInjection\TranslatorPass instead.', TranslatorPass::class), E_USER_DEPRECATED); + +use Symfony\Component\Translation\DependencyInjection\TranslatorPass as BaseTranslatorPass; + +/** + * Adds tagged translation.formatter services to translation writer. + * + * @deprecated since version 3.4, to be removed in 4.0. Use {@link BaseTranslatorPass instead}. + */ +class TranslatorPass extends BaseTranslatorPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/UnusedTagsPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/UnusedTagsPass.php new file mode 100644 index 0000000..264bde0 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/UnusedTagsPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Find all service tags which are defined, but not used and yield a warning log message. + * + * @author Florian Pfitzer + */ +class UnusedTagsPass implements CompilerPassInterface +{ + private $whitelist = array( + 'annotations.cached_reader', + 'cache.pool.clearer', + 'console.command', + 'container.hot_path', + 'container.service_locator', + 'container.service_subscriber', + 'controller.service_arguments', + 'config_cache.resource_checker', + 'data_collector', + 'form.type', + 'form.type_extension', + 'form.type_guesser', + 'kernel.cache_clearer', + 'kernel.cache_warmer', + 'kernel.event_listener', + 'kernel.event_subscriber', + 'kernel.fragment_renderer', + 'monolog.logger', + 'routing.expression_language_provider', + 'routing.loader', + 'security.expression_language_provider', + 'security.remember_me_aware', + 'security.voter', + 'serializer.encoder', + 'serializer.normalizer', + 'templating.helper', + 'translation.dumper', + 'translation.extractor', + 'translation.loader', + 'twig.extension', + 'twig.loader', + 'validator.constraint_validator', + 'validator.initializer', + ); + + public function process(ContainerBuilder $container) + { + $tags = array_unique(array_merge($container->findTags(), $this->whitelist)); + + foreach ($container->findUnusedTags() as $tag) { + // skip whitelisted tags + if (\in_array($tag, $this->whitelist)) { + continue; + } + + // check for typos + $candidates = array(); + foreach ($tags as $definedTag) { + if ($definedTag === $tag) { + continue; + } + + if (false !== strpos($definedTag, $tag) || levenshtein($tag, $definedTag) <= \strlen($tag) / 3) { + $candidates[] = $definedTag; + } + } + + $services = array_keys($container->findTaggedServiceIds($tag)); + $message = sprintf('Tag "%s" was defined on service(s) "%s", but was never used.', $tag, implode('", "', $services)); + if (!empty($candidates)) { + $message .= sprintf(' Did you mean "%s"?', implode('", "', $candidates)); + } + + $container->log($this, $message); + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ValidateWorkflowsPass.php new file mode 100644 index 0000000..4a85e4e --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/ValidateWorkflowsPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass as BaseValidateWorkflowsPass; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', ValidateWorkflowsPass::class, BaseValidateWorkflowsPass::class), E_USER_DEPRECATED); + +/** + * @author Tobias Nyholm + * + * @deprecated since version 3.3, to be removed in 4.0. Use {@link BaseValidateWorkflowsPass} instead + */ +class ValidateWorkflowsPass extends BaseValidateWorkflowsPass +{ +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.php new file mode 100644 index 0000000..c5498ad --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Compiler/WorkflowGuardListenerPass.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\Bundle\FrameworkBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Exception\LogicException; + +/** + * @author Christian Flothmann + * @author Grégoire Pineau + */ +class WorkflowGuardListenerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasParameter('workflow.has_guard_listeners')) { + return; + } + + $container->getParameterBag()->remove('workflow.has_guard_listeners'); + + $servicesNeeded = array( + 'security.token_storage', + 'security.authorization_checker', + 'security.authentication.trust_resolver', + 'security.role_hierarchy', + ); + + foreach ($servicesNeeded as $service) { + if (!$container->has($service)) { + throw new LogicException(sprintf('The "%s" service is needed to be able to use the workflow guard listener.', $service)); + } + } + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/Configuration.php b/vendor/symfony/framework-bundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..7f47411 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,960 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; + +use Doctrine\Common\Annotations\Annotation; +use Doctrine\Common\Cache\Cache; +use Symfony\Bundle\FullStack; +use Symfony\Component\Asset\Package; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Form\Form; +use Symfony\Component\Lock\Lock; +use Symfony\Component\Lock\Store\SemaphoreStore; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Translation\Translator; +use Symfony\Component\Validator\Validation; +use Symfony\Component\WebLink\HttpHeaderSerializer; + +/** + * FrameworkExtension configuration structure. + * + * @author Jeremy Mikola + */ +class Configuration implements ConfigurationInterface +{ + private $debug; + + /** + * @param bool $debug Whether debugging is enabled or not + */ + public function __construct($debug) + { + $this->debug = (bool) $debug; + } + + /** + * Generates the configuration tree builder. + * + * @return TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('framework'); + + $rootNode + ->beforeNormalization() + ->ifTrue(function ($v) { return !isset($v['assets']) && isset($v['templating']) && class_exists(Package::class); }) + ->then(function ($v) { + $v['assets'] = array(); + + return $v; + }) + ->end() + ->children() + ->scalarNode('secret')->end() + ->scalarNode('http_method_override') + ->info("Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. Note: When using the HttpCache, you need to call the method in your front controller instead") + ->defaultTrue() + ->end() + ->arrayNode('trusted_proxies') + ->setDeprecated('The "%path%.%node%" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead.') + ->beforeNormalization() + ->ifTrue(function ($v) { + return !\is_array($v) && null !== $v; + }) + ->then(function ($v) { return \is_bool($v) ? array() : preg_split('/\s*,\s*/', $v); }) + ->end() + ->prototype('scalar') + ->validate() + ->ifTrue(function ($v) { + if (empty($v)) { + return false; + } + + if (false !== strpos($v, '/')) { + if ('0.0.0.0/0' === $v) { + return false; + } + + list($v, $mask) = explode('/', $v, 2); + + if (strcmp($mask, (int) $mask) || $mask < 1 || $mask > (false !== strpos($v, ':') ? 128 : 32)) { + return true; + } + } + + return !filter_var($v, FILTER_VALIDATE_IP); + }) + ->thenInvalid('Invalid proxy IP "%s"') + ->end() + ->end() + ->end() + ->scalarNode('ide')->defaultNull()->end() + ->booleanNode('test')->end() + ->scalarNode('default_locale')->defaultValue('en')->end() + ->arrayNode('trusted_hosts') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ; + + $this->addCsrfSection($rootNode); + $this->addFormSection($rootNode); + $this->addEsiSection($rootNode); + $this->addSsiSection($rootNode); + $this->addFragmentsSection($rootNode); + $this->addProfilerSection($rootNode); + $this->addWorkflowSection($rootNode); + $this->addRouterSection($rootNode); + $this->addSessionSection($rootNode); + $this->addRequestSection($rootNode); + $this->addTemplatingSection($rootNode); + $this->addAssetsSection($rootNode); + $this->addTranslatorSection($rootNode); + $this->addValidationSection($rootNode); + $this->addAnnotationsSection($rootNode); + $this->addSerializerSection($rootNode); + $this->addPropertyAccessSection($rootNode); + $this->addPropertyInfoSection($rootNode); + $this->addCacheSection($rootNode); + $this->addPhpErrorsSection($rootNode); + $this->addWebLinkSection($rootNode); + $this->addLockSection($rootNode); + + return $treeBuilder; + } + + private function addCsrfSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('csrf_protection') + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->addDefaultsIfNotSet() + ->children() + // defaults to framework.session.enabled && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class) + ->booleanNode('enabled')->defaultNull()->end() + ->end() + ->end() + ->end() + ; + } + + private function addFormSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('form') + ->info('form configuration') + ->{!class_exists(FullStack::class) && class_exists(Form::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->children() + ->arrayNode('csrf_protection') + ->treatFalseLike(array('enabled' => false)) + ->treatTrueLike(array('enabled' => true)) + ->treatNullLike(array('enabled' => true)) + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('enabled')->defaultNull()->end() // defaults to framework.csrf_protection.enabled + ->scalarNode('field_name')->defaultValue('_token')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addEsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('esi') + ->info('esi configuration') + ->canBeEnabled() + ->end() + ->end() + ; + } + + private function addSsiSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('ssi') + ->info('ssi configuration') + ->canBeEnabled() + ->end() + ->end(); + } + + private function addFragmentsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('fragments') + ->info('fragments configuration') + ->canBeEnabled() + ->children() + ->scalarNode('path')->defaultValue('/_fragment')->end() + ->end() + ->end() + ->end() + ; + } + + private function addProfilerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('profiler') + ->info('profiler configuration') + ->canBeEnabled() + ->children() + ->booleanNode('collect')->defaultTrue()->end() + ->booleanNode('only_exceptions')->defaultFalse()->end() + ->booleanNode('only_master_requests')->defaultFalse()->end() + ->scalarNode('dsn')->defaultValue('file:%kernel.cache_dir%/profiler')->end() + ->arrayNode('matcher') + ->setDeprecated('The "profiler.matcher" configuration key has been deprecated in Symfony 3.4 and it will be removed in 4.0.') + ->canBeEnabled() + ->performNoDeepMerging() + ->fixXmlConfig('ip') + ->children() + ->scalarNode('path') + ->info('use the urldecoded format') + ->example('^/path to resource/') + ->end() + ->scalarNode('service')->end() + ->arrayNode('ips') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addWorkflowSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->fixXmlConfig('workflow') + ->children() + ->arrayNode('workflows') + ->canBeEnabled() + ->beforeNormalization() + ->always(function ($v) { + if (true === $v['enabled']) { + $workflows = $v; + unset($workflows['enabled']); + + if (1 === \count($workflows) && isset($workflows[0]['enabled'])) { + $workflows = array(); + } + + $v = array( + 'enabled' => true, + 'workflows' => $workflows, + ); + } + + return $v; + }) + ->end() + ->children() + ->arrayNode('workflows') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('support') + ->fixXmlConfig('place') + ->fixXmlConfig('transition') + ->children() + ->arrayNode('audit_trail') + ->canBeEnabled() + ->end() + ->enumNode('type') + ->values(array('workflow', 'state_machine')) + ->end() + ->arrayNode('marking_store') + ->fixXmlConfig('argument') + ->children() + ->enumNode('type') + ->values(array('multiple_state', 'single_state')) + ->end() + ->arrayNode('arguments') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array($v); }) + ->end() + ->requiresAtLeastOneElement() + ->prototype('scalar') + ->end() + ->end() + ->scalarNode('service') + ->cannotBeEmpty() + ->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return isset($v['type']) && isset($v['service']); }) + ->thenInvalid('"type" and "service" cannot be used together.') + ->end() + ->validate() + ->ifTrue(function ($v) { return !empty($v['arguments']) && isset($v['service']); }) + ->thenInvalid('"arguments" and "service" cannot be used together.') + ->end() + ->end() + ->arrayNode('supports') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar') + ->cannotBeEmpty() + ->validate() + ->ifTrue(function ($v) { return !class_exists($v) && !interface_exists($v); }) + ->thenInvalid('The supported class or interface "%s" does not exist.') + ->end() + ->end() + ->end() + ->scalarNode('support_strategy') + ->cannotBeEmpty() + ->end() + ->scalarNode('initial_place') + ->defaultNull() + ->end() + ->arrayNode('places') + ->isRequired() + ->requiresAtLeastOneElement() + ->prototype('scalar') + ->cannotBeEmpty() + ->end() + ->end() + ->arrayNode('transitions') + ->beforeNormalization() + ->always() + ->then(function ($transitions) { + // It's an indexed array, we let the validation occurs + if (isset($transitions[0])) { + return $transitions; + } + + foreach ($transitions as $name => $transition) { + if (array_key_exists('name', $transition)) { + continue; + } + $transition['name'] = $name; + $transitions[$name] = $transition; + } + + return $transitions; + }) + ->end() + ->isRequired() + ->requiresAtLeastOneElement() + ->prototype('array') + ->children() + ->scalarNode('name') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->scalarNode('guard') + ->cannotBeEmpty() + ->info('An expression to block the transition') + ->example('is_fully_authenticated() and has_role(\'ROLE_JOURNALIST\') and subject.getTitle() == \'My first article\'') + ->end() + ->arrayNode('from') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array($v); }) + ->end() + ->requiresAtLeastOneElement() + ->prototype('scalar') + ->cannotBeEmpty() + ->end() + ->end() + ->arrayNode('to') + ->beforeNormalization() + ->ifString() + ->then(function ($v) { return array($v); }) + ->end() + ->requiresAtLeastOneElement() + ->prototype('scalar') + ->cannotBeEmpty() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return $v['supports'] && isset($v['support_strategy']); + }) + ->thenInvalid('"supports" and "support_strategy" cannot be used together.') + ->end() + ->validate() + ->ifTrue(function ($v) { + return !$v['supports'] && !isset($v['support_strategy']); + }) + ->thenInvalid('"supports" or "support_strategy" should be configured.') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRouterSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('router') + ->info('router configuration') + ->canBeEnabled() + ->children() + ->scalarNode('resource')->isRequired()->end() + ->scalarNode('type')->end() + ->scalarNode('http_port')->defaultValue(80)->end() + ->scalarNode('https_port')->defaultValue(443)->end() + ->scalarNode('strict_requirements') + ->info( + "set to true to throw an exception when a parameter does not match the requirements\n". + "set to false to disable exceptions when a parameter does not match the requirements (and return null instead)\n". + "set to null to disable parameter checks against requirements\n". + "'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production" + ) + ->defaultTrue() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addSessionSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('session') + ->info('session configuration') + ->canBeEnabled() + ->children() + ->scalarNode('storage_id')->defaultValue('session.storage.native')->end() + ->scalarNode('handler_id')->defaultValue('session.handler.native_file')->end() + ->scalarNode('name') + ->validate() + ->ifTrue(function ($v) { + parse_str($v, $parsed); + + return implode('&', array_keys($parsed)) !== (string) $v; + }) + ->thenInvalid('Session name %s contains illegal character(s)') + ->end() + ->end() + ->scalarNode('cookie_lifetime')->end() + ->scalarNode('cookie_path')->end() + ->scalarNode('cookie_domain')->end() + ->booleanNode('cookie_secure')->end() + ->booleanNode('cookie_httponly')->defaultTrue()->end() + ->booleanNode('use_cookies')->end() + ->scalarNode('gc_divisor')->end() + ->scalarNode('gc_probability')->defaultValue(1)->end() + ->scalarNode('gc_maxlifetime')->end() + ->booleanNode('use_strict_mode') + ->defaultTrue() + ->setDeprecated('The "%path%.%node%" option is enabled by default and deprecated since Symfony 3.4. It will be always enabled in 4.0.') + ->end() + ->scalarNode('save_path')->defaultValue('%kernel.cache_dir%/sessions')->end() + ->integerNode('metadata_update_threshold') + ->defaultValue('0') + ->info('seconds to wait between 2 session metadata updates') + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addRequestSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('request') + ->info('request configuration') + ->canBeEnabled() + ->fixXmlConfig('format') + ->children() + ->arrayNode('formats') + ->useAttributeAsKey('name') + ->prototype('array') + ->beforeNormalization() + ->ifTrue(function ($v) { return \is_array($v) && isset($v['mime_type']); }) + ->then(function ($v) { return $v['mime_type']; }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return !\is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTemplatingSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('templating') + ->info('templating configuration') + ->canBeEnabled() + ->beforeNormalization() + ->ifTrue(function ($v) { return false === $v || \is_array($v) && false === $v['enabled']; }) + ->then(function () { return array('enabled' => false, 'engines' => false); }) + ->end() + ->children() + ->scalarNode('hinclude_default_template')->defaultNull()->end() + ->scalarNode('cache')->end() + ->arrayNode('form') + ->addDefaultsIfNotSet() + ->fixXmlConfig('resource') + ->children() + ->arrayNode('resources') + ->addDefaultChildrenIfNoneSet() + ->prototype('scalar')->defaultValue('FrameworkBundle:Form')->end() + ->validate() + ->ifTrue(function ($v) {return !\in_array('FrameworkBundle:Form', $v); }) + ->then(function ($v) { + return array_merge(array('FrameworkBundle:Form'), $v); + }) + ->end() + ->end() + ->end() + ->end() + ->end() + ->fixXmlConfig('engine') + ->children() + ->arrayNode('engines') + ->example(array('twig')) + ->isRequired() + ->requiresAtLeastOneElement() + ->canBeUnset() + ->beforeNormalization() + ->ifTrue(function ($v) { return !\is_array($v) && false !== $v; }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->fixXmlConfig('loader') + ->children() + ->arrayNode('loaders') + ->beforeNormalization() + ->ifTrue(function ($v) { return !\is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addAssetsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('assets') + ->info('assets configuration') + ->{!class_exists(FullStack::class) && class_exists(Package::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version_strategy')->defaultNull()->end() + ->scalarNode('version')->defaultNull()->end() + ->scalarNode('version_format')->defaultValue('%%s?%%s')->end() + ->scalarNode('json_manifest_path')->defaultNull()->end() + ->scalarNode('base_path')->defaultValue('')->end() + ->arrayNode('base_urls') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !\is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version_strategy']) && isset($v['version']); + }) + ->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets".') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version_strategy']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets".') + ->end() + ->fixXmlConfig('package') + ->children() + ->arrayNode('packages') + ->useAttributeAsKey('name') + ->prototype('array') + ->fixXmlConfig('base_url') + ->children() + ->scalarNode('version_strategy')->defaultNull()->end() + ->scalarNode('version') + ->beforeNormalization() + ->ifTrue(function ($v) { return '' === $v; }) + ->then(function ($v) { return; }) + ->end() + ->end() + ->scalarNode('version_format')->defaultNull()->end() + ->scalarNode('json_manifest_path')->defaultNull()->end() + ->scalarNode('base_path')->defaultValue('')->end() + ->arrayNode('base_urls') + ->requiresAtLeastOneElement() + ->beforeNormalization() + ->ifTrue(function ($v) { return !\is_array($v); }) + ->then(function ($v) { return array($v); }) + ->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version_strategy']) && isset($v['version']); + }) + ->thenInvalid('You cannot use both "version_strategy" and "version" at the same time under "assets" packages.') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version_strategy']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.') + ->end() + ->validate() + ->ifTrue(function ($v) { + return isset($v['version']) && isset($v['json_manifest_path']); + }) + ->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addTranslatorSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('translator') + ->info('translator configuration') + ->{!class_exists(FullStack::class) && class_exists(Translator::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->fixXmlConfig('fallback') + ->fixXmlConfig('path') + ->children() + ->arrayNode('fallbacks') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->defaultValue(array('en')) + ->end() + ->booleanNode('logging')->defaultValue($this->debug)->end() + ->scalarNode('formatter')->defaultValue('translator.formatter.default')->end() + ->scalarNode('default_path') + ->info('The default path used to load translations') + ->defaultValue('%kernel.project_dir%/translations') + ->end() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addValidationSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('validation') + ->info('validation configuration') + ->{!class_exists(FullStack::class) && class_exists(Validation::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->children() + ->scalarNode('cache') + // Can be removed in 4.0, when validator.mapping.cache.doctrine.apc is removed + ->setDeprecated('The "%path%.%node%" option is deprecated since Symfony 3.2 and will be removed in 4.0. Configure the "cache.validator" service under "framework.cache.pools" instead.') + ->beforeNormalization() + ->ifString()->then(function ($v) { + if ('validator.mapping.cache.doctrine.apc' === $v && !class_exists('Doctrine\Common\Cache\ApcCache')) { + throw new LogicException('Doctrine APC cache for the validator cannot be enabled as the Doctrine Cache package is not installed.'); + } + + return $v; + }) + ->end() + ->end() + ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() + ->arrayNode('static_method') + ->defaultValue(array('loadValidatorMetadata')) + ->prototype('scalar')->end() + ->treatFalseLike(array()) + ->validate() + ->ifTrue(function ($v) { return !\is_array($v); }) + ->then(function ($v) { return (array) $v; }) + ->end() + ->end() + ->scalarNode('translation_domain')->defaultValue('validators')->end() + ->booleanNode('strict_email')->defaultFalse()->end() + ->arrayNode('mapping') + ->addDefaultsIfNotSet() + ->fixXmlConfig('path') + ->children() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addAnnotationsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('annotations') + ->info('annotation configuration') + ->{class_exists(Annotation::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->children() + ->scalarNode('cache')->defaultValue(interface_exists(Cache::class) ? 'php_array' : 'none')->end() + ->scalarNode('file_cache_dir')->defaultValue('%kernel.cache_dir%/annotations')->end() + ->booleanNode('debug')->defaultValue($this->debug)->end() + ->end() + ->end() + ->end() + ; + } + + private function addSerializerSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('serializer') + ->info('serializer configuration') + ->{!class_exists(FullStack::class) && class_exists(Serializer::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->children() + ->booleanNode('enable_annotations')->{!class_exists(FullStack::class) && class_exists(Annotation::class) ? 'defaultTrue' : 'defaultFalse'}()->end() + ->scalarNode('cache') + ->setDeprecated('The "%path%.%node%" option is deprecated since Symfony 3.1 and will be removed in 4.0. Configure the "cache.serializer" service under "framework.cache.pools" instead.') + ->end() + ->scalarNode('name_converter')->end() + ->scalarNode('circular_reference_handler')->end() + ->arrayNode('mapping') + ->addDefaultsIfNotSet() + ->fixXmlConfig('path') + ->children() + ->arrayNode('paths') + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addPropertyAccessSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('property_access') + ->addDefaultsIfNotSet() + ->info('Property access configuration') + ->children() + ->booleanNode('magic_call')->defaultFalse()->end() + ->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end() + ->end() + ->end() + ->end() + ; + } + + private function addPropertyInfoSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('property_info') + ->info('Property info configuration') + ->canBeEnabled() + ->end() + ->end() + ; + } + + private function addCacheSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('cache') + ->info('Cache configuration') + ->addDefaultsIfNotSet() + ->fixXmlConfig('pool') + ->children() + ->scalarNode('prefix_seed') + ->info('Used to namespace cache keys when using several apps with the same shared backend') + ->example('my-application-name') + ->end() + ->scalarNode('app') + ->info('App related cache pools configuration') + ->defaultValue('cache.adapter.filesystem') + ->end() + ->scalarNode('system') + ->info('System related cache pools configuration') + ->defaultValue('cache.adapter.system') + ->end() + ->scalarNode('directory')->defaultValue('%kernel.cache_dir%/pools')->end() + ->scalarNode('default_doctrine_provider')->end() + ->scalarNode('default_psr6_provider')->end() + ->scalarNode('default_redis_provider')->defaultValue('redis://localhost')->end() + ->scalarNode('default_memcached_provider')->defaultValue('memcached://localhost')->end() + ->arrayNode('pools') + ->useAttributeAsKey('name') + ->prototype('array') + ->children() + ->scalarNode('adapter')->defaultValue('cache.app')->end() + ->booleanNode('public')->defaultFalse()->end() + ->integerNode('default_lifetime')->end() + ->scalarNode('provider') + ->info('The service name to use as provider when the specified adapter needs one.') + ->end() + ->scalarNode('clearer')->end() + ->end() + ->end() + ->validate() + ->ifTrue(function ($v) { return isset($v['cache.app']) || isset($v['cache.system']); }) + ->thenInvalid('"cache.app" and "cache.system" are reserved names') + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addPhpErrorsSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('php_errors') + ->info('PHP errors handling configuration') + ->addDefaultsIfNotSet() + ->children() + ->booleanNode('log') + ->info('Use the app logger instead of the PHP logger for logging PHP errors.') + ->defaultValue($this->debug) + ->treatNullLike($this->debug) + ->end() + ->booleanNode('throw') + ->info('Throw PHP errors as \ErrorException instances.') + ->defaultValue($this->debug) + ->treatNullLike($this->debug) + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addLockSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('lock') + ->info('Lock configuration') + ->{!class_exists(FullStack::class) && class_exists(Lock::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->beforeNormalization() + ->ifString()->then(function ($v) { return array('enabled' => true, 'resources' => $v); }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return \is_array($v) && !isset($v['resources']); }) + ->then(function ($v) { + $e = $v['enabled']; + unset($v['enabled']); + + return array('enabled' => $e, 'resources' => $v); + }) + ->end() + ->addDefaultsIfNotSet() + ->fixXmlConfig('resource') + ->children() + ->arrayNode('resources') + ->requiresAtLeastOneElement() + ->defaultValue(array('default' => array(class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock'))) + ->beforeNormalization() + ->ifString()->then(function ($v) { return array('default' => $v); }) + ->end() + ->beforeNormalization() + ->ifTrue(function ($v) { return \is_array($v) && array_keys($v) === range(0, \count($v) - 1); }) + ->then(function ($v) { return array('default' => $v); }) + ->end() + ->prototype('array') + ->beforeNormalization()->ifString()->then(function ($v) { return array($v); })->end() + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ; + } + + private function addWebLinkSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('web_link') + ->info('web links configuration') + ->{!class_exists(FullStack::class) && class_exists(HttpHeaderSerializer::class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->end() + ->end() + ; + } +} diff --git a/vendor/symfony/framework-bundle/DependencyInjection/FrameworkExtension.php b/vendor/symfony/framework-bundle/DependencyInjection/FrameworkExtension.php new file mode 100644 index 0000000..cc23e81 --- /dev/null +++ b/vendor/symfony/framework-bundle/DependencyInjection/FrameworkExtension.php @@ -0,0 +1,1731 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; + +use Doctrine\Common\Annotations\AnnotationRegistry; +use Doctrine\Common\Annotations\Reader; +use Symfony\Bridge\Monolog\Processor\DebugProcessor; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Routing\AnnotatedRouteControllerLoader; +use Symfony\Bundle\FullStack; +use Symfony\Component\Cache\Adapter\AbstractAdapter; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\ResettableInterface; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\Resource\DirectoryResource; +use Symfony\Component\Config\ResourceCheckerInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\EnvVarProcessorInterface; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Finder\Finder; +use Symfony\Component\Form\FormTypeGuesserInterface; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\Lock\Factory; +use Symfony\Component\Lock\Lock; +use Symfony\Component\Lock\LockInterface; +use Symfony\Component\Lock\Store\StoreFactory; +use Symfony\Component\Lock\StoreInterface; +use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\PropertyInfo\PropertyAccessExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyDescriptionExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyListExtractorInterface; +use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; +use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; +use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Security\Core\Security; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Serializer\Encoder\DecoderInterface; +use Symfony\Component\Serializer\Encoder\EncoderInterface; +use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; +use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; +use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand; +use Symfony\Component\Translation\Translator; +use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\ObjectInitializerInterface; +use Symfony\Component\WebLink\HttpHeaderSerializer; +use Symfony\Component\Workflow; +use Symfony\Component\Yaml\Command\LintCommand as BaseYamlLintCommand; +use Symfony\Component\Yaml\Yaml; + +/** + * FrameworkExtension. + * + * @author Fabien Potencier + * @author Jeremy Mikola + * @author Kévin Dunglas + * @author Grégoire Pineau + */ +class FrameworkExtension extends Extension +{ + private $formConfigEnabled = false; + private $translationConfigEnabled = false; + private $sessionConfigEnabled = false; + private $annotationsConfigEnabled = false; + private $validatorConfigEnabled = false; + + /** + * @var string|null + */ + private $kernelRootHash; + + /** + * Responds to the app.config configuration parameter. + * + * @throws LogicException + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new XmlFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); + + $loader->load('web.xml'); + $loader->load('services.xml'); + + $container->getDefinition('kernel.class_cache.cache_warmer')->setPrivate(true); + $container->getDefinition('uri_signer')->setPrivate(true); + $container->getDefinition('config_cache_factory')->setPrivate(true); + $container->getDefinition('response_listener')->setPrivate(true); + $container->getDefinition('file_locator')->setPrivate(true); + $container->getDefinition('streamed_response_listener')->setPrivate(true); + $container->getDefinition('locale_listener')->setPrivate(true); + $container->getDefinition('validate_request_listener')->setPrivate(true); + + // forward compatibility with Symfony 4.0 where the ContainerAwareEventDispatcher class is removed + if (!class_exists(ContainerAwareEventDispatcher::class)) { + $definition = $container->getDefinition('event_dispatcher'); + $definition->setClass(EventDispatcher::class); + $definition->setArguments(array()); + } + + if (\PHP_VERSION_ID < 70000) { + $definition = $container->getDefinition('kernel.class_cache.cache_warmer'); + $definition->addTag('kernel.cache_warmer'); + // Ignore deprecation for PHP versions below 7.0 + $definition->setDeprecated(false); + } + + $loader->load('fragment_renderer.xml'); + + $container->getDefinition('fragment.handler')->setPrivate(true); + $container->getDefinition('fragment.renderer.inline')->setPrivate(true); + $container->getDefinition('fragment.renderer.hinclude')->setPrivate(true); + $container->getDefinition('fragment.renderer.esi')->setPrivate(true); + $container->getDefinition('fragment.renderer.ssi')->setPrivate(true); + + if (class_exists(Application::class)) { + $loader->load('console.xml'); + + if (!class_exists(BaseXliffLintCommand::class)) { + $container->removeDefinition('console.command.xliff_lint'); + } + if (!class_exists(BaseYamlLintCommand::class)) { + $container->removeDefinition('console.command.yaml_lint'); + } + } + + // Load Cache configuration first as it is used by other components + $loader->load('cache.xml'); + + $container->getDefinition('cache.adapter.system')->setPrivate(true); + $container->getDefinition('cache.adapter.apcu')->setPrivate(true); + $container->getDefinition('cache.adapter.doctrine')->setPrivate(true); + $container->getDefinition('cache.adapter.filesystem')->setPrivate(true); + $container->getDefinition('cache.adapter.psr6')->setPrivate(true); + $container->getDefinition('cache.adapter.redis')->setPrivate(true); + $container->getDefinition('cache.adapter.memcached')->setPrivate(true); + $container->getDefinition('cache.default_clearer')->setPrivate(true); + + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $this->annotationsConfigEnabled = $this->isConfigEnabled($container, $config['annotations']); + $this->translationConfigEnabled = $this->isConfigEnabled($container, $config['translator']); + + // A translator must always be registered (as support is included by + // default in the Form and Validator component). If disabled, an identity + // translator will be used and everything will still work as expected. + if ($this->isConfigEnabled($container, $config['translator']) || $this->isConfigEnabled($container, $config['form']) || $this->isConfigEnabled($container, $config['validation'])) { + if (!class_exists('Symfony\Component\Translation\Translator') && $this->isConfigEnabled($container, $config['translator'])) { + throw new LogicException('Translation support cannot be enabled as the Translation component is not installed.'); + } + + if (class_exists(Translator::class)) { + $loader->load('identity_translator.xml'); + } + } + + if (isset($config['secret'])) { + $container->setParameter('kernel.secret', $config['secret']); + } + + $container->setParameter('kernel.http_method_override', $config['http_method_override']); + $container->setParameter('kernel.trusted_hosts', $config['trusted_hosts']); + if ($config['trusted_proxies']) { + $container->setParameter('kernel.trusted_proxies', $config['trusted_proxies']); + } + $container->setParameter('kernel.default_locale', $config['default_locale']); + + if (!$container->hasParameter('debug.file_link_format')) { + if (!$container->hasParameter('templating.helper.code.file_link_format')) { + $links = array( + 'textmate' => 'txmt://open?url=file://%%f&line=%%l', + 'macvim' => 'mvim://open?url=file://%%f&line=%%l', + 'emacs' => 'emacs://open?url=file://%%f&line=%%l', + 'sublime' => 'subl://open?url=file://%%f&line=%%l', + 'phpstorm' => 'phpstorm://open?file=%%f&line=%%l', + ); + $ide = $config['ide']; + + $container->setParameter('templating.helper.code.file_link_format', str_replace('%', '%%', ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format')) ?: (isset($links[$ide]) ? $links[$ide] : $ide)); + } + $container->setParameter('debug.file_link_format', '%templating.helper.code.file_link_format%'); + } + + if (!empty($config['test'])) { + $loader->load('test.xml'); + + $container->getDefinition('test.client.history')->setPrivate(true); + $container->getDefinition('test.client.cookiejar')->setPrivate(true); + $container->getDefinition('test.session.listener')->setPrivate(true); + } + + if ($this->isConfigEnabled($container, $config['session'])) { + $this->sessionConfigEnabled = true; + $this->registerSessionConfiguration($config['session'], $container, $loader); + } + + if ($this->isConfigEnabled($container, $config['request'])) { + $this->registerRequestConfiguration($config['request'], $container, $loader); + } + + if (null === $config['csrf_protection']['enabled']) { + $config['csrf_protection']['enabled'] = $this->sessionConfigEnabled && !class_exists(FullStack::class) && interface_exists(CsrfTokenManagerInterface::class); + } + $this->registerSecurityCsrfConfiguration($config['csrf_protection'], $container, $loader); + + if ($this->isConfigEnabled($container, $config['form'])) { + if (!class_exists('Symfony\Component\Form\Form')) { + throw new LogicException('Form support cannot be enabled as the Form component is not installed.'); + } + + $this->formConfigEnabled = true; + $this->registerFormConfiguration($config, $container, $loader); + + if (class_exists('Symfony\Component\Validator\Validation')) { + $config['validation']['enabled'] = true; + } else { + $container->setParameter('validator.translation_domain', 'validators'); + + $container->removeDefinition('form.type_extension.form.validator'); + $container->removeDefinition('form.type_guesser.validator'); + } + } else { + $container->removeDefinition('console.command.form_debug'); + } + + if ($this->isConfigEnabled($container, $config['assets'])) { + if (!class_exists('Symfony\Component\Asset\Package')) { + throw new LogicException('Asset support cannot be enabled as the Asset component is not installed.'); + } + + $this->registerAssetsConfiguration($config['assets'], $container, $loader); + } + + if ($this->isConfigEnabled($container, $config['templating'])) { + if (!class_exists('Symfony\Component\Templating\PhpEngine')) { + throw new LogicException('Templating support cannot be enabled as the Templating component is not installed.'); + } + + $this->registerTemplatingConfiguration($config['templating'], $container, $loader); + } + + $this->registerValidationConfiguration($config['validation'], $container, $loader); + $this->registerEsiConfiguration($config['esi'], $container, $loader); + $this->registerSsiConfiguration($config['ssi'], $container, $loader); + $this->registerFragmentsConfiguration($config['fragments'], $container, $loader); + $this->registerTranslatorConfiguration($config['translator'], $container, $loader); + $this->registerProfilerConfiguration($config['profiler'], $container, $loader); + $this->registerCacheConfiguration($config['cache'], $container); + $this->registerWorkflowConfiguration($config['workflows'], $container, $loader); + $this->registerDebugConfiguration($config['php_errors'], $container, $loader); + $this->registerRouterConfiguration($config['router'], $container, $loader); + $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); + $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader); + + if ($this->isConfigEnabled($container, $config['serializer'])) { + if (!class_exists('Symfony\Component\Serializer\Serializer')) { + throw new LogicException('Serializer support cannot be enabled as the Serializer component is not installed.'); + } + + $this->registerSerializerConfiguration($config['serializer'], $container, $loader); + } + + if ($this->isConfigEnabled($container, $config['property_info'])) { + $this->registerPropertyInfoConfiguration($container, $loader); + } + + if ($this->isConfigEnabled($container, $config['lock'])) { + $this->registerLockConfiguration($config['lock'], $container, $loader); + } + + if ($this->isConfigEnabled($container, $config['web_link'])) { + if (!class_exists(HttpHeaderSerializer::class)) { + throw new LogicException('WebLink support cannot be enabled as the WebLink component is not installed.'); + } + + $loader->load('web_link.xml'); + } + + $this->addAnnotatedClassesToCompile(array( + '**\\Controller\\', + '**\\Entity\\', + + // Added explicitly so that we don't rely on the class map being dumped to make it work + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', + )); + + $container->registerForAutoconfiguration(Command::class) + ->addTag('console.command'); + $container->registerForAutoconfiguration(ResourceCheckerInterface::class) + ->addTag('config_cache.resource_checker'); + $container->registerForAutoconfiguration(EnvVarProcessorInterface::class) + ->addTag('container.env_var_processor'); + $container->registerForAutoconfiguration(ServiceSubscriberInterface::class) + ->addTag('container.service_subscriber'); + $container->registerForAutoconfiguration(ArgumentValueResolverInterface::class) + ->addTag('controller.argument_value_resolver'); + $container->registerForAutoconfiguration(AbstractController::class) + ->addTag('controller.service_arguments'); + $container->registerForAutoconfiguration(Controller::class) + ->addTag('controller.service_arguments'); + $container->registerForAutoconfiguration(DataCollectorInterface::class) + ->addTag('data_collector'); + $container->registerForAutoconfiguration(FormTypeInterface::class) + ->addTag('form.type'); + $container->registerForAutoconfiguration(FormTypeGuesserInterface::class) + ->addTag('form.type_guesser'); + $container->registerForAutoconfiguration(CacheClearerInterface::class) + ->addTag('kernel.cache_clearer'); + $container->registerForAutoconfiguration(CacheWarmerInterface::class) + ->addTag('kernel.cache_warmer'); + $container->registerForAutoconfiguration(EventSubscriberInterface::class) + ->addTag('kernel.event_subscriber'); + $container->registerForAutoconfiguration(ResettableInterface::class) + ->addTag('kernel.reset', array('method' => 'reset')); + $container->registerForAutoconfiguration(PropertyListExtractorInterface::class) + ->addTag('property_info.list_extractor'); + $container->registerForAutoconfiguration(PropertyTypeExtractorInterface::class) + ->addTag('property_info.type_extractor'); + $container->registerForAutoconfiguration(PropertyDescriptionExtractorInterface::class) + ->addTag('property_info.description_extractor'); + $container->registerForAutoconfiguration(PropertyAccessExtractorInterface::class) + ->addTag('property_info.access_extractor'); + $container->registerForAutoconfiguration(EncoderInterface::class) + ->addTag('serializer.encoder'); + $container->registerForAutoconfiguration(DecoderInterface::class) + ->addTag('serializer.encoder'); + $container->registerForAutoconfiguration(NormalizerInterface::class) + ->addTag('serializer.normalizer'); + $container->registerForAutoconfiguration(DenormalizerInterface::class) + ->addTag('serializer.normalizer'); + $container->registerForAutoconfiguration(ConstraintValidatorInterface::class) + ->addTag('validator.constraint_validator'); + $container->registerForAutoconfiguration(ObjectInitializerInterface::class) + ->addTag('validator.initializer'); + + if (!$container->getParameter('kernel.debug')) { + // remove tagged iterator argument for resource checkers + $container->getDefinition('config_cache_factory')->setArguments(array()); + } + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Config\\ConfigCache', + 'Symfony\\Component\\Config\\FileLocator', + + 'Symfony\\Component\\Debug\\ErrorHandler', + + 'Symfony\\Component\\DependencyInjection\\ContainerAwareInterface', + 'Symfony\\Component\\DependencyInjection\\Container', + + 'Symfony\\Component\\EventDispatcher\\Event', + 'Symfony\\Component\\EventDispatcher\\ContainerAwareEventDispatcher', + + 'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener', + 'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener', + 'Symfony\\Component\\HttpKernel\\Bundle\\Bundle', + 'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', + 'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata', + 'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory', + 'Symfony\\Component\\HttpKernel\\Event\\KernelEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent', + 'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent', + 'Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent', + 'Symfony\\Component\\HttpKernel\\HttpKernel', + 'Symfony\\Component\\HttpKernel\\KernelEvents', + 'Symfony\\Component\\HttpKernel\\Config\\FileLocator', + + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerNameParser', + 'Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver', + + // Cannot be included because annotations will parse the big compiled class file + // 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller', + + // cannot be included as commands are discovered based on the path to this class via Reflection + // 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle', + )); + } + } + + /** + * {@inheritdoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($container->getParameter('kernel.debug')); + } + + private function registerFormConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('form.xml'); + + $container->getDefinition('form.resolved_type_factory')->setPrivate(true); + $container->getDefinition('form.registry')->setPrivate(true); + $container->getDefinition('form.type_guesser.validator')->setPrivate(true); + $container->getDefinition('form.type.form')->setPrivate(true); + $container->getDefinition('form.type.choice')->setPrivate(true); + $container->getDefinition('form.type_extension.form.http_foundation')->setPrivate(true); + $container->getDefinition('form.type_extension.form.validator')->setPrivate(true); + $container->getDefinition('form.type_extension.repeated.validator')->setPrivate(true); + $container->getDefinition('form.type_extension.submit.validator')->setPrivate(true); + $container->getDefinition('form.type_extension.upload.validator')->setPrivate(true); + $container->getDefinition('deprecated.form.registry')->setPrivate(true); + + if (null === $config['form']['csrf_protection']['enabled']) { + $config['form']['csrf_protection']['enabled'] = $config['csrf_protection']['enabled']; + } + + if ($this->isConfigEnabled($container, $config['form']['csrf_protection'])) { + $loader->load('form_csrf.xml'); + + $container->getDefinition('form.type_extension.csrf')->setPrivate(true); + $container->getDefinition('deprecated.form.registry.csrf')->setPrivate(true); + + $container->setParameter('form.type_extension.csrf.enabled', true); + $container->setParameter('form.type_extension.csrf.field_name', $config['form']['csrf_protection']['field_name']); + } else { + $container->setParameter('form.type_extension.csrf.enabled', false); + } + + if (!class_exists(Translator::class)) { + $container->removeDefinition('form.type_extension.upload.validator'); + } + } + + private function registerEsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + $container->removeDefinition('fragment.renderer.esi'); + + return; + } + + $loader->load('esi.xml'); + + $container->getDefinition('esi')->setPrivate(true); + $container->getDefinition('esi_listener')->setPrivate(true); + } + + private function registerSsiConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + $container->removeDefinition('fragment.renderer.ssi'); + + return; + } + + $loader->load('ssi.xml'); + + $container->getDefinition('ssi')->setPrivate(true); + $container->getDefinition('ssi_listener')->setPrivate(true); + } + + private function registerFragmentsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + $container->removeDefinition('fragment.renderer.hinclude'); + + return; + } + + $loader->load('fragment_listener.xml'); + $container->setParameter('fragment.path', $config['path']); + } + + private function registerProfilerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + // this is needed for the WebProfiler to work even if the profiler is disabled + $container->setParameter('data_collector.templates', array()); + + return; + } + + $loader->load('profiling.xml'); + $loader->load('collectors.xml'); + $loader->load('cache_debug.xml'); + + $container->getDefinition('data_collector.request')->setPrivate(true); + $container->getDefinition('data_collector.router')->setPrivate(true); + $container->getDefinition('profiler_listener')->setPrivate(true); + + if ($this->formConfigEnabled) { + $loader->load('form_debug.xml'); + + $container->getDefinition('form.resolved_type_factory')->setPrivate(true); + $container->getDefinition('data_collector.form.extractor')->setPrivate(true); + $container->getDefinition('data_collector.form')->setPrivate(true); + } + + if ($this->validatorConfigEnabled) { + $loader->load('validator_debug.xml'); + } + + if ($this->translationConfigEnabled) { + $loader->load('translation_debug.xml'); + + $container->getDefinition('data_collector.translation')->setPrivate(true); + + $container->getDefinition('translator.data_collector')->setDecoratedService('translator'); + } + + $container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']); + $container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']); + + // Choose storage class based on the DSN + list($class) = explode(':', $config['dsn'], 2); + if ('file' !== $class) { + throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class)); + } + + $container->setParameter('profiler.storage.dsn', $config['dsn']); + + if ($this->isConfigEnabled($container, $config['matcher'])) { + if (isset($config['matcher']['service'])) { + $container->setAlias('profiler.request_matcher', $config['matcher']['service'])->setPrivate(true); + } elseif (isset($config['matcher']['ip']) || isset($config['matcher']['path']) || isset($config['matcher']['ips'])) { + $definition = $container->register('profiler.request_matcher', 'Symfony\\Component\\HttpFoundation\\RequestMatcher'); + $definition->setPublic(false); + + if (isset($config['matcher']['ip'])) { + $definition->addMethodCall('matchIp', array($config['matcher']['ip'])); + } + + if (isset($config['matcher']['ips'])) { + $definition->addMethodCall('matchIps', array($config['matcher']['ips'])); + } + + if (isset($config['matcher']['path'])) { + $definition->addMethodCall('matchPath', array($config['matcher']['path'])); + } + } + } + + $container->getDefinition('profiler') + ->addArgument($config['collect']) + ->addTag('kernel.reset', array('method' => 'reset')); + } + + private function registerWorkflowConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$config['enabled']) { + $container->removeDefinition('console.command.workflow_dump'); + + return; + } + + if (!class_exists(Workflow\Workflow::class)) { + throw new LogicException('Workflow support cannot be enabled as the Workflow component is not installed.'); + } + + $loader->load('workflow.xml'); + + $container->getDefinition('workflow.marking_store.multiple_state')->setPrivate(true); + $container->getDefinition('workflow.marking_store.single_state')->setPrivate(true); + $container->getDefinition('workflow.registry')->setPrivate(true); + + $registryDefinition = $container->getDefinition('workflow.registry'); + + foreach ($config['workflows'] as $name => $workflow) { + if (!array_key_exists('type', $workflow)) { + $workflow['type'] = 'workflow'; + @trigger_error(sprintf('The "type" option of the "framework.workflows.%s" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0.', $name), E_USER_DEPRECATED); + } + $type = $workflow['type']; + + $transitions = array(); + foreach ($workflow['transitions'] as $transition) { + if ('workflow' === $type) { + $transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $transition['from'], $transition['to'])); + } elseif ('state_machine' === $type) { + foreach ($transition['from'] as $from) { + foreach ($transition['to'] as $to) { + $transitions[] = new Definition(Workflow\Transition::class, array($transition['name'], $from, $to)); + } + } + } + } + + // Create a Definition + $definitionDefinition = new Definition(Workflow\Definition::class); + $definitionDefinition->setPublic(false); + $definitionDefinition->addArgument($workflow['places']); + $definitionDefinition->addArgument($transitions); + $definitionDefinition->addTag('workflow.definition', array( + 'name' => $name, + 'type' => $type, + 'marking_store' => isset($workflow['marking_store']['type']) ? $workflow['marking_store']['type'] : null, + )); + if (isset($workflow['initial_place'])) { + $definitionDefinition->addArgument($workflow['initial_place']); + } + + // Create MarkingStore + if (isset($workflow['marking_store']['type'])) { + $markingStoreDefinition = new ChildDefinition('workflow.marking_store.'.$workflow['marking_store']['type']); + foreach ($workflow['marking_store']['arguments'] as $argument) { + $markingStoreDefinition->addArgument($argument); + } + } elseif (isset($workflow['marking_store']['service'])) { + $markingStoreDefinition = new Reference($workflow['marking_store']['service']); + } + + // Create Workflow + $workflowId = sprintf('%s.%s', $type, $name); + $workflowDefinition = new ChildDefinition(sprintf('%s.abstract', $type)); + $workflowDefinition->replaceArgument(0, new Reference(sprintf('%s.definition', $workflowId))); + if (isset($markingStoreDefinition)) { + $workflowDefinition->replaceArgument(1, $markingStoreDefinition); + } + $workflowDefinition->replaceArgument(3, $name); + + // Store to container + $container->setDefinition($workflowId, $workflowDefinition); + $container->setDefinition(sprintf('%s.definition', $workflowId), $definitionDefinition); + + // Add workflow to Registry + if ($workflow['supports']) { + foreach ($workflow['supports'] as $supportedClassName) { + $strategyDefinition = new Definition(Workflow\SupportStrategy\ClassInstanceSupportStrategy::class, array($supportedClassName)); + $strategyDefinition->setPublic(false); + $registryDefinition->addMethodCall('add', array(new Reference($workflowId), $strategyDefinition)); + } + } elseif (isset($workflow['support_strategy'])) { + $registryDefinition->addMethodCall('add', array(new Reference($workflowId), new Reference($workflow['support_strategy']))); + } + + // Enable the AuditTrail + if ($workflow['audit_trail']['enabled']) { + $listener = new Definition(Workflow\EventListener\AuditTrailListener::class); + $listener->setPrivate(true); + $listener->addTag('monolog.logger', array('channel' => 'workflow')); + $listener->addTag('kernel.event_listener', array('event' => sprintf('workflow.%s.leave', $name), 'method' => 'onLeave')); + $listener->addTag('kernel.event_listener', array('event' => sprintf('workflow.%s.transition', $name), 'method' => 'onTransition')); + $listener->addTag('kernel.event_listener', array('event' => sprintf('workflow.%s.enter', $name), 'method' => 'onEnter')); + $listener->addArgument(new Reference('logger')); + $container->setDefinition(sprintf('%s.listener.audit_trail', $workflowId), $listener); + } + + // Add Guard Listener + $guard = new Definition(Workflow\EventListener\GuardListener::class); + $guard->setPrivate(true); + $configuration = array(); + foreach ($workflow['transitions'] as $config) { + $transitionName = $config['name']; + + if (!isset($config['guard'])) { + continue; + } + + if (!class_exists(ExpressionLanguage::class)) { + throw new LogicException('Cannot guard workflows as the ExpressionLanguage component is not installed.'); + } + + if (!class_exists(Security::class)) { + throw new LogicException('Cannot guard workflows as the Security component is not installed.'); + } + + $eventName = sprintf('workflow.%s.guard.%s', $name, $transitionName); + $guard->addTag('kernel.event_listener', array('event' => $eventName, 'method' => 'onTransition')); + $configuration[$eventName] = $config['guard']; + } + if ($configuration) { + $guard->setArguments(array( + $configuration, + new Reference('workflow.security.expression_language'), + new Reference('security.token_storage'), + new Reference('security.authorization_checker'), + new Reference('security.authentication.trust_resolver'), + new Reference('security.role_hierarchy'), + new Reference('validator', ContainerInterface::NULL_ON_INVALID_REFERENCE), + )); + + $container->setDefinition(sprintf('%s.listener.guard', $workflowId), $guard); + $container->setParameter('workflow.has_guard_listeners', true); + } + } + } + + private function registerDebugConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('debug_prod.xml'); + + $container->getDefinition('debug.debug_handlers_listener')->setPrivate(true); + + if (class_exists(Stopwatch::class)) { + $container->register('debug.stopwatch', Stopwatch::class) + ->addArgument(true) + ->setPrivate(true) + ->addTag('kernel.reset', array('method' => 'reset')); + $container->setAlias(Stopwatch::class, new Alias('debug.stopwatch', false)); + } + + $debug = $container->getParameter('kernel.debug'); + + if ($debug) { + $container->setParameter('debug.container.dump', '%kernel.cache_dir%/%kernel.container_class%.xml'); + } + + if ($debug && class_exists(Stopwatch::class)) { + $loader->load('debug.xml'); + $container->getDefinition('debug.event_dispatcher')->setPrivate(true); + $container->getDefinition('debug.controller_resolver')->setPrivate(true); + $container->getDefinition('debug.argument_resolver')->setPrivate(true); + } + + $definition = $container->findDefinition('debug.debug_handlers_listener'); + + if (!$config['log']) { + $definition->replaceArgument(1, null); + } + + if (!$config['throw']) { + $container->setParameter('debug.error_handler.throw_at', 0); + } + + $definition->replaceArgument(4, $debug); + $definition->replaceArgument(6, $debug); + + if ($debug && class_exists(DebugProcessor::class)) { + $definition = new Definition(DebugProcessor::class); + $definition->setPublic(false); + $container->setDefinition('debug.log_processor', $definition); + } + } + + private function registerRouterConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + $container->removeDefinition('console.command.router_debug'); + $container->removeDefinition('console.command.router_match'); + + return; + } + + $loader->load('routing.xml'); + + $container->getDefinition('router_listener')->setPrivate(true); + + $container->setParameter('router.resource', $config['resource']); + $container->setParameter('router.cache_class_prefix', $container->getParameter('kernel.container_class')); + $router = $container->findDefinition('router.default'); + $argument = $router->getArgument(2); + $argument['strict_requirements'] = $config['strict_requirements']; + if (isset($config['type'])) { + $argument['resource_type'] = $config['type']; + } + $router->replaceArgument(2, $argument); + + $container->setParameter('request_listener.http_port', $config['http_port']); + $container->setParameter('request_listener.https_port', $config['https_port']); + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'Symfony\\Component\\Routing\\RequestContext', + 'Symfony\\Component\\Routing\\Router', + 'Symfony\\Bundle\\FrameworkBundle\\Routing\\RedirectableUrlMatcher', + $container->findDefinition('router.default')->getClass(), + )); + } + + if ($this->annotationsConfigEnabled) { + $container->register('routing.loader.annotation', AnnotatedRouteControllerLoader::class) + ->setPublic(false) + ->addTag('routing.loader', array('priority' => -10)) + ->addArgument(new Reference('annotation_reader')); + + $container->register('routing.loader.annotation.directory', AnnotationDirectoryLoader::class) + ->setPublic(false) + ->addTag('routing.loader', array('priority' => -10)) + ->setArguments(array( + new Reference('file_locator'), + new Reference('routing.loader.annotation'), + )); + + $container->register('routing.loader.annotation.file', AnnotationFileLoader::class) + ->setPublic(false) + ->addTag('routing.loader', array('priority' => -10)) + ->setArguments(array( + new Reference('file_locator'), + new Reference('routing.loader.annotation'), + )); + } + } + + private function registerSessionConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('session.xml'); + + $container->getDefinition('session.storage.native')->setPrivate(true); + $container->getDefinition('session.storage.php_bridge')->setPrivate(true); + $container->getDefinition('session_listener')->setPrivate(true); + $container->getDefinition('session.save_listener')->setPrivate(true); + $container->getAlias('session.storage.filesystem')->setPrivate(true); + + // session storage + $container->setAlias('session.storage', $config['storage_id'])->setPrivate(true); + $options = array('cache_limiter' => '0'); + foreach (array('name', 'cookie_lifetime', 'cookie_path', 'cookie_domain', 'cookie_secure', 'cookie_httponly', 'use_cookies', 'gc_maxlifetime', 'gc_probability', 'gc_divisor', 'use_strict_mode') as $key) { + if (isset($config[$key])) { + $options[$key] = $config[$key]; + } + } + + $container->setParameter('session.storage.options', $options); + + // session handler (the internal callback registered with PHP session management) + if (null === $config['handler_id']) { + // Set the handler class to be null + $container->getDefinition('session.storage.native')->replaceArgument(1, null); + $container->getDefinition('session.storage.php_bridge')->replaceArgument(0, null); + } else { + $container->setAlias('session.handler', $config['handler_id'])->setPrivate(true); + } + + $container->setParameter('session.save_path', $config['save_path']); + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\HttpKernel\\EventListener\\SessionListener', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\AbstractProxy', + 'Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy', + $container->getDefinition('session')->getClass(), + )); + + if ($container->hasDefinition($config['storage_id'])) { + $this->addClassesToCompile(array( + $container->findDefinition('session.storage')->getClass(), + )); + } + } + + $container->setParameter('session.metadata.update_threshold', $config['metadata_update_threshold']); + } + + private function registerRequestConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if ($config['formats']) { + $loader->load('request.xml'); + + $container->getDefinition('request.add_request_formats_listener')->setPrivate(true); + + $container + ->getDefinition('request.add_request_formats_listener') + ->replaceArgument(0, $config['formats']) + ; + } + } + + private function registerTemplatingConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('templating.xml'); + + $container->getDefinition('templating.name_parser')->setPrivate(true); + $container->getDefinition('templating.filename_parser')->setPrivate(true); + + $container->setParameter('fragment.renderer.hinclude.global_template', $config['hinclude_default_template']); + + if ($container->getParameter('kernel.debug')) { + $logger = new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE); + + $container->getDefinition('templating.loader.cache') + ->addTag('monolog.logger', array('channel' => 'templating')) + ->addMethodCall('setLogger', array($logger)); + $container->getDefinition('templating.loader.chain') + ->addTag('monolog.logger', array('channel' => 'templating')) + ->addMethodCall('setLogger', array($logger)); + } + + if (!empty($config['loaders'])) { + $loaders = array_map(function ($loader) { return new Reference($loader); }, $config['loaders']); + + // Use a delegation unless only a single loader was registered + if (1 === \count($loaders)) { + $container->setAlias('templating.loader', (string) reset($loaders))->setPrivate(true); + } else { + $container->getDefinition('templating.loader.chain')->addArgument($loaders); + $container->setAlias('templating.loader', 'templating.loader.chain')->setPrivate(true); + } + } + + $container->setParameter('templating.loader.cache.path', null); + if (isset($config['cache'])) { + // Wrap the existing loader with cache (must happen after loaders are registered) + $container->setDefinition('templating.loader.wrapped', $container->findDefinition('templating.loader')); + $loaderCache = $container->getDefinition('templating.loader.cache'); + $container->setParameter('templating.loader.cache.path', $config['cache']); + + $container->setDefinition('templating.loader', $loaderCache); + } + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateReference', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\TemplateNameParser', + $container->findDefinition('templating.locator')->getClass(), + )); + } + + $container->setParameter('templating.engines', $config['engines']); + $engines = array_map(function ($engine) { return new Reference('templating.engine.'.$engine); }, $config['engines']); + + // Use a delegation unless only a single engine was registered + if (1 === \count($engines)) { + $container->setAlias('templating', (string) reset($engines))->setPublic(true); + } else { + $templateEngineDefinition = $container->getDefinition('templating.engine.delegating'); + foreach ($engines as $engine) { + $templateEngineDefinition->addMethodCall('addEngine', array($engine)); + } + $container->setAlias('templating', 'templating.engine.delegating')->setPublic(true); + } + + $container->getDefinition('fragment.renderer.hinclude') + ->addTag('kernel.fragment_renderer', array('alias' => 'hinclude')) + ->replaceArgument(0, new Reference('templating')) + ; + + // configure the PHP engine if needed + if (\in_array('php', $config['engines'], true)) { + $loader->load('templating_php.xml'); + + $container->getDefinition('templating.helper.slots')->setPrivate(true); + $container->getDefinition('templating.helper.request')->setPrivate(true); + $container->getDefinition('templating.helper.session')->setPrivate(true); + $container->getDefinition('templating.helper.router')->setPrivate(true); + $container->getDefinition('templating.helper.assets')->setPrivate(true); + $container->getDefinition('templating.helper.actions')->setPrivate(true); + $container->getDefinition('templating.helper.code')->setPrivate(true); + $container->getDefinition('templating.helper.translator')->setPrivate(true); + $container->getDefinition('templating.helper.form')->setPrivate(true); + $container->getDefinition('templating.helper.stopwatch')->setPrivate(true); + $container->getDefinition('templating.globals')->setPrivate(true); + + $container->setParameter('templating.helper.form.resources', $config['form']['resources']); + + if ($container->getParameter('kernel.debug') && class_exists(Stopwatch::class)) { + $loader->load('templating_debug.xml'); + + $container->setDefinition('templating.engine.php', $container->findDefinition('debug.templating.engine.php')); + $container->setAlias('debug.templating.engine.php', 'templating.engine.php')->setPrivate(true); + } + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\\Component\\Templating\\Storage\\FileStorage', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', + 'Symfony\\Bundle\\FrameworkBundle\\Templating\\Loader\\FilesystemLoader', + )); + } + + if ($container->has('assets.packages')) { + $container->getDefinition('templating.helper.assets')->replaceArgument(0, new Reference('assets.packages')); + } else { + $container->removeDefinition('templating.helper.assets'); + } + + if (!$this->translationConfigEnabled) { + $container->removeDefinition('templating.helper.translator'); + } + } + } + + private function registerAssetsConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('assets.xml'); + + $container->getDefinition('assets.packages')->setPrivate(true); + $container->getDefinition('assets.context')->setPrivate(true); + $container->getDefinition('assets.path_package')->setPrivate(true); + $container->getDefinition('assets.url_package')->setPrivate(true); + $container->getDefinition('assets.static_version_strategy')->setPrivate(true); + + $defaultVersion = null; + + if ($config['version_strategy']) { + $defaultVersion = new Reference($config['version_strategy']); + } else { + $defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], '_default'); + } + + $defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion); + $container->setDefinition('assets._default_package', $defaultPackage); + + $namedPackages = array(); + foreach ($config['packages'] as $name => $package) { + if (null !== $package['version_strategy']) { + $version = new Reference($package['version_strategy']); + } elseif (!array_key_exists('version', $package) && null === $package['json_manifest_path']) { + // if neither version nor json_manifest_path are specified, use the default + $version = $defaultVersion; + } else { + // let format fallback to main version_format + $format = $package['version_format'] ?: $config['version_format']; + $version = isset($package['version']) ? $package['version'] : null; + $version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $name); + } + + $container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version)); + $namedPackages[$name] = new Reference('assets._package_'.$name); + } + + $container->getDefinition('assets.packages') + ->replaceArgument(0, new Reference('assets._default_package')) + ->replaceArgument(1, $namedPackages) + ; + } + + /** + * Returns a definition for an asset package. + */ + private function createPackageDefinition($basePath, array $baseUrls, Reference $version) + { + if ($basePath && $baseUrls) { + throw new \LogicException('An asset package cannot have base URLs and base paths.'); + } + + $package = new ChildDefinition($baseUrls ? 'assets.url_package' : 'assets.path_package'); + $package + ->setPublic(false) + ->replaceArgument(0, $baseUrls ?: $basePath) + ->replaceArgument(1, $version) + ; + + return $package; + } + + private function createVersion(ContainerBuilder $container, $version, $format, $jsonManifestPath, $name) + { + // Configuration prevents $version and $jsonManifestPath from being set + if (null !== $version) { + $def = new ChildDefinition('assets.static_version_strategy'); + $def + ->replaceArgument(0, $version) + ->replaceArgument(1, $format) + ; + $container->setDefinition('assets._version_'.$name, $def); + + return new Reference('assets._version_'.$name); + } + + if (null !== $jsonManifestPath) { + $def = new ChildDefinition('assets.json_manifest_version_strategy'); + $def->replaceArgument(0, $jsonManifestPath); + $container->setDefinition('assets._version_'.$name, $def); + + return new Reference('assets._version_'.$name); + } + + return new Reference('assets.empty_version_strategy'); + } + + private function registerTranslatorConfiguration(array $config, ContainerBuilder $container, LoaderInterface $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + $container->removeDefinition('console.command.translation_debug'); + $container->removeDefinition('console.command.translation_update'); + + return; + } + + $loader->load('translation.xml'); + + $container->getDefinition('translator.default')->setPrivate(true); + $container->getDefinition('translation.loader.php')->setPrivate(true); + $container->getDefinition('translation.loader.yml')->setPrivate(true); + $container->getDefinition('translation.loader.xliff')->setPrivate(true); + $container->getDefinition('translation.loader.po')->setPrivate(true); + $container->getDefinition('translation.loader.mo')->setPrivate(true); + $container->getDefinition('translation.loader.qt')->setPrivate(true); + $container->getDefinition('translation.loader.csv')->setPrivate(true); + $container->getDefinition('translation.loader.res')->setPrivate(true); + $container->getDefinition('translation.loader.dat')->setPrivate(true); + $container->getDefinition('translation.loader.ini')->setPrivate(true); + $container->getDefinition('translation.loader.json')->setPrivate(true); + $container->getDefinition('translation.dumper.php')->setPrivate(true); + $container->getDefinition('translation.dumper.xliff')->setPrivate(true); + $container->getDefinition('translation.dumper.po')->setPrivate(true); + $container->getDefinition('translation.dumper.mo')->setPrivate(true); + $container->getDefinition('translation.dumper.yml')->setPrivate(true); + $container->getDefinition('translation.dumper.qt')->setPrivate(true); + $container->getDefinition('translation.dumper.csv')->setPrivate(true); + $container->getDefinition('translation.dumper.ini')->setPrivate(true); + $container->getDefinition('translation.dumper.json')->setPrivate(true); + $container->getDefinition('translation.dumper.res')->setPrivate(true); + $container->getDefinition('translation.extractor.php')->setPrivate(true); + $container->getDefinition('translator_listener')->setPrivate(true); + $container->getDefinition('translation.loader')->setPrivate(true); + $container->getDefinition('translation.reader')->setPrivate(true); + $container->getDefinition('translation.extractor')->setPrivate(true); + $container->getDefinition('translation.writer')->setPrivate(true); + + // Use the "real" translator instead of the identity default + $container->setAlias('translator', 'translator.default')->setPublic(true); + $container->setAlias('translator.formatter', new Alias($config['formatter'], false)); + $translator = $container->findDefinition('translator.default'); + $translator->addMethodCall('setFallbackLocales', array($config['fallbacks'])); + + $container->setParameter('translator.logging', $config['logging']); + $container->setParameter('translator.default_path', $config['default_path']); + + // Discover translation directories + $dirs = array(); + if (class_exists('Symfony\Component\Validator\Validation')) { + $r = new \ReflectionClass('Symfony\Component\Validator\Validation'); + + $dirs[] = \dirname($r->getFileName()).'/Resources/translations'; + } + if (class_exists('Symfony\Component\Form\Form')) { + $r = new \ReflectionClass('Symfony\Component\Form\Form'); + + $dirs[] = \dirname($r->getFileName()).'/Resources/translations'; + } + if (class_exists('Symfony\Component\Security\Core\Exception\AuthenticationException')) { + $r = new \ReflectionClass('Symfony\Component\Security\Core\Exception\AuthenticationException'); + + $dirs[] = \dirname(\dirname($r->getFileName())).'/Resources/translations'; + } + $defaultDir = $container->getParameterBag()->resolveValue($config['default_path']); + $rootDir = $container->getParameter('kernel.root_dir'); + foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) { + if ($container->fileExists($dir = $bundle['path'].'/Resources/translations')) { + $dirs[] = $dir; + } + if ($container->fileExists($dir = $rootDir.sprintf('/Resources/%s/translations', $name))) { + $dirs[] = $dir; + } + } + + foreach ($config['paths'] as $dir) { + if ($container->fileExists($dir)) { + $dirs[] = $dir; + } else { + throw new \UnexpectedValueException(sprintf('%s defined in translator.paths does not exist or is not a directory', $dir)); + } + } + + if ($container->fileExists($defaultDir)) { + $dirs[] = $defaultDir; + } + if ($container->fileExists($dir = $rootDir.'/Resources/translations')) { + $dirs[] = $dir; + } + + // Register translation resources + if ($dirs) { + $files = array(); + $finder = Finder::create() + ->followLinks() + ->files() + ->filter(function (\SplFileInfo $file) { + return 2 === substr_count($file->getBasename(), '.') && preg_match('/\.\w+$/', $file->getBasename()); + }) + ->in($dirs) + ->sortByName() + ; + + foreach ($finder as $file) { + list(, $locale) = explode('.', $file->getBasename(), 3); + if (!isset($files[$locale])) { + $files[$locale] = array(); + } + + $files[$locale][] = (string) $file; + } + + $options = array_merge( + $translator->getArgument(4), + array('resource_files' => $files) + ); + + $translator->replaceArgument(4, $options); + } + } + + private function registerValidationConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->validatorConfigEnabled = $this->isConfigEnabled($container, $config)) { + return; + } + + if (!class_exists('Symfony\Component\Validator\Validation')) { + throw new LogicException('Validation support cannot be enabled as the Validator component is not installed.'); + } + + $loader->load('validator.xml'); + + $container->getDefinition('validator.builder')->setPrivate(true); + $container->getDefinition('validator.expression')->setPrivate(true); + $container->getDefinition('validator.email')->setPrivate(true); + + $validatorBuilder = $container->getDefinition('validator.builder'); + + $container->setParameter('validator.translation_domain', $config['translation_domain']); + + $files = array('xml' => array(), 'yml' => array()); + $this->registerValidatorMapping($container, $config, $files); + + if (!empty($files['xml'])) { + $validatorBuilder->addMethodCall('addXmlMappings', array($files['xml'])); + } + + if (!empty($files['yml'])) { + $validatorBuilder->addMethodCall('addYamlMappings', array($files['yml'])); + } + + $definition = $container->findDefinition('validator.email'); + $definition->replaceArgument(0, $config['strict_email']); + + if (array_key_exists('enable_annotations', $config) && $config['enable_annotations']) { + if (!$this->annotationsConfigEnabled) { + throw new \LogicException('"enable_annotations" on the validator cannot be set as Annotations support is disabled.'); + } + + $validatorBuilder->addMethodCall('enableAnnotationMapping', array(new Reference('annotation_reader'))); + } + + if (array_key_exists('static_method', $config) && $config['static_method']) { + foreach ($config['static_method'] as $methodName) { + $validatorBuilder->addMethodCall('addMethodMapping', array($methodName)); + } + } + + if (isset($config['cache']) && $config['cache']) { + $container->setParameter( + 'validator.mapping.cache.prefix', + 'validator_'.$this->getKernelRootHash($container) + ); + + $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference($config['cache']))); + } elseif (!$container->getParameter('kernel.debug')) { + $validatorBuilder->addMethodCall('setMetadataCache', array(new Reference('validator.mapping.cache.symfony'))); + } + } + + private function registerValidatorMapping(ContainerBuilder $container, array $config, array &$files) + { + $fileRecorder = function ($extension, $path) use (&$files) { + $files['yaml' === $extension ? 'yml' : $extension][] = $path; + }; + + if (interface_exists('Symfony\Component\Form\FormInterface')) { + $reflClass = new \ReflectionClass('Symfony\Component\Form\FormInterface'); + $fileRecorder('xml', \dirname($reflClass->getFileName()).'/Resources/config/validation.xml'); + } + + foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { + $dirname = $bundle['path']; + + if ( + $container->fileExists($file = $dirname.'/Resources/config/validation.yaml', false) || + $container->fileExists($file = $dirname.'/Resources/config/validation.yml', false) + ) { + $fileRecorder('yml', $file); + } + + if ($container->fileExists($file = $dirname.'/Resources/config/validation.xml', false)) { + $fileRecorder('xml', $file); + } + + if ($container->fileExists($dir = $dirname.'/Resources/config/validation', '/^$/')) { + $this->registerMappingFilesFromDir($dir, $fileRecorder); + } + } + + $projectDir = $container->getParameter('kernel.project_dir'); + if ($container->fileExists($dir = $projectDir.'/config/validator', '/^$/')) { + $this->registerMappingFilesFromDir($dir, $fileRecorder); + } + + $this->registerMappingFilesFromConfig($container, $config, $fileRecorder); + } + + private function registerMappingFilesFromDir($dir, callable $fileRecorder) + { + foreach (Finder::create()->followLinks()->files()->in($dir)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) { + $fileRecorder($file->getExtension(), $file->getRealPath()); + } + } + + private function registerMappingFilesFromConfig(ContainerBuilder $container, array $config, callable $fileRecorder) + { + foreach ($config['mapping']['paths'] as $path) { + if (is_dir($path)) { + $this->registerMappingFilesFromDir($path, $fileRecorder); + $container->addResource(new DirectoryResource($path, '/^$/')); + } elseif ($container->fileExists($path, false)) { + if (!preg_match('/\.(xml|ya?ml)$/', $path, $matches)) { + throw new \RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path)); + } + $fileRecorder($matches[1], $path); + } else { + throw new \RuntimeException(sprintf('Could not open file or directory "%s".', $path)); + } + } + } + + private function registerAnnotationsConfiguration(array $config, ContainerBuilder $container, $loader) + { + if (!$this->annotationsConfigEnabled) { + return; + } + + if (!class_exists('Doctrine\Common\Annotations\Annotation')) { + throw new LogicException('Annotations cannot be enabled as the Doctrine Annotation library is not installed.'); + } + + $loader->load('annotations.xml'); + + $container->getAlias('annotation_reader')->setPrivate(true); + + if (!method_exists(AnnotationRegistry::class, 'registerUniqueLoader')) { + $container->getDefinition('annotations.dummy_registry') + ->setMethodCalls(array(array('registerLoader', array('class_exists')))); + } + + if ('none' !== $config['cache']) { + if (!class_exists('Doctrine\Common\Cache\CacheProvider')) { + throw new LogicException('Annotations cannot be enabled as the Doctrine Cache library is not installed.'); + } + + $cacheService = $config['cache']; + + if ('php_array' === $config['cache']) { + $cacheService = 'annotations.cache'; + + // Enable warmer only if PHP array is used for cache + $definition = $container->findDefinition('annotations.cache_warmer'); + $definition->addTag('kernel.cache_warmer'); + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\Component\Cache\Adapter\PhpArrayAdapter', + 'Symfony\Component\Cache\DoctrineProvider', + )); + } + } elseif ('file' === $config['cache']) { + $cacheDir = $container->getParameterBag()->resolveValue($config['file_cache_dir']); + + if (!is_dir($cacheDir) && false === @mkdir($cacheDir, 0777, true) && !is_dir($cacheDir)) { + throw new \RuntimeException(sprintf('Could not create cache directory "%s".', $cacheDir)); + } + + $container + ->getDefinition('annotations.filesystem_cache') + ->replaceArgument(0, $cacheDir) + ; + + $cacheService = 'annotations.filesystem_cache'; + } + + $container + ->getDefinition('annotations.cached_reader') + ->replaceArgument(2, $config['debug']) + // temporary property to lazy-reference the cache provider without using it until AddAnnotationsCachedReaderPass runs + ->setProperty('cacheProviderBackup', new ServiceClosureArgument(new Reference($cacheService))) + ->addTag('annotations.cached_reader') + ; + + $container->setAlias('annotation_reader', 'annotations.cached_reader')->setPrivate(true); + $container->setAlias(Reader::class, new Alias('annotations.cached_reader', false)); + } else { + $container->removeDefinition('annotations.cached_reader'); + } + } + + private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) { + return; + } + + $loader->load('property_access.xml'); + + $container->getDefinition('property_accessor')->setPrivate(true); + + $container + ->getDefinition('property_accessor') + ->replaceArgument(0, $config['magic_call']) + ->replaceArgument(1, $config['throw_exception_on_invalid_index']) + ; + } + + private function registerSecurityCsrfConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + if (!class_exists('Symfony\Component\Security\Csrf\CsrfToken')) { + throw new LogicException('CSRF support cannot be enabled as the Security CSRF component is not installed. Try running "composer require symfony/security-csrf".'); + } + + if (!$this->sessionConfigEnabled) { + throw new \LogicException('CSRF protection needs sessions to be enabled.'); + } + + // Enable services for CSRF protection (even without forms) + $loader->load('security_csrf.xml'); + } + + private function registerSerializerConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('serializer.xml'); + + if (!class_exists(DateIntervalNormalizer::class)) { + $container->removeDefinition('serializer.normalizer.dateinterval'); + } + + $container->getDefinition('serializer.mapping.cache.symfony')->setPrivate(true); + + $chainLoader = $container->getDefinition('serializer.mapping.chain_loader'); + + if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) { + $container->removeAlias('serializer.property_accessor'); + $container->removeDefinition('serializer.normalizer.object'); + } + + if (!class_exists(Yaml::class)) { + $container->removeDefinition('serializer.encoder.yaml'); + } + + $serializerLoaders = array(); + if (isset($config['enable_annotations']) && $config['enable_annotations']) { + if (!$this->annotationsConfigEnabled) { + throw new \LogicException('"enable_annotations" on the serializer cannot be set as Annotations support is disabled.'); + } + + $annotationLoader = new Definition( + 'Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', + array(new Reference('annotation_reader')) + ); + $annotationLoader->setPublic(false); + + $serializerLoaders[] = $annotationLoader; + } + + $fileRecorder = function ($extension, $path) use (&$serializerLoaders) { + $definition = new Definition(\in_array($extension, array('yaml', 'yml')) ? 'Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader' : 'Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader', array($path)); + $definition->setPublic(false); + $serializerLoaders[] = $definition; + }; + + foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) { + $dirname = $bundle['path']; + + if ($container->fileExists($file = $dirname.'/Resources/config/serialization.xml', false)) { + $fileRecorder('xml', $file); + } + + if ( + $container->fileExists($file = $dirname.'/Resources/config/serialization.yaml', false) || + $container->fileExists($file = $dirname.'/Resources/config/serialization.yml', false) + ) { + $fileRecorder('yml', $file); + } + + if ($container->fileExists($dir = $dirname.'/Resources/config/serialization', '/^$/')) { + $this->registerMappingFilesFromDir($dir, $fileRecorder); + } + } + + $projectDir = $container->getParameter('kernel.project_dir'); + if ($container->fileExists($dir = $projectDir.'/config/serializer', '/^$/')) { + $this->registerMappingFilesFromDir($dir, $fileRecorder); + } + + $this->registerMappingFilesFromConfig($container, $config, $fileRecorder); + + $chainLoader->replaceArgument(0, $serializerLoaders); + $container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders); + + if (isset($config['cache']) && $config['cache']) { + $container->setParameter( + 'serializer.mapping.cache.prefix', + 'serializer_'.$this->getKernelRootHash($container) + ); + + $container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument( + 1, new Reference($config['cache']) + ); + } elseif (!$container->getParameter('kernel.debug')) { + $cacheMetadataFactory = new Definition( + CacheClassMetadataFactory::class, + array( + new Reference('serializer.mapping.cache_class_metadata_factory.inner'), + new Reference('serializer.mapping.cache.symfony'), + ) + ); + $cacheMetadataFactory->setPublic(false); + $cacheMetadataFactory->setDecoratedService('serializer.mapping.class_metadata_factory'); + + $container->setDefinition('serializer.mapping.cache_class_metadata_factory', $cacheMetadataFactory); + } + + if (isset($config['name_converter']) && $config['name_converter']) { + $container->getDefinition('serializer.normalizer.object')->replaceArgument(1, new Reference($config['name_converter'])); + } + + if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) { + $container->getDefinition('serializer.normalizer.object')->addMethodCall('setCircularReferenceHandler', array(new Reference($config['circular_reference_handler']))); + } + } + + private function registerPropertyInfoConfiguration(ContainerBuilder $container, XmlFileLoader $loader) + { + if (!interface_exists(PropertyInfoExtractorInterface::class)) { + throw new LogicException('PropertyInfo support cannot be enabled as the PropertyInfo component is not installed. Try running "composer require symfony/property-info".'); + } + + $loader->load('property_info.xml'); + + $container->getDefinition('property_info')->setPrivate(true); + + if (interface_exists('phpDocumentor\Reflection\DocBlockFactoryInterface')) { + $definition = $container->register('property_info.php_doc_extractor', 'Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor'); + $definition->setPrivate(true); + $definition->addTag('property_info.description_extractor', array('priority' => -1000)); + $definition->addTag('property_info.type_extractor', array('priority' => -1001)); + } + } + + private function registerLockConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) + { + $loader->load('lock.xml'); + + foreach ($config['resources'] as $resourceName => $resourceStores) { + if (0 === \count($resourceStores)) { + continue; + } + + // Generate stores + $storeDefinitions = array(); + foreach ($resourceStores as $storeDsn) { + $storeDsn = $container->resolveEnvPlaceholders($storeDsn, null, $usedEnvs); + switch (true) { + case 'flock' === $storeDsn: + $storeDefinition = new Reference('lock.store.flock'); + break; + case 'semaphore' === $storeDsn: + $storeDefinition = new Reference('lock.store.semaphore'); + break; + case $usedEnvs || preg_match('#^[a-z]++://#', $storeDsn): + if (!$container->hasDefinition($connectionDefinitionId = $container->hash($storeDsn))) { + $connectionDefinition = new Definition(\stdClass::class); + $connectionDefinition->setPublic(false); + $connectionDefinition->setFactory(array(AbstractAdapter::class, 'createConnection')); + $connectionDefinition->setArguments(array($storeDsn, array('lazy' => true))); + $container->setDefinition($connectionDefinitionId, $connectionDefinition); + } + + $storeDefinition = new Definition(StoreInterface::class); + $storeDefinition->setPublic(false); + $storeDefinition->setFactory(array(StoreFactory::class, 'createStore')); + $storeDefinition->setArguments(array(new Reference($connectionDefinitionId))); + + $container->setDefinition($storeDefinitionId = 'lock.'.$resourceName.'.store.'.$container->hash($storeDsn), $storeDefinition); + + $storeDefinition = new Reference($storeDefinitionId); + break; + default: + throw new InvalidArgumentException(sprintf('Lock store DSN "%s" is not valid in resource "%s"', $storeDsn, $resourceName)); + } + + $storeDefinitions[] = $storeDefinition; + } + + // Wrap array of stores with CombinedStore + if (\count($storeDefinitions) > 1) { + $combinedDefinition = new ChildDefinition('lock.store.combined.abstract'); + $combinedDefinition->replaceArgument(0, $storeDefinitions); + $container->setDefinition('lock.'.$resourceName.'.store', $combinedDefinition); + } else { + $container->setAlias('lock.'.$resourceName.'.store', new Alias((string) $storeDefinitions[0], false)); + } + + // Generate factories for each resource + $factoryDefinition = new ChildDefinition('lock.factory.abstract'); + $factoryDefinition->replaceArgument(0, new Reference('lock.'.$resourceName.'.store')); + $container->setDefinition('lock.'.$resourceName.'.factory', $factoryDefinition); + + // Generate services for lock instances + $lockDefinition = new Definition(Lock::class); + $lockDefinition->setPublic(false); + $lockDefinition->setFactory(array(new Reference('lock.'.$resourceName.'.factory'), 'createLock')); + $lockDefinition->setArguments(array($resourceName)); + $container->setDefinition('lock.'.$resourceName, $lockDefinition); + + // provide alias for default resource + if ('default' === $resourceName) { + $container->setAlias('lock.store', new Alias('lock.'.$resourceName.'.store', false)); + $container->setAlias('lock.factory', new Alias('lock.'.$resourceName.'.factory', false)); + $container->setAlias('lock', new Alias('lock.'.$resourceName, false)); + $container->setAlias(StoreInterface::class, new Alias('lock.store', false)); + $container->setAlias(Factory::class, new Alias('lock.factory', false)); + $container->setAlias(LockInterface::class, new Alias('lock', false)); + } + } + } + + private function registerCacheConfiguration(array $config, ContainerBuilder $container) + { + $version = new Parameter('container.build_id'); + $container->getDefinition('cache.adapter.apcu')->replaceArgument(2, $version); + $container->getDefinition('cache.adapter.system')->replaceArgument(2, $version); + $container->getDefinition('cache.adapter.filesystem')->replaceArgument(2, $config['directory']); + + if (isset($config['prefix_seed'])) { + $container->setParameter('cache.prefix.seed', $config['prefix_seed']); + } + if ($container->hasParameter('cache.prefix.seed')) { + // Inline any env vars referenced in the parameter + $container->setParameter('cache.prefix.seed', $container->resolveEnvPlaceholders($container->getParameter('cache.prefix.seed'), true)); + } + foreach (array('doctrine', 'psr6', 'redis', 'memcached') as $name) { + if (isset($config[$name = 'default_'.$name.'_provider'])) { + $container->setAlias('cache.'.$name, new Alias(Compiler\CachePoolPass::getServiceProvider($container, $config[$name]), false)); + } + } + foreach (array('app', 'system') as $name) { + $config['pools']['cache.'.$name] = array( + 'adapter' => $config[$name], + 'public' => true, + ); + } + foreach ($config['pools'] as $name => $pool) { + $definition = new ChildDefinition($pool['adapter']); + $definition->setPublic($pool['public']); + unset($pool['adapter'], $pool['public']); + + $definition->addTag('cache.pool', $pool); + $container->setDefinition($name, $definition); + } + + if (method_exists(PropertyAccessor::class, 'createCache')) { + $propertyAccessDefinition = $container->register('cache.property_access', AdapterInterface::class); + $propertyAccessDefinition->setPublic(false); + + if (!$container->getParameter('kernel.debug')) { + $propertyAccessDefinition->setFactory(array(PropertyAccessor::class, 'createCache')); + $propertyAccessDefinition->setArguments(array(null, null, $version, new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))); + $propertyAccessDefinition->addTag('cache.pool', array('clearer' => 'cache.system_clearer')); + $propertyAccessDefinition->addTag('monolog.logger', array('channel' => 'cache')); + } else { + $propertyAccessDefinition->setClass(ArrayAdapter::class); + $propertyAccessDefinition->setArguments(array(0, false)); + } + } + + if (\PHP_VERSION_ID < 70000) { + $this->addClassesToCompile(array( + 'Symfony\Component\Cache\Adapter\ApcuAdapter', + 'Symfony\Component\Cache\Adapter\FilesystemAdapter', + 'Symfony\Component\Cache\CacheItem', + )); + } + } + + /** + * Gets a hash of the kernel root directory. + * + * @return string + */ + private function getKernelRootHash(ContainerBuilder $container) + { + if (!$this->kernelRootHash) { + $this->kernelRootHash = hash('sha256', $container->getParameter('kernel.root_dir')); + } + + return $this->kernelRootHash; + } + + /** + * Returns the base path for the XSD files. + * + * @return string The XSD base path + */ + public function getXsdValidationBasePath() + { + return \dirname(__DIR__).'/Resources/config/schema'; + } + + public function getNamespace() + { + return 'http://symfony.com/schema/dic/symfony'; + } +} diff --git a/vendor/symfony/framework-bundle/EventListener/ResolveControllerNameSubscriber.php b/vendor/symfony/framework-bundle/EventListener/ResolveControllerNameSubscriber.php new file mode 100644 index 0000000..5fb6c3b --- /dev/null +++ b/vendor/symfony/framework-bundle/EventListener/ResolveControllerNameSubscriber.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\Bundle\FrameworkBundle\EventListener; + +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Guarantees that the _controller key is parsed into its final format. + * + * @author Ryan Weaver + */ +class ResolveControllerNameSubscriber implements EventSubscriberInterface +{ + private $parser; + + public function __construct(ControllerNameParser $parser) + { + $this->parser = $parser; + } + + public function onKernelRequest(GetResponseEvent $event) + { + $controller = $event->getRequest()->attributes->get('_controller'); + if (\is_string($controller) && false === strpos($controller, '::') && 2 === substr_count($controller, ':')) { + // controller in the a:b:c notation then + $event->getRequest()->attributes->set('_controller', $this->parser->parse($controller)); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 24), + ); + } +} diff --git a/vendor/symfony/framework-bundle/EventListener/SessionListener.php b/vendor/symfony/framework-bundle/EventListener/SessionListener.php new file mode 100644 index 0000000..aa5b4ba --- /dev/null +++ b/vendor/symfony/framework-bundle/EventListener/SessionListener.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\Bundle\FrameworkBundle\EventListener; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\SessionListener instead.', SessionListener::class), E_USER_DEPRECATED); + +/** + * Sets the session in the request. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\SessionListener instead + */ +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/vendor/symfony/framework-bundle/EventListener/TestSessionListener.php b/vendor/symfony/framework-bundle/EventListener/TestSessionListener.php new file mode 100644 index 0000000..55356be --- /dev/null +++ b/vendor/symfony/framework-bundle/EventListener/TestSessionListener.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\Bundle\FrameworkBundle\EventListener; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use Symfony\Component\HttpKernel\EventListener\TestSessionListener instead.', TestSessionListener::class), E_USER_DEPRECATED); + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\EventListener\AbstractTestSessionListener; + +/** + * TestSessionListener. + * + * @author Fabien Potencier + * + * @deprecated since version 3.3, to be removed in 4.0. + */ +class TestSessionListener extends AbstractTestSessionListener +{ + protected $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/vendor/symfony/framework-bundle/FrameworkBundle.php b/vendor/symfony/framework-bundle/FrameworkBundle.php new file mode 100644 index 0000000..3288c4b --- /dev/null +++ b/vendor/symfony/framework-bundle/FrameworkBundle.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle; + +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddDebugLogProcessorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheCollectorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolClearerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPrunerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ContainerBuilderDebugDumpPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass; +use Symfony\Component\Config\Resource\ClassExistenceResource; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\DependencyInjection\AddConsoleCommandPass; +use Symfony\Component\Debug\ErrorHandler; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; +use Symfony\Component\Form\DependencyInjection\FormPass; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass; +use Symfony\Component\HttpKernel\DependencyInjection\FragmentRendererPass; +use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; +use Symfony\Component\HttpKernel\DependencyInjection\RegisterControllerArgumentLocatorsPass; +use Symfony\Component\HttpKernel\DependencyInjection\RemoveEmptyControllerArgumentLocatorsPass; +use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass; +use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass; +use Symfony\Component\Serializer\DependencyInjection\SerializerPass; +use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass; +use Symfony\Component\Translation\DependencyInjection\TranslationExtractorPass; +use Symfony\Component\Translation\DependencyInjection\TranslatorPass; +use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; +use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass; +use Symfony\Component\Workflow\DependencyInjection\ValidateWorkflowsPass; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FrameworkBundle extends Bundle +{ + public function boot() + { + ErrorHandler::register(null, false)->throwAt($this->container->getParameter('debug.error_handler.throw_at'), true); + + if ($this->container->hasParameter('kernel.trusted_proxies')) { + @trigger_error('The "kernel.trusted_proxies" parameter is deprecated since Symfony 3.3 and will be removed in 4.0. Use the Request::setTrustedProxies() method in your front controller instead.', E_USER_DEPRECATED); + + if ($trustedProxies = $this->container->getParameter('kernel.trusted_proxies')) { + Request::setTrustedProxies($trustedProxies, Request::getTrustedHeaderSet()); + } + } + + if ($this->container->getParameter('kernel.http_method_override')) { + Request::enableHttpMethodParameterOverride(); + } + + if ($trustedHosts = $this->container->getParameter('kernel.trusted_hosts')) { + Request::setTrustedHosts($trustedHosts); + } + } + + public function build(ContainerBuilder $container) + { + parent::build($container); + + $hotPathEvents = array( + KernelEvents::REQUEST, + KernelEvents::CONTROLLER, + KernelEvents::CONTROLLER_ARGUMENTS, + KernelEvents::RESPONSE, + KernelEvents::FINISH_REQUEST, + ); + + $container->addCompilerPass(new LoggerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); + $container->addCompilerPass(new RegisterControllerArgumentLocatorsPass()); + $container->addCompilerPass(new RemoveEmptyControllerArgumentLocatorsPass(), PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new RoutingResolverPass()); + $container->addCompilerPass(new ProfilerPass()); + // must be registered before removing private services as some might be listeners/subscribers + // but as late as possible to get resolved parameters + $container->addCompilerPass((new RegisterListenersPass())->setHotPathEvents($hotPathEvents), PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new TemplatingPass()); + $this->addCompilerPassIfExists($container, AddConstraintValidatorsPass::class, PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new AddAnnotationsCachedReaderPass(), PassConfig::TYPE_AFTER_REMOVING, -255); + $this->addCompilerPassIfExists($container, AddValidatorInitializersPass::class); + $this->addCompilerPassIfExists($container, AddConsoleCommandPass::class, PassConfig::TYPE_BEFORE_REMOVING); + if (class_exists(TranslatorPass::class)) { + // Arguments to be removed in 4.0, relying on the default values + $container->addCompilerPass(new TranslatorPass('translator.default', 'translation.loader')); + } + $container->addCompilerPass(new LoggingTranslatorPass()); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + $this->addCompilerPassIfExists($container, TranslationExtractorPass::class); + $this->addCompilerPassIfExists($container, TranslationDumperPass::class); + $container->addCompilerPass(new FragmentRendererPass()); + $this->addCompilerPassIfExists($container, SerializerPass::class); + $this->addCompilerPassIfExists($container, PropertyInfoPass::class); + $container->addCompilerPass(new DataCollectorTranslatorPass()); + $container->addCompilerPass(new ControllerArgumentValueResolverPass()); + $container->addCompilerPass(new CachePoolPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, 32); + $this->addCompilerPassIfExists($container, ValidateWorkflowsPass::class); + $container->addCompilerPass(new CachePoolClearerPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING); + $this->addCompilerPassIfExists($container, FormPass::class); + $container->addCompilerPass(new WorkflowGuardListenerPass()); + $container->addCompilerPass(new ResettableServicePass()); + + if ($container->getParameter('kernel.debug')) { + $container->addCompilerPass(new AddDebugLogProcessorPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); + $container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING); + $container->addCompilerPass(new ContainerBuilderDebugDumpPass(), PassConfig::TYPE_BEFORE_REMOVING, -255); + $container->addCompilerPass(new CacheCollectorPass(), PassConfig::TYPE_BEFORE_REMOVING); + } + } + + private function addCompilerPassIfExists(ContainerBuilder $container, $class, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION, $priority = 0) + { + $container->addResource(new ClassExistenceResource($class)); + + if (class_exists($class)) { + $container->addCompilerPass(new $class(), $type, $priority); + } + } + + public function registerCommands(Application $application) + { + // noop + } +} diff --git a/vendor/symfony/framework-bundle/HttpCache/HttpCache.php b/vendor/symfony/framework-bundle/HttpCache/HttpCache.php new file mode 100644 index 0000000..d9bd456 --- /dev/null +++ b/vendor/symfony/framework-bundle/HttpCache/HttpCache.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\Bundle\FrameworkBundle\HttpCache; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpCache\HttpCache as BaseHttpCache; +use Symfony\Component\HttpKernel\HttpCache\Store; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * Manages HTTP cache objects in a Container. + * + * @author Fabien Potencier + */ +abstract class HttpCache extends BaseHttpCache +{ + protected $cacheDir; + protected $kernel; + + /** + * @param KernelInterface $kernel A KernelInterface instance + * @param string $cacheDir The cache directory (default used if null) + */ + public function __construct(KernelInterface $kernel, $cacheDir = null) + { + $this->kernel = $kernel; + $this->cacheDir = $cacheDir; + + parent::__construct($kernel, $this->createStore(), $this->createSurrogate(), array_merge(array('debug' => $kernel->isDebug()), $this->getOptions())); + } + + /** + * Forwards the Request to the backend and returns the Response. + * + * @param Request $request A Request instance + * @param bool $raw 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, $raw = false, Response $entry = null) + { + $this->getKernel()->boot(); + $this->getKernel()->getContainer()->set('cache', $this); + + return parent::forward($request, $raw, $entry); + } + + /** + * Returns an array of options to customize the Cache configuration. + * + * @return array An array of options + */ + protected function getOptions() + { + return array(); + } + + protected function createSurrogate() + { + return new Esi(); + } + + protected function createStore() + { + return new Store($this->cacheDir ?: $this->kernel->getCacheDir().'/http_cache'); + } +} diff --git a/vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php b/vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.php new file mode 100644 index 0000000..8134589 --- /dev/null +++ b/vendor/symfony/framework-bundle/Kernel/MicroKernelTrait.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\Bundle\FrameworkBundle\Kernel; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Routing\RouteCollectionBuilder; + +/** + * A Kernel that provides configuration hooks. + * + * @author Ryan Weaver + * @author Fabien Potencier + */ +trait MicroKernelTrait +{ + /** + * Add or import routes into your application. + * + * $routes->import('config/routing.yml'); + * $routes->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); + * + * @param RouteCollectionBuilder $routes + */ + abstract protected function configureRoutes(RouteCollectionBuilder $routes); + + /** + * Configures the container. + * + * You can register extensions: + * + * $c->loadFromExtension('framework', array( + * 'secret' => '%secret%' + * )); + * + * Or services: + * + * $c->register('halloween', 'FooBundle\HalloweenProvider'); + * + * Or parameters: + * + * $c->setParameter('halloween', 'lot of fun'); + * + * @param ContainerBuilder $c + * @param LoaderInterface $loader + */ + abstract protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader); + + /** + * {@inheritdoc} + */ + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(function (ContainerBuilder $container) use ($loader) { + $container->loadFromExtension('framework', array( + 'router' => array( + 'resource' => 'kernel:loadRoutes', + 'type' => 'service', + ), + )); + + if ($this instanceof EventSubscriberInterface) { + $container->register('kernel', static::class) + ->setSynthetic(true) + ->setPublic(true) + ->addTag('kernel.event_subscriber') + ; + } + + $this->configureContainer($container, $loader); + + $container->addObjectResource($this); + }); + } + + /** + * @internal + */ + public function loadRoutes(LoaderInterface $loader) + { + $routes = new RouteCollectionBuilder($loader); + $this->configureRoutes($routes); + + return $routes->build(); + } +} diff --git a/vendor/symfony/framework-bundle/LICENSE b/vendor/symfony/framework-bundle/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/framework-bundle/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/vendor/symfony/framework-bundle/README.md b/vendor/symfony/framework-bundle/README.md new file mode 100644 index 0000000..9280d87 --- /dev/null +++ b/vendor/symfony/framework-bundle/README.md @@ -0,0 +1,10 @@ +FrameworkBundle +=============== + +Resources +--------- + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/framework-bundle/Resources/config/annotations.xml b/vendor/symfony/framework-bundle/Resources/config/annotations.xml new file mode 100644 index 0000000..2b4ea42 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/annotations.xml @@ -0,0 +1,57 @@ + + + + + + + + + + required + + + + + + + + class_exists + + + + + + + + + + + + + + + + + + %kernel.cache_dir%/annotations.php + + #^Symfony\\(?:Component\\HttpKernel\\|Bundle\\FrameworkBundle\\Controller\\(?!AbstractController$|Controller$))# + %kernel.debug% + + + + + + + %kernel.cache_dir%/annotations.php + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/assets.xml b/vendor/symfony/framework-bundle/Resources/config/assets.xml new file mode 100644 index 0000000..fd95fbf --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/assets.xml @@ -0,0 +1,54 @@ + + + + + + + false + + + + + + + + + + + + + + + + + + %asset.request_context.base_path% + %asset.request_context.secure% + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/cache.xml b/vendor/symfony/framework-bundle/Resources/config/cache.xml new file mode 100644 index 0000000..f388501 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/cache.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + %kernel.cache_dir%/pools + + + + + + + + 0 + + + + + + + + + + + + 0 + + + + + + + + + + 0 + %kernel.cache_dir%/pools + + + + + + + + + + 0 + + + + + + + + 0 + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/cache_debug.xml b/vendor/symfony/framework-bundle/Resources/config/cache_debug.xml new file mode 100644 index 0000000..1b9b407 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/cache_debug.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/collectors.xml b/vendor/symfony/framework-bundle/Resources/config/collectors.xml new file mode 100644 index 0000000..95d1376 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/collectors.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.cache_dir%/%kernel.container_class% + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/console.xml b/vendor/symfony/framework-bundle/Resources/config/console.xml new file mode 100644 index 0000000..34f47a0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/console.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %translator.default_path% + + + + + + + + + %kernel.default_locale% + %translator.default_path% + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/debug.xml b/vendor/symfony/framework-bundle/Resources/config/debug.xml new file mode 100644 index 0000000..9aa1367 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/debug.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/debug_prod.xml b/vendor/symfony/framework-bundle/Resources/config/debug_prod.xml new file mode 100644 index 0000000..28a8a8a --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/debug_prod.xml @@ -0,0 +1,31 @@ + + + + + + -1 + + + + + + + + + null + + -1 + %debug.error_handler.throw_at% + true + + true + + + + %debug.file_link_format% + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/esi.xml b/vendor/symfony/framework-bundle/Resources/config/esi.xml new file mode 100644 index 0000000..cd9fa06 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/esi.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/form.xml b/vendor/symfony/framework-bundle/Resources/config/form.xml new file mode 100644 index 0000000..211bccb --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/form.xml @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + + + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %validator.translation_domain% + + + + + The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/form_csrf.xml b/vendor/symfony/framework-bundle/Resources/config/form_csrf.xml new file mode 100644 index 0000000..d0162ea --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/form_csrf.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + %form.type_extension.csrf.enabled% + %form.type_extension.csrf.field_name% + + %validator.translation_domain% + + + + + + The service "%service_id%" is internal and deprecated since Symfony 3.3 and will be removed in Symfony 4.0 + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/form_debug.xml b/vendor/symfony/framework-bundle/Resources/config/form_debug.xml new file mode 100644 index 0000000..525b18c --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/form_debug.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/fragment_listener.xml b/vendor/symfony/framework-bundle/Resources/config/fragment_listener.xml new file mode 100644 index 0000000..49fbbba --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/fragment_listener.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + %fragment.path% + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/fragment_renderer.xml b/vendor/symfony/framework-bundle/Resources/config/fragment_renderer.xml new file mode 100644 index 0000000..3f51852 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/fragment_renderer.xml @@ -0,0 +1,51 @@ + + + + + + + /_fragment + + + + + + + + + %kernel.debug% + + + + + + + %fragment.path% + + + + + + %fragment.renderer.hinclude.global_template% + %fragment.path% + + + + + + + + %fragment.path% + + + + + + + + %fragment.path% + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/identity_translator.xml b/vendor/symfony/framework-bundle/Resources/config/identity_translator.xml new file mode 100644 index 0000000..def3c1d --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/identity_translator.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/lock.xml b/vendor/symfony/framework-bundle/Resources/config/lock.xml new file mode 100644 index 0000000..e4c2231 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/lock.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/profiling.xml b/vendor/symfony/framework-bundle/Resources/config/profiling.xml new file mode 100644 index 0000000..635dc8d --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/profiling.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + %profiler.storage.dsn% + + + + + + + + %profiler_listener.only_exceptions% + %profiler_listener.only_master_requests% + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/property_access.xml b/vendor/symfony/framework-bundle/Resources/config/property_access.xml new file mode 100644 index 0000000..91924e5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/property_access.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/property_info.xml b/vendor/symfony/framework-bundle/Resources/config/property_info.xml new file mode 100644 index 0000000..a893127 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/property_info.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/request.xml b/vendor/symfony/framework-bundle/Resources/config/request.xml new file mode 100644 index 0000000..9cb049c --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/request.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/routing.xml b/vendor/symfony/framework-bundle/Resources/config/routing.xml new file mode 100644 index 0000000..1ad1a75 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/routing.xml @@ -0,0 +1,126 @@ + + + + + + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\UrlGenerator + Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher + Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper + %router.cache_class_prefix%UrlMatcher + %router.cache_class_prefix%UrlGenerator + localhost + http + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %router.resource% + + %kernel.cache_dir% + %kernel.debug% + %router.options.generator_class% + %router.options.generator_base_class% + %router.options.generator_dumper_class% + %router.options.generator.cache_class% + %router.options.matcher_class% + %router.options.matcher_base_class% + %router.options.matcher_dumper_class% + %router.options.matcher.cache_class% + + + + + + + + + + + + + + + %router.request_context.base_url% + GET + %router.request_context.host% + %router.request_context.scheme% + %request_listener.http_port% + %request_listener.https_port% + + + + + + + + + + + + + + + + + %kernel.project_dir% + %kernel.debug% + + + + + %request_listener.http_port% + %request_listener.https_port% + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/schema/symfony-1.0.xsd b/vendor/symfony/framework-bundle/Resources/config/schema/symfony-1.0.xsd new file mode 100644 index 0000000..7c27068 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/schema/symfony-1.0.xsd @@ -0,0 +1,326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/security_csrf.xml b/vendor/symfony/framework-bundle/Resources/config/security_csrf.xml new file mode 100644 index 0000000..8a3ffac --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/security_csrf.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/serializer.xml b/vendor/symfony/framework-bundle/Resources/config/serializer.xml new file mode 100644 index 0000000..42dc667 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/serializer.xml @@ -0,0 +1,129 @@ + + + + + + %kernel.cache_dir%/serialization.php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + null + + + + + + + + + + + + + + + + + + + + + + null + + + + + + + + %serializer.mapping.cache.file% + + + + + + + %serializer.mapping.cache.file% + + + + + + %serializer.mapping.cache.prefix% + + The "%service_id%" service is deprecated since Symfony 3.2 and will be removed in 4.0. APCu should now be automatically used when available. + + + + The "%service_id%" service is deprecated since Symfony 3.1 and will be removed in 4.0. APCu should now be automatically used when available. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/services.xml b/vendor/symfony/framework-bundle/Resources/config/services.xml new file mode 100644 index 0000000..f5c4443 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/services.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Symfony\Component\HttpFoundation\ParameterBag + Symfony\Component\HttpFoundation\HeaderBag + Symfony\Component\HttpFoundation\FileBag + Symfony\Component\HttpFoundation\ServerBag + Symfony\Component\HttpFoundation\Request + Symfony\Component\HttpKernel\Kernel + + The "%service_id%" option is deprecated since version 3.3, to be removed in 4.0. + + + + + + + + + + + + + + + %kernel.root_dir%/Resources + + %kernel.root_dir% + + + + + + %kernel.secret% + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/session.xml b/vendor/symfony/framework-bundle/Resources/config/session.xml new file mode 100644 index 0000000..ce7a3dd --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/session.xml @@ -0,0 +1,82 @@ + + + + + + _sf2_meta + + + + + + + + + + + + + + + + + %session.metadata.storage_key% + %session.metadata.update_threshold% + + + + %session.storage.options% + + + + + + + + + + + + + + + + %kernel.cache_dir%/sessions + MOCKSESSID + + + + + + + %session.save_path% + + + + + + The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use the `session.lazy_write` ini setting instead. + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/ssi.xml b/vendor/symfony/framework-bundle/Resources/config/ssi.xml new file mode 100644 index 0000000..8a3c0cb --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/ssi.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/templating.xml b/vendor/symfony/framework-bundle/Resources/config/templating.xml new file mode 100644 index 0000000..68b0eee --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/templating.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + %kernel.cache_dir% + + + + + + %kernel.root_dir%/Resources + + + + + + + + + + + + + + + %templating.loader.cache.path% + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/templating_debug.xml b/vendor/symfony/framework-bundle/Resources/config/templating_debug.xml new file mode 100644 index 0000000..0bdfbe2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/templating_debug.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + %kernel.charset% + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/templating_php.xml b/vendor/symfony/framework-bundle/Resources/config/templating_php.xml new file mode 100644 index 0000000..82d5b4c --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/templating_php.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + %kernel.charset% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.project_dir% + %kernel.charset% + + + + + + + + + + + + + + + + + + + + %templating.helper.form.resources% + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/test.xml b/vendor/symfony/framework-bundle/Resources/config/test.xml new file mode 100644 index 0000000..ff109c4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/test.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + %test.client.parameters% + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/translation.xml b/vendor/symfony/framework-bundle/Resources/config/translation.xml new file mode 100644 index 0000000..fddd2ab --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/translation.xml @@ -0,0 +1,149 @@ + + + + + + + + + + + %kernel.default_locale% + + + %kernel.cache_dir%/translations + %kernel.debug% + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + yaml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use "translation.reader" instead. + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/translation_debug.xml b/vendor/symfony/framework-bundle/Resources/config/translation_debug.xml new file mode 100644 index 0000000..945f865 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/translation_debug.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/validator.xml b/vendor/symfony/framework-bundle/Resources/config/validator.xml new file mode 100644 index 0000000..66e7c7c --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/validator.xml @@ -0,0 +1,76 @@ + + + + + + + %kernel.cache_dir%/validation.php + + + + + + + + + + + + + + + + + + + + %validator.translation_domain% + + + + + + + + %validator.mapping.cache.file% + + + + + + + + + %validator.mapping.cache.file% + + + + + + + + + + %validator.mapping.cache.prefix% + + + + The "%service_id%" service is deprecated since Symfony 3.4 and will be removed in 4.0. Use a Psr6 cache like "validator.mapping.cache.symfony" instead. + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/validator_debug.xml b/vendor/symfony/framework-bundle/Resources/config/validator_debug.xml new file mode 100644 index 0000000..2548926 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/validator_debug.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/web.xml b/vendor/symfony/framework-bundle/Resources/config/web.xml new file mode 100644 index 0000000..0622c41 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/web.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %kernel.charset% + + + + + + + + + + %kernel.default_locale% + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/web_link.xml b/vendor/symfony/framework-bundle/Resources/config/web_link.xml new file mode 100644 index 0000000..6ce8208 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/web_link.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/config/workflow.xml b/vendor/symfony/framework-bundle/Resources/config/workflow.xml new file mode 100644 index 0000000..d881f69 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/config/workflow.xml @@ -0,0 +1,31 @@ + + + + + + + + + + null + + + + + + null + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/attributes.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/attributes.html.php new file mode 100644 index 0000000..a9bd62f --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/attributes.html.php @@ -0,0 +1,9 @@ + $v): ?> + +escape($k), $view->escape(false !== $translation_domain ? $view['translator']->trans($v, array(), $translation_domain) : $v)) ?> + +escape($k), $view->escape($k)) ?> + +escape($k), $view->escape($v)) ?> + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/button_attributes.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/button_attributes.html.php new file mode 100644 index 0000000..279233b --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/button_attributes.html.php @@ -0,0 +1,2 @@ +id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" +block($form, 'attributes') : '' ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/button_label.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/button_label.html.php new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/button_row.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/button_row.html.php new file mode 100644 index 0000000..b52e929 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/button_row.html.php @@ -0,0 +1,3 @@ +
    + widget($form) ?> +
    diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/button_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/button_widget.html.php new file mode 100644 index 0000000..4b63876 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/button_widget.html.php @@ -0,0 +1,4 @@ + $name, '%id%' => $id)) + : $view['form']->humanize($name); } ?> + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/checkbox_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/checkbox_widget.html.php new file mode 100644 index 0000000..143557d --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/checkbox_widget.html.php @@ -0,0 +1,5 @@ +block($form, 'widget_attributes') ?> + 0): ?> value="escape($value) ?>" + checked="checked" +/> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/choice_attributes.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/choice_attributes.html.php new file mode 100644 index 0000000..18f8368 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/choice_attributes.html.php @@ -0,0 +1,8 @@ +disabled="disabled" + $v): ?> + +escape($k), $view->escape($k)) ?> + +escape($k), $view->escape($v)) ?> + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/choice_options.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/choice_options.html.php new file mode 100644 index 0000000..211ae73 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/choice_options.html.php @@ -0,0 +1 @@ +block($form, 'choice_widget_options') ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget.html.php new file mode 100644 index 0000000..13593a9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget.html.php @@ -0,0 +1,5 @@ + +block($form, 'choice_widget_expanded') ?> + +block($form, 'choice_widget_collapsed') ?> + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_collapsed.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_collapsed.html.php new file mode 100644 index 0000000..92298cf --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_collapsed.html.php @@ -0,0 +1,18 @@ + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_expanded.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_expanded.html.php new file mode 100644 index 0000000..2c42687 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_expanded.html.php @@ -0,0 +1,6 @@ +
    block($form, 'widget_container_attributes') ?>> + + widget($child) ?> + label($child, null, array('translation_domain' => $choice_translation_domain)) ?> + +
    diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_options.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_options.html.php new file mode 100644 index 0000000..f1c6ad3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/choice_widget_options.html.php @@ -0,0 +1,13 @@ + + + $choice): ?> + + + block($form, 'choice_widget_options', array('choices' => $choice)) ?> + + + + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/collection_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/collection_widget.html.php new file mode 100644 index 0000000..9f6a94b --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/collection_widget.html.php @@ -0,0 +1,4 @@ + + escape($view['form']->row($prototype)) ?> + +widget($form, array('attr' => $attr)) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/color_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/color_widget.html.php new file mode 100644 index 0000000..10a8cdf --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/color_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'color')); diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/container_attributes.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/container_attributes.html.php new file mode 100644 index 0000000..302bbfc --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/container_attributes.html.php @@ -0,0 +1 @@ +block($form, 'widget_container_attributes') ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/date_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/date_widget.html.php new file mode 100644 index 0000000..22e51d2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/date_widget.html.php @@ -0,0 +1,11 @@ + + block($form, 'form_widget_simple'); ?> + +
    block($form, 'widget_container_attributes') ?>> + widget($form['year']), + $view['form']->widget($form['month']), + $view['form']->widget($form['day']), + ), $date_pattern) ?> +
    + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/datetime_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/datetime_widget.html.php new file mode 100644 index 0000000..aef2a32 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/datetime_widget.html.php @@ -0,0 +1,7 @@ + + block($form, 'form_widget_simple'); ?> + +
    block($form, 'widget_container_attributes') ?>> + widget($form['date']).' '.$view['form']->widget($form['time']) ?> +
    + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/email_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/email_widget.html.php new file mode 100644 index 0000000..0b30c5b --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/email_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'email')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form.html.php new file mode 100644 index 0000000..fb789fa --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form.html.php @@ -0,0 +1,3 @@ +start($form) ?> + widget($form) ?> +end($form) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_enctype.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_enctype.html.php new file mode 100644 index 0000000..36eba3c --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_enctype.html.php @@ -0,0 +1 @@ +vars['multipart']): ?>enctype="multipart/form-data" diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_end.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_end.html.php new file mode 100644 index 0000000..fe68439 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_end.html.php @@ -0,0 +1,4 @@ + +rest($form) ?> + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_errors.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_errors.html.php new file mode 100644 index 0000000..77c60d7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_errors.html.php @@ -0,0 +1,7 @@ + 0): ?> +
      + +
    • getMessage() ?>
    • + +
    + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_label.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_label.html.php new file mode 100644 index 0000000..78c953e --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_label.html.php @@ -0,0 +1,8 @@ + + + + $name, '%id%' => $id)) + : $view['form']->humanize($name); } ?> +block($form, 'attributes', array('attr' => $label_attr)); } ?>>escape(false !== $translation_domain ? $view['translator']->trans($label, array(), $translation_domain) : $label) ?> + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_rest.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_rest.html.php new file mode 100644 index 0000000..89041c6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_rest.html.php @@ -0,0 +1,5 @@ + + isRendered()): ?> + row($child) ?> + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_row.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_row.html.php new file mode 100644 index 0000000..a4f86d0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_row.html.php @@ -0,0 +1,5 @@ +
    + label($form) ?> + errors($form) ?> + widget($form) ?> +
    diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_rows.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_rows.html.php new file mode 100644 index 0000000..8c3ba86 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_rows.html.php @@ -0,0 +1,3 @@ + + row($child) ?> + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_start.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_start.html.php new file mode 100644 index 0000000..ba2f3a4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_start.html.php @@ -0,0 +1,6 @@ + + +
    action="" $v) { printf(' %s="%s"', $view->escape($k), $view->escape($v)); } ?> enctype="multipart/form-data"> + + + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_widget.html.php new file mode 100644 index 0000000..c5af39a --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_widget.html.php @@ -0,0 +1,5 @@ + +block($form, 'form_widget_compound')?> + +block($form, 'form_widget_simple')?> + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_widget_compound.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_widget_compound.html.php new file mode 100644 index 0000000..7a4f7cd --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_widget_compound.html.php @@ -0,0 +1,7 @@ +
    block($form, 'widget_container_attributes') ?>> + parent && $errors): ?> + errors($form) ?> + + block($form, 'form_rows') ?> + rest($form) ?> +
    diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/form_widget_simple.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/form_widget_simple.html.php new file mode 100644 index 0000000..5d7654f --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/form_widget_simple.html.php @@ -0,0 +1 @@ +block($form, 'widget_attributes') ?> value="escape($value) ?>" /> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/hidden_row.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/hidden_row.html.php new file mode 100644 index 0000000..3239d8f --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/hidden_row.html.php @@ -0,0 +1 @@ +widget($form) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/hidden_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/hidden_widget.html.php new file mode 100644 index 0000000..a43f7de --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/hidden_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'hidden')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/integer_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/integer_widget.html.php new file mode 100644 index 0000000..5fceb49 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/integer_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'number')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/money_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/money_widget.html.php new file mode 100644 index 0000000..25fe13f --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/money_widget.html.php @@ -0,0 +1 @@ +formEncodeCurrency($money_pattern, $view['form']->block($form, 'form_widget_simple')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/number_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/number_widget.html.php new file mode 100644 index 0000000..bf4a4c4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/number_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'text')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/password_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/password_widget.html.php new file mode 100644 index 0000000..ec96cfb --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/password_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'password')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/percent_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/percent_widget.html.php new file mode 100644 index 0000000..8519da4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/percent_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'text')) ?> % diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/radio_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/radio_widget.html.php new file mode 100644 index 0000000..ddc8c52 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/radio_widget.html.php @@ -0,0 +1,5 @@ +block($form, 'widget_attributes') ?> + value="escape($value) ?>" + checked="checked" +/> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/range_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/range_widget.html.php new file mode 100644 index 0000000..4c628f8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/range_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'range')); diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/repeated_row.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/repeated_row.html.php new file mode 100644 index 0000000..d4af23d --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/repeated_row.html.php @@ -0,0 +1 @@ +block($form, 'form_rows') ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/reset_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/reset_widget.html.php new file mode 100644 index 0000000..e8fa18e --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/reset_widget.html.php @@ -0,0 +1 @@ +block($form, 'button_widget', array('type' => isset($type) ? $type : 'reset')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/search_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/search_widget.html.php new file mode 100644 index 0000000..48a33f4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/search_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'search')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/submit_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/submit_widget.html.php new file mode 100644 index 0000000..6bf71f5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/submit_widget.html.php @@ -0,0 +1 @@ +block($form, 'button_widget', array('type' => isset($type) ? $type : 'submit')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/tel_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/tel_widget.html.php new file mode 100644 index 0000000..7779538 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/tel_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'tel')); diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/textarea_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/textarea_widget.html.php new file mode 100644 index 0000000..c989ce5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/textarea_widget.html.php @@ -0,0 +1 @@ + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/time_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/time_widget.html.php new file mode 100644 index 0000000..e24411c --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/time_widget.html.php @@ -0,0 +1,22 @@ + + block($form, 'form_widget_simple'); ?> + + array('size' => 1)) : array() ?> +
    block($form, 'widget_container_attributes') ?>> + widget($form['hour'], $vars); + + if ($with_minutes) { + echo ':'; + echo $view['form']->widget($form['minute'], $vars); + } + + if ($with_seconds) { + echo ':'; + echo $view['form']->widget($form['second'], $vars); + } + ?> +
    + diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/url_widget.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/url_widget.html.php new file mode 100644 index 0000000..9e26318 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/url_widget.html.php @@ -0,0 +1 @@ +block($form, 'form_widget_simple', array('type' => isset($type) ? $type : 'url')) ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/widget_attributes.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/widget_attributes.html.php new file mode 100644 index 0000000..41c0cc7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/widget_attributes.html.php @@ -0,0 +1,3 @@ +id="escape($id) ?>" name="escape($full_name) ?>" disabled="disabled" + required="required" +block($form, 'attributes') : '' ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/Form/widget_container_attributes.html.php b/vendor/symfony/framework-bundle/Resources/views/Form/widget_container_attributes.html.php new file mode 100644 index 0000000..fdd176d --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/Form/widget_container_attributes.html.php @@ -0,0 +1,2 @@ +id="escape($id) ?>" +block($form, 'attributes') : '' ?> diff --git a/vendor/symfony/framework-bundle/Resources/views/FormTable/button_row.html.php b/vendor/symfony/framework-bundle/Resources/views/FormTable/button_row.html.php new file mode 100644 index 0000000..67d0137 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/FormTable/button_row.html.php @@ -0,0 +1,6 @@ + + + + widget($form); ?> + + diff --git a/vendor/symfony/framework-bundle/Resources/views/FormTable/form_row.html.php b/vendor/symfony/framework-bundle/Resources/views/FormTable/form_row.html.php new file mode 100644 index 0000000..e2f03ff --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/FormTable/form_row.html.php @@ -0,0 +1,9 @@ + + + label($form); ?> + + + errors($form); ?> + widget($form); ?> + + diff --git a/vendor/symfony/framework-bundle/Resources/views/FormTable/form_widget_compound.html.php b/vendor/symfony/framework-bundle/Resources/views/FormTable/form_widget_compound.html.php new file mode 100644 index 0000000..adc8973 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/FormTable/form_widget_compound.html.php @@ -0,0 +1,11 @@ +block($form, 'widget_container_attributes'); ?>> + parent && $errors): ?> + + + + + block($form, 'form_rows'); ?> + rest($form); ?> +
    + errors($form); ?> +
    diff --git a/vendor/symfony/framework-bundle/Resources/views/FormTable/hidden_row.html.php b/vendor/symfony/framework-bundle/Resources/views/FormTable/hidden_row.html.php new file mode 100644 index 0000000..116b300 --- /dev/null +++ b/vendor/symfony/framework-bundle/Resources/views/FormTable/hidden_row.html.php @@ -0,0 +1,5 @@ + + + widget($form); ?> + + diff --git a/vendor/symfony/framework-bundle/Routing/AnnotatedRouteControllerLoader.php b/vendor/symfony/framework-bundle/Routing/AnnotatedRouteControllerLoader.php new file mode 100644 index 0000000..474c725 --- /dev/null +++ b/vendor/symfony/framework-bundle/Routing/AnnotatedRouteControllerLoader.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\Bundle\FrameworkBundle\Routing; + +use Symfony\Component\Routing\Loader\AnnotationClassLoader; +use Symfony\Component\Routing\Route; + +/** + * AnnotatedRouteControllerLoader is an implementation of AnnotationClassLoader + * that sets the '_controller' default based on the class and method names. + * + * @author Fabien Potencier + */ +class AnnotatedRouteControllerLoader extends AnnotationClassLoader +{ + /** + * Configures the _controller default parameter of a given Route instance. + * + * @param mixed $annot The annotation class instance + */ + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + if ('__invoke' === $method->getName()) { + $route->setDefault('_controller', $class->getName()); + } else { + $route->setDefault('_controller', $class->getName().'::'.$method->getName()); + } + } + + /** + * Makes the default route name more sane by removing common keywords. + * + * @return string + */ + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + { + return preg_replace(array( + '/(bundle|controller)_/', + '/action(_\d+)?$/', + '/__/', + ), array( + '_', + '\\1', + '_', + ), parent::getDefaultRouteName($class, $method)); + } +} diff --git a/vendor/symfony/framework-bundle/Routing/DelegatingLoader.php b/vendor/symfony/framework-bundle/Routing/DelegatingLoader.php new file mode 100644 index 0000000..cf94ffe --- /dev/null +++ b/vendor/symfony/framework-bundle/Routing/DelegatingLoader.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\Bundle\FrameworkBundle\Routing; + +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Component\Config\Exception\FileLoaderLoadException; +use Symfony\Component\Config\Loader\DelegatingLoader as BaseDelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolverInterface; + +/** + * DelegatingLoader delegates route loading to other loaders using a loader resolver. + * + * This implementation resolves the _controller attribute from the short notation + * to the fully-qualified form (from a:b:c to class::method). + * + * @author Fabien Potencier + */ +class DelegatingLoader extends BaseDelegatingLoader +{ + protected $parser; + private $loading = false; + + /** + * @param ControllerNameParser $parser A ControllerNameParser instance + * @param LoaderResolverInterface $resolver A LoaderResolverInterface instance + */ + public function __construct(ControllerNameParser $parser, LoaderResolverInterface $resolver) + { + $this->parser = $parser; + + parent::__construct($resolver); + } + + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + if ($this->loading) { + // This can happen if a fatal error occurs in parent::load(). + // Here is the scenario: + // - while routes are being loaded by parent::load() below, a fatal error + // occurs (e.g. parse error in a controller while loading annotations); + // - PHP abruptly empties the stack trace, bypassing all catch/finally blocks; + // it then calls the registered shutdown functions; + // - the ErrorHandler catches the fatal error and re-injects it for rendering + // thanks to HttpKernel->terminateWithException() (that calls handleException()); + // - at this stage, if we try to load the routes again, we must prevent + // the fatal error from occurring a second time, + // otherwise the PHP process would be killed immediately; + // - while rendering the exception page, the router can be required + // (by e.g. the web profiler that needs to generate an URL); + // - this handles the case and prevents the second fatal error + // by triggering an exception beforehand. + + throw new FileLoaderLoadException($resource, null, null, null, $type); + } + $this->loading = true; + + try { + $collection = parent::load($resource, $type); + } finally { + $this->loading = false; + } + + foreach ($collection->all() as $route) { + if (!\is_string($controller = $route->getDefault('_controller')) || !$controller) { + continue; + } + + try { + $controller = $this->parser->parse($controller); + } catch (\InvalidArgumentException $e) { + // unable to optimize unknown notation + } + + $route->setDefault('_controller', $controller); + } + + return $collection; + } +} diff --git a/vendor/symfony/framework-bundle/Routing/RedirectableUrlMatcher.php b/vendor/symfony/framework-bundle/Routing/RedirectableUrlMatcher.php new file mode 100644 index 0000000..5571c74 --- /dev/null +++ b/vendor/symfony/framework-bundle/Routing/RedirectableUrlMatcher.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\Bundle\FrameworkBundle\Routing; + +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcher as BaseMatcher; + +/** + * @author Fabien Potencier + */ +class RedirectableUrlMatcher extends BaseMatcher +{ + /** + * Redirects the user to another URL. + * + * @param string $path The path info to redirect to + * @param string $route The route that matched + * @param string $scheme The URL scheme (null to keep the current one) + * + * @return array An array of parameters + */ + public function redirect($path, $route, $scheme = null) + { + return array( + '_controller' => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction', + 'path' => $path, + 'permanent' => true, + 'scheme' => $scheme, + 'httpPort' => $this->context->getHttpPort(), + 'httpsPort' => $this->context->getHttpsPort(), + '_route' => $route, + ); + } +} diff --git a/vendor/symfony/framework-bundle/Routing/Router.php b/vendor/symfony/framework-bundle/Routing/Router.php new file mode 100644 index 0000000..3904996 --- /dev/null +++ b/vendor/symfony/framework-bundle/Routing/Router.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Routing; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Router as BaseRouter; + +/** + * This Router creates the Loader only when the cache is empty. + * + * @author Fabien Potencier + */ +class Router extends BaseRouter implements WarmableInterface, ServiceSubscriberInterface +{ + private $container; + private $collectedParameters = array(); + + /** + * @param ContainerInterface $container A ContainerInterface instance + * @param mixed $resource The main resource to load + * @param array $options An array of options + * @param RequestContext $context The context + */ + public function __construct(ContainerInterface $container, $resource, array $options = array(), RequestContext $context = null) + { + $this->container = $container; + + $this->resource = $resource; + $this->context = $context ?: new RequestContext(); + $this->setOptions($options); + } + + /** + * {@inheritdoc} + */ + public function getRouteCollection() + { + if (null === $this->collection) { + $this->collection = $this->container->get('routing.loader')->load($this->resource, $this->options['resource_type']); + $this->resolveParameters($this->collection); + $this->collection->addResource(new ContainerParametersResource($this->collectedParameters)); + } + + return $this->collection; + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + $currentDir = $this->getOption('cache_dir'); + + // force cache generation + $this->setOption('cache_dir', $cacheDir); + $this->getMatcher(); + $this->getGenerator(); + + $this->setOption('cache_dir', $currentDir); + } + + /** + * Replaces placeholders with service container parameter values in: + * - the route defaults, + * - the route requirements, + * - the route path, + * - the route host, + * - the route schemes, + * - the route methods. + */ + private function resolveParameters(RouteCollection $collection) + { + foreach ($collection as $route) { + foreach ($route->getDefaults() as $name => $value) { + $route->setDefault($name, $this->resolve($value)); + } + + foreach ($route->getRequirements() as $name => $value) { + $route->setRequirement($name, $this->resolve($value)); + } + + $route->setPath($this->resolve($route->getPath())); + $route->setHost($this->resolve($route->getHost())); + + $schemes = array(); + foreach ($route->getSchemes() as $scheme) { + $schemes = array_merge($schemes, explode('|', $this->resolve($scheme))); + } + $route->setSchemes($schemes); + + $methods = array(); + foreach ($route->getMethods() as $method) { + $methods = array_merge($methods, explode('|', $this->resolve($method))); + } + $route->setMethods($methods); + $route->setCondition($this->resolve($route->getCondition())); + } + } + + /** + * Recursively replaces placeholders with the service container parameters. + * + * @param mixed $value The source which might contain "%placeholders%" + * + * @return mixed The source with the placeholders replaced by the container + * parameters. Arrays are resolved recursively. + * + * @throws ParameterNotFoundException When a placeholder does not exist as a container parameter + * @throws RuntimeException When a container value is not a string or a numeric value + */ + private function resolve($value) + { + if (\is_array($value)) { + foreach ($value as $key => $val) { + $value[$key] = $this->resolve($val); + } + + return $value; + } + + if (!\is_string($value)) { + return $value; + } + + $container = $this->container; + + $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($container, $value) { + // skip %% + if (!isset($match[1])) { + return '%%'; + } + + if (preg_match('/^env\(\w+\)$/', $match[1])) { + throw new RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1])); + } + + $resolved = $container->getParameter($match[1]); + + if (\is_string($resolved) || is_numeric($resolved)) { + $this->collectedParameters[$match[1]] = $resolved; + + return (string) $resolved; + } + + throw new RuntimeException(sprintf('The container parameter "%s", used in the route configuration value "%s", must be a string or numeric, but it is of type %s.', $match[1], $value, \gettype($resolved))); + }, $value); + + return str_replace('%%', '%', $escapedValue); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedServices() + { + return array( + 'routing.loader' => LoaderInterface::class, + ); + } +} diff --git a/vendor/symfony/framework-bundle/Templating/DelegatingEngine.php b/vendor/symfony/framework-bundle/Templating/DelegatingEngine.php new file mode 100644 index 0000000..07bf39f --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/DelegatingEngine.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\Bundle\FrameworkBundle\Templating; + +use Psr\Container\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\DelegatingEngine as BaseDelegatingEngine; + +/** + * DelegatingEngine selects an engine for a given template. + * + * @author Fabien Potencier + */ +class DelegatingEngine extends BaseDelegatingEngine implements EngineInterface +{ + protected $container; + + public function __construct(ContainerInterface $container, array $engineIds) + { + $this->container = $container; + $this->engines = $engineIds; + } + + /** + * {@inheritdoc} + */ + public function getEngine($name) + { + $this->resolveEngines(); + + return parent::getEngine($name); + } + + /** + * {@inheritdoc} + */ + public function renderResponse($view, array $parameters = array(), Response $response = null) + { + $engine = $this->getEngine($view); + + if ($engine instanceof EngineInterface) { + return $engine->renderResponse($view, $parameters, $response); + } + + if (null === $response) { + $response = new Response(); + } + + $response->setContent($engine->render($view, $parameters)); + + return $response; + } + + /** + * Resolved engine ids to their real engine instances from the container. + */ + private function resolveEngines() + { + foreach ($this->engines as $i => $engine) { + if (\is_string($engine)) { + $this->engines[$i] = $this->container->get($engine); + } + } + } +} diff --git a/vendor/symfony/framework-bundle/Templating/EngineInterface.php b/vendor/symfony/framework-bundle/Templating/EngineInterface.php new file mode 100644 index 0000000..cc0d573 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/EngineInterface.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\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\EngineInterface as BaseEngineInterface; + +/** + * EngineInterface is the interface each engine must implement. + * + * @author Fabien Potencier + */ +interface EngineInterface extends BaseEngineInterface +{ + /** + * Renders a view and returns a Response. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param Response $response A Response instance + * + * @return Response A Response instance + * + * @throws \RuntimeException if the template cannot be rendered + */ + public function renderResponse($view, array $parameters = array(), Response $response = null); +} diff --git a/vendor/symfony/framework-bundle/Templating/GlobalVariables.php b/vendor/symfony/framework-bundle/Templating/GlobalVariables.php new file mode 100644 index 0000000..4e07b9b --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/GlobalVariables.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\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +/** + * GlobalVariables is the entry point for Symfony global variables in PHP templates. + * + * @author Fabien Potencier + */ +class GlobalVariables +{ + protected $container; + + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + /** + * @return TokenInterface|null + */ + public function getToken() + { + if (!$this->container->has('security.token_storage')) { + return; + } + + return $this->container->get('security.token_storage')->getToken(); + } + + public function getUser() + { + if (!$token = $this->getToken()) { + return; + } + + $user = $token->getUser(); + if (!\is_object($user)) { + return; + } + + return $user; + } + + /** + * @return Request|null The HTTP request object + */ + public function getRequest() + { + if ($this->container->has('request_stack')) { + return $this->container->get('request_stack')->getCurrentRequest(); + } + } + + /** + * @return Session|null The session + */ + public function getSession() + { + if ($request = $this->getRequest()) { + return $request->getSession(); + } + } + + /** + * @return string The current environment string (e.g 'dev') + */ + public function getEnvironment() + { + return $this->container->getParameter('kernel.environment'); + } + + /** + * @return bool The current debug mode + */ + public function getDebug() + { + return (bool) $this->container->getParameter('kernel.debug'); + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/ActionsHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/ActionsHelper.php new file mode 100644 index 0000000..76a6046 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/ActionsHelper.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\Templating\Helper\Helper; + +/** + * ActionsHelper manages action inclusions. + * + * @author Fabien Potencier + */ +class ActionsHelper extends Helper +{ + private $handler; + + public function __construct(FragmentHandler $handler) + { + $this->handler = $handler; + } + + /** + * Returns the fragment content for a given URI. + * + * @param string $uri A URI + * @param array $options An array of options + * + * @return string The fragment content + * + * @see FragmentHandler::render() + */ + public function render($uri, array $options = array()) + { + $strategy = isset($options['strategy']) ? $options['strategy'] : 'inline'; + unset($options['strategy']); + + return $this->handler->render($uri, $strategy, $options); + } + + public function controller($controller, $attributes = array(), $query = array()) + { + return new ControllerReference($controller, $attributes, $query); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'actions'; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/AssetsHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/AssetsHelper.php new file mode 100644 index 0000000..072b6e7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/AssetsHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Asset\Packages; +use Symfony\Component\Templating\Helper\Helper; + +/** + * AssetsHelper helps manage asset URLs. + * + * @author Fabien Potencier + */ +class AssetsHelper extends Helper +{ + private $packages; + + public function __construct(Packages $packages) + { + $this->packages = $packages; + } + + /** + * Returns the public url/path of an asset. + * + * If the package used to generate the path is an instance of + * UrlPackage, you will always get a URL and not a path. + * + * @param string $path A public path + * @param string $packageName The name of the asset package to use + * + * @return string The public path of the asset + */ + public function getUrl($path, $packageName = null) + { + return $this->packages->getUrl($path, $packageName); + } + + /** + * Returns the version of an asset. + * + * @param string $path A public path + * @param string $packageName The name of the asset package to use + * + * @return string The asset version + */ + public function getVersion($path, $packageName = null) + { + return $this->packages->getVersion($path, $packageName); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'assets'; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/CodeHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/CodeHelper.php new file mode 100644 index 0000000..c4087a2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/CodeHelper.php @@ -0,0 +1,225 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; +use Symfony\Component\Templating\Helper\Helper; + +/** + * @author Fabien Potencier + */ +class CodeHelper extends Helper +{ + protected $fileLinkFormat; + protected $rootDir; + protected $charset; + + /** + * @param string|FileLinkFormatter $fileLinkFormat The format for links to source files + * @param string $rootDir The project root directory + * @param string $charset The charset + */ + public function __construct($fileLinkFormat, $rootDir, $charset) + { + $this->fileLinkFormat = $fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); + $this->rootDir = str_replace('\\', '/', $rootDir).'/'; + $this->charset = $charset; + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + public function formatArgsAsText(array $args) + { + return strip_tags($this->formatArgs($args)); + } + + public function abbrClass($class) + { + $parts = explode('\\', $class); + $short = array_pop($parts); + + return sprintf('%s', $class, $short); + } + + public function abbrMethod($method) + { + if (false !== strpos($method, '::')) { + list($class, $method) = explode('::', $method, 2); + $result = sprintf('%s::%s()', $this->abbrClass($class), $method); + } elseif ('Closure' === $method) { + $result = sprintf('%1$s', $method); + } else { + $result = sprintf('%1$s()', $method); + } + + return $result; + } + + /** + * Formats an array as a string. + * + * @param array $args The argument array + * + * @return string + */ + public function formatArgs(array $args) + { + $result = array(); + foreach ($args as $key => $item) { + if ('object' === $item[0]) { + $parts = explode('\\', $item[1]); + $short = array_pop($parts); + $formattedValue = sprintf('object(%s)', $item[1], $short); + } elseif ('array' === $item[0]) { + $formattedValue = sprintf('array(%s)', \is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); + } elseif ('string' === $item[0]) { + $formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES, $this->getCharset())); + } 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", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES, $this->getCharset()), true)); + } + + $result[] = \is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); + } + + return implode(', ', $result); + } + + /** + * Returns an excerpt of a code file around the given line number. + * + * @param string $file A file path + * @param int $line The selected line number + * + * @return string An HTML string + */ + public function fileExcerpt($file, $line) + { + if (is_readable($file)) { + if (\extension_loaded('fileinfo')) { + $finfo = new \finfo(); + + // Check if the file is an application/octet-stream (eg. Phar file) because highlight_file cannot parse these files + if ('application/octet-stream' === $finfo->file($file, FILEINFO_MIME_TYPE)) { + return; + } + } + + // highlight_file could throw warnings + // see https://bugs.php.net/bug.php?id=25725 + $code = @highlight_file($file, true); + // remove main code/span tags + $code = preg_replace('#^\s*(.*)\s*#s', '\\1', $code); + $content = explode('
    ', $code); + + $lines = array(); + for ($i = max($line - 3, 1), $max = min($line + 3, \count($content)); $i <= $max; ++$i) { + $lines[] = ''.self::fixCodeMarkup($content[$i - 1]).''; + } + + return '
      '.implode("\n", $lines).'
    '; + } + } + + /** + * Formats a file path. + * + * @param string $file An absolute file path + * @param int $line The line number + * @param string $text Use this text for the link rather than the file path + * + * @return string + */ + public function formatFile($file, $line, $text = null) + { + $flags = ENT_QUOTES | ENT_SUBSTITUTE; + + if (null === $text) { + $file = trim($file); + $fileStr = $file; + if (0 === strpos($fileStr, $this->rootDir)) { + $fileStr = str_replace(array('\\', $this->rootDir), array('/', ''), $fileStr); + $fileStr = htmlspecialchars($fileStr, $flags, $this->charset); + $fileStr = sprintf('kernel.root_dir/%s', htmlspecialchars($this->rootDir, $flags, $this->charset), $fileStr); + } + + $text = sprintf('%s at line %d', $fileStr, $line); + } + + if (false !== $link = $this->getFileLink($file, $line)) { + return sprintf('%s', htmlspecialchars($link, $flags, $this->charset), $text); + } + + return $text; + } + + /** + * Returns the link for a given file/line pair. + * + * @param string $file An absolute file path + * @param int $line The line number + * + * @return string A link of false + */ + public function getFileLink($file, $line) + { + if ($fmt = $this->fileLinkFormat) { + return \is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line); + } + + return false; + } + + public function formatFileFromText($text) + { + return preg_replace_callback('/in ("|")?(.+?)\1(?: +(?:on|at))? +line (\d+)/s', function ($match) { + return 'in '.$this->formatFile($match[2], $match[3]); + }, $text); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'code'; + } + + protected static function fixCodeMarkup($line) + { + // ending tag from previous line + $opening = strpos($line, ''); + if (false !== $closing && (false === $opening || $closing < $opening)) { + $line = substr_replace($line, '', $closing, 7); + } + + // missing tag at the end of line + $opening = strpos($line, ''); + if (false !== $opening && (false === $closing || $closing > $opening)) { + $line .= ''; + } + + return $line; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/FormHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/FormHelper.php new file mode 100644 index 0000000..a07b050 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/FormHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Form\FormRendererInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\Templating\Helper\Helper; + +/** + * FormHelper provides helpers to help display forms. + * + * @author Fabien Potencier + * @author Bernhard Schussek + */ +class FormHelper extends Helper +{ + private $renderer; + + public function __construct(FormRendererInterface $renderer) + { + $this->renderer = $renderer; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'form'; + } + + /** + * Sets a theme for a given view. + * + * The theme format is ":". + * + * @param FormView $view A FormView instance + * @param string|array $themes A theme or an array of theme + * @param bool $useDefaultThemes If true, will use default themes defined in the renderer + */ + public function setTheme(FormView $view, $themes, $useDefaultThemes = true) + { + $this->renderer->setTheme($view, $themes, $useDefaultThemes); + } + + /** + * Renders the HTML for a form. + * + * Example usage: + * + * form($form) ?> + * + * You can pass options during the call: + * + * form($form, array('attr' => array('class' => 'foo'))) ?> + * + * form($form, array('separator' => '+++++')) ?> + * + * This method is mainly intended for prototyping purposes. If you want to + * control the layout of a form in a more fine-grained manner, you are + * advised to use the other helper methods for rendering the parts of the + * form individually. You can also create a custom form theme to adapt + * the look of the form. + * + * @param FormView $view The view for which to render the form + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function form(FormView $view, array $variables = array()) + { + return $this->renderer->renderBlock($view, 'form', $variables); + } + + /** + * Renders the form start tag. + * + * Example usage templates: + * + * start($form) ?>> + * + * @param FormView $view The view for which to render the start tag + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function start(FormView $view, array $variables = array()) + { + return $this->renderer->renderBlock($view, 'form_start', $variables); + } + + /** + * Renders the form end tag. + * + * Example usage templates: + * + * end($form) ?>> + * + * @param FormView $view The view for which to render the end tag + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function end(FormView $view, array $variables = array()) + { + return $this->renderer->renderBlock($view, 'form_end', $variables); + } + + /** + * Renders the HTML for a given view. + * + * Example usage: + * + * widget($form) ?> + * + * You can pass options during the call: + * + * widget($form, array('attr' => array('class' => 'foo'))) ?> + * + * widget($form, array('separator' => '+++++')) ?> + * + * @param FormView $view The view for which to render the widget + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function widget(FormView $view, array $variables = array()) + { + return $this->renderer->searchAndRenderBlock($view, 'widget', $variables); + } + + /** + * Renders the entire form field "row". + * + * @param FormView $view The view for which to render the row + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function row(FormView $view, array $variables = array()) + { + return $this->renderer->searchAndRenderBlock($view, 'row', $variables); + } + + /** + * Renders the label of the given view. + * + * @param FormView $view The view for which to render the label + * @param string $label The label + * @param array $variables Additional variables passed to the template + * + * @return string The HTML markup + */ + public function label(FormView $view, $label = null, array $variables = array()) + { + if (null !== $label) { + $variables += array('label' => $label); + } + + return $this->renderer->searchAndRenderBlock($view, 'label', $variables); + } + + /** + * Renders the errors of the given view. + * + * @return string The HTML markup + */ + public function errors(FormView $view) + { + return $this->renderer->searchAndRenderBlock($view, 'errors'); + } + + /** + * Renders views which have not already been rendered. + * + * @param FormView $view The parent view + * @param array $variables An array of variables + * + * @return string The HTML markup + */ + public function rest(FormView $view, array $variables = array()) + { + return $this->renderer->searchAndRenderBlock($view, 'rest', $variables); + } + + /** + * Renders a block of the template. + * + * @param FormView $view The view for determining the used themes + * @param string $blockName The name of the block to render + * @param array $variables The variable to pass to the template + * + * @return string The HTML markup + */ + public function block(FormView $view, $blockName, array $variables = array()) + { + return $this->renderer->renderBlock($view, $blockName, $variables); + } + + /** + * Returns a CSRF token. + * + * Use this helper for CSRF protection without the overhead of creating a + * form. + * + * echo $view['form']->csrfToken('rm_user_'.$user->getId()); + * + * Check the token in your action using the same CSRF token id. + * + * // $csrfProvider being an instance of Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface + * if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) { + * throw new \RuntimeException('CSRF attack detected.'); + * } + * + * @param string $tokenId The CSRF token id of the protected action + * + * @return string A CSRF token + * + * @throws \BadMethodCallException when no CSRF provider was injected in the constructor + */ + public function csrfToken($tokenId) + { + return $this->renderer->renderCsrfToken($tokenId); + } + + public function humanize($text) + { + return $this->renderer->humanize($text); + } + + /** + * @internal + */ + public function formEncodeCurrency($text, $widget = '') + { + if ('UTF-8' === $charset = $this->getCharset()) { + $text = htmlspecialchars($text, ENT_QUOTES | (\defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0), 'UTF-8'); + } else { + $text = htmlentities($text, ENT_QUOTES | (\defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0), 'UTF-8'); + $text = iconv('UTF-8', $charset, $text); + $widget = iconv('UTF-8', $charset, $widget); + } + + return str_replace('{{ widget }}', $widget, $text); + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/RequestHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/RequestHelper.php new file mode 100644 index 0000000..3beaaad --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/RequestHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Templating\Helper\Helper; + +/** + * RequestHelper provides access to the current request parameters. + * + * @author Fabien Potencier + */ +class RequestHelper extends Helper +{ + protected $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * Returns a parameter from the current request object. + * + * @param string $key The name of the parameter + * @param string $default A default value + * + * @return mixed + * + * @see Request::get() + */ + public function getParameter($key, $default = null) + { + return $this->getRequest()->get($key, $default); + } + + /** + * Returns the locale. + * + * @return string + */ + public function getLocale() + { + return $this->getRequest()->getLocale(); + } + + private function getRequest() + { + if (!$this->requestStack->getCurrentRequest()) { + throw new \LogicException('A Request must be available.'); + } + + return $this->requestStack->getCurrentRequest(); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'request'; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/RouterHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/RouterHelper.php new file mode 100644 index 0000000..683efd5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/RouterHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Templating\Helper\Helper; + +/** + * RouterHelper manages links between pages in a template context. + * + * @author Fabien Potencier + */ +class RouterHelper extends Helper +{ + protected $generator; + + public function __construct(UrlGeneratorInterface $router) + { + $this->generator = $router; + } + + /** + * Generates a URL reference (as an absolute or relative path) to the route with the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param bool $relative Whether to generate a relative or absolute path + * + * @return string The generated URL reference + * + * @see UrlGeneratorInterface + */ + public function path($name, $parameters = array(), $relative = false) + { + return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); + } + + /** + * Generates a URL reference (as an absolute URL or network path) to the route with the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param bool $schemeRelative Whether to omit the scheme in the generated URL reference + * + * @return string The generated URL reference + * + * @see UrlGeneratorInterface + */ + public function url($name, $parameters = array(), $schemeRelative = false) + { + return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'router'; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/SessionHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/SessionHelper.php new file mode 100644 index 0000000..4de6e7e --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/SessionHelper.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Templating\Helper\Helper; + +/** + * SessionHelper provides read-only access to the session attributes. + * + * @author Fabien Potencier + */ +class SessionHelper extends Helper +{ + protected $session; + protected $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value + * + * @return mixed + */ + public function get($name, $default = null) + { + return $this->getSession()->get($name, $default); + } + + public function getFlash($name, array $default = array()) + { + return $this->getSession()->getFlashBag()->get($name, $default); + } + + public function getFlashes() + { + return $this->getSession()->getFlashBag()->all(); + } + + public function hasFlash($name) + { + return $this->getSession()->getFlashBag()->has($name); + } + + private function getSession() + { + if (null === $this->session) { + if (!$this->requestStack->getMasterRequest()) { + throw new \LogicException('A Request must be available.'); + } + + $this->session = $this->requestStack->getMasterRequest()->getSession(); + } + + return $this->session; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'session'; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/StopwatchHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/StopwatchHelper.php new file mode 100644 index 0000000..72269ec --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/StopwatchHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Templating\Helper\Helper; + +/** + * StopwatchHelper provides methods time your PHP templates. + * + * @author Wouter J + */ +class StopwatchHelper extends Helper +{ + private $stopwatch; + + public function __construct(Stopwatch $stopwatch = null) + { + $this->stopwatch = $stopwatch; + } + + public function getName() + { + return 'stopwatch'; + } + + public function __call($method, $arguments = array()) + { + if (null !== $this->stopwatch) { + if (method_exists($this->stopwatch, $method)) { + return \call_user_func_array(array($this->stopwatch, $method), $arguments); + } + + throw new \BadMethodCallException(sprintf('Method "%s" of Stopwatch does not exist', $method)); + } + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Helper/TranslatorHelper.php b/vendor/symfony/framework-bundle/Templating/Helper/TranslatorHelper.php new file mode 100644 index 0000000..5c4a9ae --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Helper/TranslatorHelper.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\Bundle\FrameworkBundle\Templating\Helper; + +use Symfony\Component\Templating\Helper\Helper; +use Symfony\Component\Translation\TranslatorInterface; + +/** + * @author Fabien Potencier + */ +class TranslatorHelper extends Helper +{ + protected $translator; + + public function __construct(TranslatorInterface $translator) + { + $this->translator = $translator; + } + + /** + * @see TranslatorInterface::trans() + */ + public function trans($id, array $parameters = array(), $domain = 'messages', $locale = null) + { + return $this->translator->trans($id, $parameters, $domain, $locale); + } + + /** + * @see TranslatorInterface::transChoice() + */ + public function transChoice($id, $number, array $parameters = array(), $domain = 'messages', $locale = null) + { + return $this->translator->transChoice($id, $number, $parameters, $domain, $locale); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'translator'; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Loader/FilesystemLoader.php b/vendor/symfony/framework-bundle/Templating/Loader/FilesystemLoader.php new file mode 100644 index 0000000..4edceed --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Loader/FilesystemLoader.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Templating\Loader\LoaderInterface; +use Symfony\Component\Templating\Storage\FileStorage; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * FilesystemLoader is a loader that read templates from the filesystem. + * + * @author Fabien Potencier + */ +class FilesystemLoader implements LoaderInterface +{ + protected $locator; + + public function __construct(FileLocatorInterface $locator) + { + $this->locator = $locator; + } + + /** + * {@inheritdoc} + */ + public function load(TemplateReferenceInterface $template) + { + try { + $file = $this->locator->locate($template); + } catch (\InvalidArgumentException $e) { + return false; + } + + return new FileStorage($file); + } + + /** + * {@inheritdoc} + */ + public function isFresh(TemplateReferenceInterface $template, $time) + { + if (false === $storage = $this->load($template)) { + return false; + } + + if (!is_readable((string) $storage)) { + return false; + } + + return filemtime((string) $storage) < $time; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/Loader/TemplateLocator.php b/vendor/symfony/framework-bundle/Templating/Loader/TemplateLocator.php new file mode 100644 index 0000000..dc57cc0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/Loader/TemplateLocator.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\Bundle\FrameworkBundle\Templating\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * TemplateLocator locates templates in bundles. + * + * @author Fabien Potencier + */ +class TemplateLocator implements FileLocatorInterface +{ + protected $locator; + protected $cache; + + private $cacheHits = array(); + + /** + * @param FileLocatorInterface $locator A FileLocatorInterface instance + * @param string $cacheDir The cache path + */ + public function __construct(FileLocatorInterface $locator, $cacheDir = null) + { + if (null !== $cacheDir && file_exists($cache = $cacheDir.'/templates.php')) { + $this->cache = require $cache; + } + + $this->locator = $locator; + } + + /** + * Returns a full path for a given file. + * + * @return string The full path for the file + */ + protected function getCacheKey($template) + { + return $template->getLogicalName(); + } + + /** + * Returns a full path for a given file. + * + * @param TemplateReferenceInterface $template A template + * @param string $currentPath Unused + * @param bool $first Unused + * + * @return string The full path for the file + * + * @throws \InvalidArgumentException When the template is not an instance of TemplateReferenceInterface + * @throws \InvalidArgumentException When the template file can not be found + */ + public function locate($template, $currentPath = null, $first = true) + { + if (!$template instanceof TemplateReferenceInterface) { + throw new \InvalidArgumentException('The template must be an instance of TemplateReferenceInterface.'); + } + + $key = $this->getCacheKey($template); + + if (isset($this->cacheHits[$key])) { + return $this->cacheHits[$key]; + } + if (isset($this->cache[$key])) { + return $this->cacheHits[$key] = realpath($this->cache[$key]) ?: $this->cache[$key]; + } + + try { + return $this->cacheHits[$key] = $this->locator->locate($template->getPath(), $currentPath); + } catch (\InvalidArgumentException $e) { + throw new \InvalidArgumentException(sprintf('Unable to find template "%s" : "%s".', $template, $e->getMessage()), 0, $e); + } + } +} diff --git a/vendor/symfony/framework-bundle/Templating/PhpEngine.php b/vendor/symfony/framework-bundle/Templating/PhpEngine.php new file mode 100644 index 0000000..bad7c17 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/PhpEngine.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Psr\Container\ContainerInterface; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Templating\Loader\LoaderInterface; +use Symfony\Component\Templating\PhpEngine as BasePhpEngine; +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * This engine knows how to render Symfony templates. + * + * @author Fabien Potencier + */ +class PhpEngine extends BasePhpEngine implements EngineInterface +{ + protected $container; + + public function __construct(TemplateNameParserInterface $parser, ContainerInterface $container, LoaderInterface $loader, GlobalVariables $globals = null) + { + $this->container = $container; + + parent::__construct($parser, $loader); + + if (null !== $globals) { + $this->addGlobal('app', $globals); + } + } + + /** + * {@inheritdoc} + */ + public function get($name) + { + if (!isset($this->helpers[$name])) { + throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + if (\is_string($this->helpers[$name])) { + $this->helpers[$name] = $this->container->get($this->helpers[$name]); + $this->helpers[$name]->setCharset($this->charset); + } + + return $this->helpers[$name]; + } + + /** + * {@inheritdoc} + */ + public function setHelpers(array $helpers) + { + $this->helpers = $helpers; + } + + /** + * {@inheritdoc} + */ + public function renderResponse($view, array $parameters = array(), Response $response = null) + { + if (null === $response) { + $response = new Response(); + } + + $response->setContent($this->render($view, $parameters)); + + return $response; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/TemplateFilenameParser.php b/vendor/symfony/framework-bundle/Templating/TemplateFilenameParser.php new file mode 100644 index 0000000..b687033 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/TemplateFilenameParser.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\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * TemplateFilenameParser converts template filenames to + * TemplateReferenceInterface instances. + * + * @author Fabien Potencier + */ +class TemplateFilenameParser implements TemplateNameParserInterface +{ + /** + * {@inheritdoc} + */ + public function parse($name) + { + if ($name instanceof TemplateReferenceInterface) { + return $name; + } + + $parts = explode('/', str_replace('\\', '/', $name)); + + $elements = explode('.', array_pop($parts)); + if (3 > \count($elements)) { + return false; + } + $engine = array_pop($elements); + $format = array_pop($elements); + + return new TemplateReference('', implode('/', $parts), implode('.', $elements), $format, $engine); + } +} diff --git a/vendor/symfony/framework-bundle/Templating/TemplateNameParser.php b/vendor/symfony/framework-bundle/Templating/TemplateNameParser.php new file mode 100644 index 0000000..f1e54af --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/TemplateNameParser.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Templating\TemplateNameParser as BaseTemplateNameParser; +use Symfony\Component\Templating\TemplateReferenceInterface; + +/** + * TemplateNameParser converts template names from the short notation + * "bundle:section:template.format.engine" to TemplateReferenceInterface + * instances. + * + * @author Fabien Potencier + */ +class TemplateNameParser extends BaseTemplateNameParser +{ + protected $kernel; + protected $cache = array(); + + public function __construct(KernelInterface $kernel) + { + $this->kernel = $kernel; + } + + /** + * {@inheritdoc} + */ + public function parse($name) + { + if ($name instanceof TemplateReferenceInterface) { + return $name; + } elseif (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + // normalize name + $name = str_replace(':/', ':', preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name))); + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('Template name "%s" contains invalid characters.', $name)); + } + + if ($this->isAbsolutePath($name) || !preg_match('/^(?:([^:]*):([^:]*):)?(.+)\.([^\.]+)\.([^\.]+)$/', $name, $matches) || 0 === strpos($name, '@')) { + return parent::parse($name); + } + + $template = new TemplateReference($matches[1], $matches[2], $matches[3], $matches[4], $matches[5]); + + if ($template->get('bundle')) { + try { + $this->kernel->getBundle($template->get('bundle')); + } catch (\Exception $e) { + throw new \InvalidArgumentException(sprintf('Template name "%s" is not valid.', $name), 0, $e); + } + } + + return $this->cache[$name] = $template; + } + + private function isAbsolutePath($file) + { + $isAbsolute = (bool) preg_match('#^(?:/|[a-zA-Z]:)#', $file); + + if ($isAbsolute) { + @trigger_error('Absolute template path support is deprecated since Symfony 3.1 and will be removed in 4.0.', E_USER_DEPRECATED); + } + + return $isAbsolute; + } +} diff --git a/vendor/symfony/framework-bundle/Templating/TemplateReference.php b/vendor/symfony/framework-bundle/Templating/TemplateReference.php new file mode 100644 index 0000000..d9bc982 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/TemplateReference.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\Bundle\FrameworkBundle\Templating; + +use Symfony\Component\Templating\TemplateReference as BaseTemplateReference; + +/** + * Internal representation of a template. + * + * @author Victor Berchet + */ +class TemplateReference extends BaseTemplateReference +{ + public function __construct($bundle = null, $controller = null, $name = null, $format = null, $engine = null) + { + $this->parameters = array( + 'bundle' => $bundle, + 'controller' => $controller, + 'name' => $name, + 'format' => $format, + 'engine' => $engine, + ); + } + + /** + * Returns the path to the template + * - as a path when the template is not part of a bundle + * - as a resource when the template is part of a bundle. + * + * @return string A path to the template or a resource + */ + public function getPath() + { + $controller = str_replace('\\', '/', $this->get('controller')); + + $path = (empty($controller) ? '' : $controller.'/').$this->get('name').'.'.$this->get('format').'.'.$this->get('engine'); + + return empty($this->parameters['bundle']) ? 'views/'.$path : '@'.$this->get('bundle').'/Resources/views/'.$path; + } + + /** + * {@inheritdoc} + */ + public function getLogicalName() + { + return sprintf('%s:%s:%s.%s.%s', $this->parameters['bundle'], $this->parameters['controller'], $this->parameters['name'], $this->parameters['format'], $this->parameters['engine']); + } +} diff --git a/vendor/symfony/framework-bundle/Templating/TimedPhpEngine.php b/vendor/symfony/framework-bundle/Templating/TimedPhpEngine.php new file mode 100644 index 0000000..3263ea2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Templating/TimedPhpEngine.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\Bundle\FrameworkBundle\Templating; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Stopwatch\Stopwatch; +use Symfony\Component\Templating\Loader\LoaderInterface; +use Symfony\Component\Templating\TemplateNameParserInterface; + +/** + * Times the time spent to render a template. + * + * @author Fabien Potencier + */ +class TimedPhpEngine extends PhpEngine +{ + protected $stopwatch; + + public function __construct(TemplateNameParserInterface $parser, ContainerInterface $container, LoaderInterface $loader, Stopwatch $stopwatch, GlobalVariables $globals = null) + { + parent::__construct($parser, $container, $loader, $globals); + + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function render($name, array $parameters = array()) + { + $e = $this->stopwatch->start(sprintf('template.php (%s)', $name), 'template'); + + $ret = parent::render($name, $parameters); + + $e->stop(); + + return $ret; + } +} diff --git a/vendor/symfony/framework-bundle/Test/KernelTestCase.php b/vendor/symfony/framework-bundle/Test/KernelTestCase.php new file mode 100644 index 0000000..4febf50 --- /dev/null +++ b/vendor/symfony/framework-bundle/Test/KernelTestCase.php @@ -0,0 +1,231 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Test; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ResettableContainerInterface; +use Symfony\Component\Finder\Finder; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * KernelTestCase is the base class for tests needing a Kernel. + * + * @author Fabien Potencier + */ +abstract class KernelTestCase extends TestCase +{ + protected static $class; + + /** + * @var KernelInterface + */ + protected static $kernel; + + /** + * Finds the directory where the phpunit.xml(.dist) is stored. + * + * If you run tests with the PHPUnit CLI tool, everything will work as expected. + * If not, override this method in your test classes. + * + * @return string The directory where phpunit.xml(.dist) is stored + * + * @throws \RuntimeException + * + * @deprecated since 3.4 and will be removed in 4.0. + */ + protected static function getPhpUnitXmlDir() + { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); + + if (!isset($_SERVER['argv']) || false === strpos($_SERVER['argv'][0], 'phpunit')) { + throw new \RuntimeException('You must override the KernelTestCase::createKernel() method.'); + } + + $dir = static::getPhpUnitCliConfigArgument(); + if (null === $dir && + (is_file(getcwd().\DIRECTORY_SEPARATOR.'phpunit.xml') || + is_file(getcwd().\DIRECTORY_SEPARATOR.'phpunit.xml.dist'))) { + $dir = getcwd(); + } + + // Can't continue + if (null === $dir) { + throw new \RuntimeException('Unable to guess the Kernel directory.'); + } + + if (!is_dir($dir)) { + $dir = \dirname($dir); + } + + return $dir; + } + + /** + * Finds the value of the CLI configuration option. + * + * PHPUnit will use the last configuration argument on the command line, so this only returns + * the last configuration argument. + * + * @return string The value of the PHPUnit CLI configuration option + * + * @deprecated since 3.4 and will be removed in 4.0. + */ + private static function getPhpUnitCliConfigArgument() + { + @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.4 and will be removed in 4.0.', __METHOD__), E_USER_DEPRECATED); + + $dir = null; + $reversedArgs = array_reverse($_SERVER['argv']); + foreach ($reversedArgs as $argIndex => $testArg) { + if (preg_match('/^-[^ \-]*c$/', $testArg) || '--configuration' === $testArg) { + $dir = realpath($reversedArgs[$argIndex - 1]); + break; + } elseif (0 === strpos($testArg, '--configuration=')) { + $argPath = substr($testArg, \strlen('--configuration=')); + $dir = realpath($argPath); + break; + } elseif (0 === strpos($testArg, '-c')) { + $argPath = substr($testArg, \strlen('-c')); + $dir = realpath($argPath); + break; + } + } + + return $dir; + } + + /** + * Attempts to guess the kernel location. + * + * When the Kernel is located, the file is required. + * + * @return string The Kernel class name + * + * @throws \RuntimeException + */ + protected static function getKernelClass() + { + if (isset($_SERVER['KERNEL_CLASS']) || isset($_ENV['KERNEL_CLASS'])) { + $class = isset($_ENV['KERNEL_CLASS']) ? $_ENV['KERNEL_CLASS'] : $_SERVER['KERNEL_CLASS']; + if (!class_exists($class)) { + throw new \RuntimeException(sprintf('Class "%s" doesn\'t exist or cannot be autoloaded. Check that the KERNEL_CLASS value in phpunit.xml matches the fully-qualified class name of your Kernel or override the %s::createKernel() method.', $class, static::class)); + } + + return $class; + } else { + @trigger_error(sprintf('Using the KERNEL_DIR environment variable or the automatic guessing based on the phpunit.xml / phpunit.xml.dist file location is deprecated since Symfony 3.4. Set the KERNEL_CLASS environment variable to the fully-qualified class name of your Kernel instead. Not setting the KERNEL_CLASS environment variable will throw an exception on 4.0 unless you override the %1$::createKernel() or %1$::getKernelClass() method.', static::class), E_USER_DEPRECATED); + } + + if (isset($_SERVER['KERNEL_DIR']) || isset($_ENV['KERNEL_DIR'])) { + $dir = isset($_ENV['KERNEL_DIR']) ? $_ENV['KERNEL_DIR'] : $_SERVER['KERNEL_DIR']; + + if (!is_dir($dir)) { + $phpUnitDir = static::getPhpUnitXmlDir(); + if (is_dir("$phpUnitDir/$dir")) { + $dir = "$phpUnitDir/$dir"; + } + } + } else { + $dir = static::getPhpUnitXmlDir(); + } + + $finder = new Finder(); + $finder->name('*Kernel.php')->depth(0)->in($dir); + $results = iterator_to_array($finder); + if (!\count($results)) { + throw new \RuntimeException('Either set KERNEL_DIR in your phpunit.xml according to https://symfony.com/doc/current/book/testing.html#your-first-functional-test or override the WebTestCase::createKernel() method.'); + } + + $file = current($results); + $class = $file->getBasename('.php'); + + require_once $file; + + return $class; + } + + /** + * Boots the Kernel for this test. + * + * @return KernelInterface A KernelInterface instance + */ + protected static function bootKernel(array $options = array()) + { + static::ensureKernelShutdown(); + + static::$kernel = static::createKernel($options); + static::$kernel->boot(); + + return static::$kernel; + } + + /** + * Creates a Kernel. + * + * Available options: + * + * * environment + * * debug + * + * @return KernelInterface A KernelInterface instance + */ + protected static function createKernel(array $options = array()) + { + if (null === static::$class) { + static::$class = static::getKernelClass(); + } + + if (isset($options['environment'])) { + $env = $options['environment']; + } elseif (isset($_ENV['APP_ENV'])) { + $env = $_ENV['APP_ENV']; + } elseif (isset($_SERVER['APP_ENV'])) { + $env = $_SERVER['APP_ENV']; + } else { + $env = 'test'; + } + + if (isset($options['debug'])) { + $debug = $options['debug']; + } elseif (isset($_ENV['APP_DEBUG'])) { + $debug = $_ENV['APP_DEBUG']; + } elseif (isset($_SERVER['APP_DEBUG'])) { + $debug = $_SERVER['APP_DEBUG']; + } else { + $debug = true; + } + + return new static::$class($env, $debug); + } + + /** + * Shuts the kernel down if it was used in the test. + */ + protected static function ensureKernelShutdown() + { + if (null !== static::$kernel) { + $container = static::$kernel->getContainer(); + static::$kernel->shutdown(); + if ($container instanceof ResettableContainerInterface) { + $container->reset(); + } + } + } + + /** + * Clean up Kernel usage in this test. + */ + protected function tearDown() + { + static::ensureKernelShutdown(); + } +} diff --git a/vendor/symfony/framework-bundle/Test/WebTestCase.php b/vendor/symfony/framework-bundle/Test/WebTestCase.php new file mode 100644 index 0000000..b5aaa26 --- /dev/null +++ b/vendor/symfony/framework-bundle/Test/WebTestCase.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\Bundle\FrameworkBundle\Test; + +use Symfony\Bundle\FrameworkBundle\Client; + +/** + * WebTestCase is the base class for functional tests. + * + * @author Fabien Potencier + */ +abstract class WebTestCase extends KernelTestCase +{ + /** + * Creates a Client. + * + * @param array $options An array of options to pass to the createKernel method + * @param array $server An array of server parameters + * + * @return Client A Client instance + */ + protected static function createClient(array $options = array(), array $server = array()) + { + $kernel = static::bootKernel($options); + + $client = $kernel->getContainer()->get('test.client'); + $client->setServerParameters($server); + + return $client; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php b/vendor/symfony/framework-bundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php new file mode 100644 index 0000000..b32274e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/CacheWarmer/AnnotationsCacheWarmerTest.php @@ -0,0 +1,103 @@ +cacheDir = sys_get_temp_dir().'/'.uniqid(); + $fs = new Filesystem(); + $fs->mkdir($this->cacheDir); + parent::setUp(); + } + + protected function tearDown() + { + $fs = new Filesystem(); + $fs->remove($this->cacheDir); + parent::tearDown(); + } + + public function testAnnotationsCacheWarmerWithDebugDisabled() + { + file_put_contents($this->cacheDir.'/annotations.map', sprintf('cacheDir, __FUNCTION__); + $reader = new AnnotationReader(); + $fallbackPool = new ArrayAdapter(); + $warmer = new AnnotationsCacheWarmer( + $reader, + $cacheFile, + $fallbackPool, + null + ); + $warmer->warmUp($this->cacheDir); + $this->assertFileExists($cacheFile); + + // Assert cache is valid + $reader = new CachedReader( + $this->getReadOnlyReader(), + new DoctrineProvider(new PhpArrayAdapter($cacheFile, new NullAdapter())) + ); + $refClass = new \ReflectionClass($this); + $reader->getClassAnnotations($refClass); + $reader->getMethodAnnotations($refClass->getMethod(__FUNCTION__)); + $reader->getPropertyAnnotations($refClass->getProperty('cacheDir')); + } + + public function testAnnotationsCacheWarmerWithDebugEnabled() + { + file_put_contents($this->cacheDir.'/annotations.map', sprintf('cacheDir, __FUNCTION__); + $reader = new AnnotationReader(); + $fallbackPool = new ArrayAdapter(); + $warmer = new AnnotationsCacheWarmer( + $reader, + $cacheFile, + $fallbackPool, + null, + true + ); + $warmer->warmUp($this->cacheDir); + $this->assertFileExists($cacheFile); + // Assert cache is valid + $reader = new CachedReader( + $this->getReadOnlyReader(), + new DoctrineProvider(new PhpArrayAdapter($cacheFile, new NullAdapter())), + true + ); + $refClass = new \ReflectionClass($this); + $reader->getClassAnnotations($refClass); + $reader->getMethodAnnotations($refClass->getMethod(__FUNCTION__)); + $reader->getPropertyAnnotations($refClass->getProperty('cacheDir')); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|Reader + */ + private function getReadOnlyReader() + { + $readerMock = $this->getMockBuilder('Doctrine\Common\Annotations\Reader')->getMock(); + $readerMock->expects($this->exactly(0))->method('getClassAnnotations'); + $readerMock->expects($this->exactly(0))->method('getClassAnnotation'); + $readerMock->expects($this->exactly(0))->method('getMethodAnnotations'); + $readerMock->expects($this->exactly(0))->method('getMethodAnnotation'); + $readerMock->expects($this->exactly(0))->method('getPropertyAnnotations'); + $readerMock->expects($this->exactly(0))->method('getPropertyAnnotation'); + + return $readerMock; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php b/vendor/symfony/framework-bundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.php new file mode 100644 index 0000000..5e442d6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/CacheWarmer/ClassCacheCacheWarmerTest.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\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\CacheWarmer\ClassCacheCacheWarmer; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\DeclaredClass; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\WarmedClass; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +/** + * @group legacy + */ +class ClassCacheCacheWarmerTest extends TestCase +{ + public function testWithDeclaredClasses() + { + $this->assertTrue(class_exists(WarmedClass::class, true)); + + $dir = sys_get_temp_dir(); + @unlink($dir.'/classes.php'); + file_put_contents($dir.'/classes.map', sprintf('warmUp($dir); + + $this->assertSame(<<<'EOTXT' + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\CacheWarmer\SerializerCacheWarmer; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\NullAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; +use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; +use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; +use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; + +class SerializerCacheWarmerTest extends TestCase +{ + public function testWarmUp() + { + if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) { + $this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.'); + } + + $loaders = array( + new XmlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/person.xml'), + new YamlFileLoader(__DIR__.'/../Fixtures/Serialization/Resources/author.yml'), + ); + + $file = sys_get_temp_dir().'/cache-serializer.php'; + @unlink($file); + + $fallbackPool = new ArrayAdapter(); + + $warmer = new SerializerCacheWarmer($loaders, $file, $fallbackPool); + $warmer->warmUp(\dirname($file)); + + $this->assertFileExists($file); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Person')->isHit()); + $this->assertTrue($arrayPool->getItem('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author')->isHit()); + + $values = $fallbackPool->getValues(); + + $this->assertInternalType('array', $values); + $this->assertCount(2, $values); + $this->assertArrayHasKey('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Person', $values); + $this->assertArrayHasKey('Symfony_Bundle_FrameworkBundle_Tests_Fixtures_Serialization_Author', $values); + } + + public function testWarmUpWithoutLoader() + { + if (!class_exists(CacheClassMetadataFactory::class) || !method_exists(XmlFileLoader::class, 'getMappedClasses') || !method_exists(YamlFileLoader::class, 'getMappedClasses')) { + $this->markTestSkipped('The Serializer default cache warmer has been introduced in the Serializer Component version 3.2.'); + } + + $file = sys_get_temp_dir().'/cache-serializer-without-loader.php'; + @unlink($file); + + $fallbackPool = new ArrayAdapter(); + + $warmer = new SerializerCacheWarmer(array(), $file, $fallbackPool); + $warmer->warmUp(\dirname($file)); + + $this->assertFileExists($file); + + $values = $fallbackPool->getValues(); + + $this->assertInternalType('array', $values); + $this->assertCount(0, $values); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplateFinderTest.php b/vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplateFinderTest.php new file mode 100644 index 0000000..5da592c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplateFinderTest.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\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinder; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser; +use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\BaseBundle\BaseBundle; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TemplateFinderTest extends TestCase +{ + public function testFindAllTemplates() + { + $kernel = $this + ->getMockBuilder('Symfony\Component\HttpKernel\Kernel') + ->disableOriginalConstructor() + ->getMock() + ; + + $kernel + ->expects($this->any()) + ->method('getBundle') + ; + + $kernel + ->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array('BaseBundle' => new BaseBundle()))) + ; + + $parser = new TemplateFilenameParser(); + + $finder = new TemplateFinder($kernel, $parser, __DIR__.'/../Fixtures/Resources'); + + $templates = array_map( + function ($template) { return $template->getLogicalName(); }, + $finder->findAllTemplates() + ); + + $this->assertCount(7, $templates, '->findAllTemplates() find all templates in the bundles and global folders'); + $this->assertContains('BaseBundle::base.format.engine', $templates); + $this->assertContains('BaseBundle::this.is.a.template.format.engine', $templates); + $this->assertContains('BaseBundle:controller:base.format.engine', $templates); + $this->assertContains('BaseBundle:controller:custom.format.engine', $templates); + $this->assertContains('::this.is.a.template.format.engine', $templates); + $this->assertContains('::resource.format.engine', $templates); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php b/vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.php new file mode 100644 index 0000000..75d5934 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/CacheWarmer/TemplatePathsCacheWarmerTest.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\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface; +use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplatePathsCacheWarmer; +use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Filesystem\Filesystem; + +class TemplatePathsCacheWarmerTest extends TestCase +{ + /** @var Filesystem */ + private $filesystem; + + /** @var TemplateFinderInterface */ + private $templateFinder; + + /** @var FileLocator */ + private $fileLocator; + + /** @var TemplateLocator */ + private $templateLocator; + + private $tmpDir; + + protected function setUp() + { + $this->templateFinder = $this + ->getMockBuilder(TemplateFinderInterface::class) + ->setMethods(array('findAllTemplates')) + ->getMock(); + + $this->fileLocator = $this + ->getMockBuilder(FileLocator::class) + ->setMethods(array('locate')) + ->setConstructorArgs(array('/path/to/fallback')) + ->getMock(); + + $this->templateLocator = new TemplateLocator($this->fileLocator); + + $this->tmpDir = sys_get_temp_dir().\DIRECTORY_SEPARATOR.uniqid('cache_template_paths_', true); + + $this->filesystem = new Filesystem(); + $this->filesystem->mkdir($this->tmpDir); + } + + protected function tearDown() + { + $this->filesystem->remove($this->tmpDir); + } + + public function testWarmUp() + { + $template = new TemplateReference('bundle', 'controller', 'name', 'format', 'engine'); + + $this->templateFinder + ->expects($this->once()) + ->method('findAllTemplates') + ->will($this->returnValue(array($template))); + + $this->fileLocator + ->expects($this->once()) + ->method('locate') + ->with($template->getPath()) + ->will($this->returnValue(\dirname($this->tmpDir).'/path/to/template.html.twig')); + + $warmer = new TemplatePathsCacheWarmer($this->templateFinder, $this->templateLocator); + $warmer->warmUp($this->tmpDir); + + $this->assertFileEquals(__DIR__.'/../Fixtures/TemplatePathsCache/templates.php', $this->tmpDir.'/templates.php'); + } + + public function testWarmUpEmpty() + { + $this->templateFinder + ->expects($this->once()) + ->method('findAllTemplates') + ->will($this->returnValue(array())); + + $this->fileLocator + ->expects($this->never()) + ->method('locate'); + + $warmer = new TemplatePathsCacheWarmer($this->templateFinder, $this->templateLocator); + $warmer->warmUp($this->tmpDir); + + $this->assertFileExists($this->tmpDir.'/templates.php'); + $this->assertSame(file_get_contents(__DIR__.'/../Fixtures/TemplatePathsCache/templates-empty.php'), file_get_contents($this->tmpDir.'/templates.php')); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php b/vendor/symfony/framework-bundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.php new file mode 100644 index 0000000..47c88f1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/CacheWarmer/ValidatorCacheWarmerTest.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\Bundle\FrameworkBundle\Tests\CacheWarmer; + +use Symfony\Bundle\FrameworkBundle\CacheWarmer\ValidatorCacheWarmer; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\NullAdapter; +use Symfony\Component\Cache\Adapter\PhpArrayAdapter; +use Symfony\Component\Validator\Mapping\ClassMetadata; +use Symfony\Component\Validator\ValidatorBuilder; + +class ValidatorCacheWarmerTest extends TestCase +{ + public function testWarmUp() + { + $validatorBuilder = new ValidatorBuilder(); + $validatorBuilder->addXmlMapping(__DIR__.'/../Fixtures/Validation/Resources/person.xml'); + $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/author.yml'); + $validatorBuilder->addMethodMapping('loadValidatorMetadata'); + $validatorBuilder->enableAnnotationMapping(); + + $file = sys_get_temp_dir().'/cache-validator.php'; + @unlink($file); + + $fallbackPool = new ArrayAdapter(); + + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool); + $warmer->warmUp(\dirname($file)); + + $this->assertFileExists($file); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Person')->isHit()); + $this->assertTrue($arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author')->isHit()); + + $values = $fallbackPool->getValues(); + + $this->assertInternalType('array', $values); + $this->assertCount(2, $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Person', $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Author', $values); + } + + public function testWarmUpWithAnnotations() + { + $validatorBuilder = new ValidatorBuilder(); + $validatorBuilder->addYamlMapping(__DIR__.'/../Fixtures/Validation/Resources/categories.yml'); + $validatorBuilder->enableAnnotationMapping(); + + $file = sys_get_temp_dir().'/cache-validator-with-annotations.php'; + @unlink($file); + + $fallbackPool = new ArrayAdapter(); + + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool); + $warmer->warmUp(\dirname($file)); + + $this->assertFileExists($file); + + $arrayPool = new PhpArrayAdapter($file, new NullAdapter()); + + $item = $arrayPool->getItem('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category'); + $this->assertTrue($item->isHit()); + + $this->assertInstanceOf(ClassMetadata::class, $item->get()); + + $values = $fallbackPool->getValues(); + + $this->assertInternalType('array', $values); + $this->assertCount(2, $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.Category', $values); + $this->assertArrayHasKey('Symfony.Bundle.FrameworkBundle.Tests.Fixtures.Validation.SubCategory', $values); + } + + public function testWarmUpWithoutLoader() + { + $validatorBuilder = new ValidatorBuilder(); + + $file = sys_get_temp_dir().'/cache-validator-without-loaders.php'; + @unlink($file); + + $fallbackPool = new ArrayAdapter(); + + $warmer = new ValidatorCacheWarmer($validatorBuilder, $file, $fallbackPool); + $warmer->warmUp(\dirname($file)); + + $this->assertFileExists($file); + + $values = $fallbackPool->getValues(); + + $this->assertInternalType('array', $values); + $this->assertCount(0, $values); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/ClientTest.php b/vendor/symfony/framework-bundle/Tests/ClientTest.php new file mode 100644 index 0000000..d021b0a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/ClientTest.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\Bundle\FrameworkBundle\Tests; + +use Symfony\Bundle\FrameworkBundle\Client; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\WebTestCase; +use Symfony\Component\HttpFoundation\Response; + +class ClientTest extends WebTestCase +{ + public function testRebootKernelBetweenRequests() + { + $mock = $this->getKernelMock(); + $mock->expects($this->once())->method('shutdown'); + + $client = new Client($mock); + $client->request('GET', '/'); + $client->request('GET', '/'); + } + + public function testDisabledRebootKernel() + { + $mock = $this->getKernelMock(); + $mock->expects($this->never())->method('shutdown'); + + $client = new Client($mock); + $client->disableReboot(); + $client->request('GET', '/'); + $client->request('GET', '/'); + } + + public function testEnableRebootKernel() + { + $mock = $this->getKernelMock(); + $mock->expects($this->once())->method('shutdown'); + + $client = new Client($mock); + $client->disableReboot(); + $client->request('GET', '/'); + $client->request('GET', '/'); + $client->enableReboot(); + $client->request('GET', '/'); + } + + private function getKernelMock() + { + $mock = $this->getMockBuilder($this->getKernelClass()) + ->setMethods(array('shutdown', 'boot', 'handle')) + ->disableOriginalConstructor() + ->getMock(); + + $mock->expects($this->any())->method('handle')->willReturn(new Response('foo')); + + return $mock; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.php new file mode 100644 index 0000000..e2104d3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/CacheClearCommandTest.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\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture\TestAppKernel; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\Config\Resource\ResourceInterface; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Finder\Finder; + +class CacheClearCommandTest extends TestCase +{ + /** @var TestAppKernel */ + private $kernel; + /** @var Filesystem */ + private $fs; + private $rootDir; + + protected function setUp() + { + $this->fs = new Filesystem(); + $this->kernel = new TestAppKernel('test', true); + $this->rootDir = sys_get_temp_dir().\DIRECTORY_SEPARATOR.uniqid('sf2_cache_', true); + $this->kernel->setRootDir($this->rootDir); + $this->fs->mkdir($this->rootDir); + } + + protected function tearDown() + { + $this->fs->remove($this->rootDir); + } + + public function testCacheIsFreshAfterCacheClearedWithWarmup() + { + $input = new ArrayInput(array('cache:clear')); + $application = new Application($this->kernel); + $application->setCatchExceptions(false); + + $application->doRun($input, new NullOutput()); + + // Ensure that all *.meta files are fresh + $finder = new Finder(); + $metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta'); + // simply check that cache is warmed up + $this->assertNotEmpty($metaFiles); + $configCacheFactory = new ConfigCacheFactory(true); + + foreach ($metaFiles as $file) { + $configCacheFactory->cache(substr($file, 0, -5), function () use ($file) { + $this->fail(sprintf('Meta file "%s" is not fresh', (string) $file)); + }); + } + + // check that app kernel file present in meta file of container's cache + $containerClass = $this->kernel->getContainer()->getParameter('kernel.container_class'); + $containerRef = new \ReflectionClass($containerClass); + $containerFile = \dirname(\dirname($containerRef->getFileName())).'/'.$containerClass.'.php'; + $containerMetaFile = $containerFile.'.meta'; + $kernelRef = new \ReflectionObject($this->kernel); + $kernelFile = $kernelRef->getFileName(); + /** @var ResourceInterface[] $meta */ + $meta = unserialize(file_get_contents($containerMetaFile)); + $found = false; + foreach ($meta as $resource) { + if ((string) $resource === $kernelFile) { + $found = true; + break; + } + } + $this->assertTrue($found, 'Kernel file should present as resource'); + + if (\defined('HHVM_VERSION')) { + return; + } + $containerRef = new \ReflectionClass(require $containerFile); + $containerFile = str_replace('tes_'.\DIRECTORY_SEPARATOR, 'test'.\DIRECTORY_SEPARATOR, $containerRef->getFileName()); + $this->assertRegExp(sprintf('/\'kernel.container_class\'\s*=>\s*\'%s\'/', $containerClass), file_get_contents($containerFile), 'kernel.container_class is properly set on the dumped container'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.php b/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.php new file mode 100644 index 0000000..213d75e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/TestAppKernel.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\Bundle\FrameworkBundle\Tests\Command\CacheClearCommand\Fixture; + +use Psr\Log\NullLogger; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Kernel; + +class TestAppKernel extends Kernel +{ + public function registerBundles() + { + return array( + new FrameworkBundle(), + ); + } + + public function setRootDir($rootDir) + { + $this->rootDir = $rootDir; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load(__DIR__.\DIRECTORY_SEPARATOR.'config.yml'); + } + + protected function build(ContainerBuilder $container) + { + $container->register('logger', NullLogger::class); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/config.yml b/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/config.yml new file mode 100644 index 0000000..68f8d04 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/CacheClearCommand/Fixture/config.yml @@ -0,0 +1,2 @@ +framework: + secret: test diff --git a/vendor/symfony/framework-bundle/Tests/Command/CachePruneCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/CachePruneCommandTest.php new file mode 100644 index 0000000..5356f4a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/CachePruneCommandTest.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\Bundle\FrameworkBundle\Tests\Command; + +use Symfony\Bundle\FrameworkBundle\Command\CachePoolPruneCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Cache\PruneableInterface; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\HttpKernel\KernelInterface; + +class CachePruneCommandTest extends TestCase +{ + public function testCommandWithPools() + { + $tester = $this->getCommandTester($this->getKernel(), $this->getRewindableGenerator()); + $tester->execute(array()); + } + + public function testCommandWithNoPools() + { + $tester = $this->getCommandTester($this->getKernel(), $this->getEmptyRewindableGenerator()); + $tester->execute(array()); + } + + /** + * @return RewindableGenerator + */ + private function getRewindableGenerator() + { + return new RewindableGenerator(function () { + yield 'foo_pool' => $this->getPruneableInterfaceMock(); + yield 'bar_pool' => $this->getPruneableInterfaceMock(); + }, 2); + } + + /** + * @return RewindableGenerator + */ + private function getEmptyRewindableGenerator() + { + return new RewindableGenerator(function () { + return new \ArrayIterator(array()); + }, 0); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|KernelInterface + */ + private function getKernel() + { + $container = $this + ->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface') + ->getMock(); + + $kernel = $this + ->getMockBuilder(KernelInterface::class) + ->getMock(); + + $kernel + ->expects($this->any()) + ->method('getContainer') + ->willReturn($container); + + $kernel + ->expects($this->once()) + ->method('getBundles') + ->willReturn(array()); + + return $kernel; + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject|PruneableInterface + */ + private function getPruneableInterfaceMock() + { + $pruneable = $this + ->getMockBuilder(PruneableInterface::class) + ->getMock(); + + $pruneable + ->expects($this->atLeastOnce()) + ->method('prune'); + + return $pruneable; + } + + /** + * @return CommandTester + */ + private function getCommandTester(KernelInterface $kernel, RewindableGenerator $generator) + { + $application = new Application($kernel); + $application->add(new CachePoolPruneCommand($generator)); + + return new CommandTester($application->find('cache:pool:prune')); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/RouterDebugCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/RouterDebugCommandTest.php new file mode 100644 index 0000000..8422c02 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/RouterDebugCommandTest.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RouterDebugCommandTest extends TestCase +{ + public function testDebugAllRoutes() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => null), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Name Method Scheme Host Path', $tester->getDisplay()); + } + + public function testDebugSingleRoute() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'foo'), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Route Name | foo', $tester->getDisplay()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDebugInvalidRoute() + { + $this->createCommandTester()->execute(array('name' => 'test')); + } + + /** + * @group legacy + * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand::__construct() expects an instance of "Symfony\Component\Routing\RouterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. + */ + public function testLegacyDebugCommand() + { + $application = new Application($this->getKernel()); + $application->add(new RouterDebugCommand()); + + $tester = new CommandTester($application->find('debug:router')); + + $tester->execute(array()); + + $this->assertRegExp('/foo\s+ANY\s+ANY\s+ANY\s+\\/foo/', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $application = new Application($this->getKernel()); + $application->add(new RouterDebugCommand($this->getRouter())); + + return new CommandTester($application->find('debug:router')); + } + + private function getRouter() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('foo', new Route('foo')); + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router + ->expects($this->any()) + ->method('getRouteCollection') + ->will($this->returnValue($routeCollection)); + + return $router; + } + + private function getKernel() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container + ->expects($this->atLeastOnce()) + ->method('has') + ->will($this->returnCallback(function ($id) { + if ('console.command_loader' === $id) { + return false; + } + + return true; + })) + ; + $container + ->expects($this->any()) + ->method('get') + ->with('router') + ->willReturn($this->getRouter()) + ; + + $kernel = $this->getMockBuilder(KernelInterface::class)->getMock(); + $kernel + ->expects($this->any()) + ->method('getContainer') + ->willReturn($container) + ; + $kernel + ->expects($this->once()) + ->method('getBundles') + ->willReturn(array()) + ; + + return $kernel; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/RouterMatchCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/RouterMatchCommandTest.php new file mode 100644 index 0000000..0b07bc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/RouterMatchCommandTest.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\Bundle\FrameworkBundle\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand; +use Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RouterMatchCommandTest extends TestCase +{ + public function testWithMatchPath() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('path_info' => '/foo', 'foo'), array('decorated' => false)); + + $this->assertEquals(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('Route Name | foo', $tester->getDisplay()); + } + + public function testWithNotMatchPath() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('path_info' => '/test', 'foo'), array('decorated' => false)); + + $this->assertEquals(1, $ret, 'Returns 1 in case of failure'); + $this->assertContains('None of the routes match the path "/test"', $tester->getDisplay()); + } + + /** + * @group legacy + * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\RouterMatchCommand::__construct() expects an instance of "Symfony\Component\Routing\RouterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. + * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\RouterDebugCommand::__construct() expects an instance of "Symfony\Component\Routing\RouterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. + */ + public function testLegacyMatchCommand() + { + $application = new Application($this->getKernel()); + $application->add(new RouterMatchCommand()); + $application->add(new RouterDebugCommand()); + + $tester = new CommandTester($application->find('router:match')); + + $tester->execute(array('path_info' => '/')); + + $this->assertContains('None of the routes match the path "/"', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $application = new Application($this->getKernel()); + $application->add(new RouterMatchCommand($this->getRouter())); + $application->add(new RouterDebugCommand($this->getRouter())); + + return new CommandTester($application->find('router:match')); + } + + private function getRouter() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('foo', new Route('foo')); + $requestContext = new RequestContext(); + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router + ->expects($this->any()) + ->method('getRouteCollection') + ->will($this->returnValue($routeCollection)); + $router + ->expects($this->any()) + ->method('getContext') + ->will($this->returnValue($requestContext)); + + return $router; + } + + private function getKernel() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container + ->expects($this->atLeastOnce()) + ->method('has') + ->will($this->returnCallback(function ($id) { + if ('console.command_loader' === $id) { + return false; + } + + return true; + })) + ; + $container + ->expects($this->any()) + ->method('get') + ->with('router') + ->willReturn($this->getRouter()) + ; + + $kernel = $this->getMockBuilder(KernelInterface::class)->getMock(); + $kernel + ->expects($this->any()) + ->method('getContainer') + ->willReturn($container) + ; + $kernel + ->expects($this->once()) + ->method('getBundles') + ->willReturn(array()) + ; + + return $kernel; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/TranslationDebugCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/TranslationDebugCommandTest.php new file mode 100644 index 0000000..f08921a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/TranslationDebugCommandTest.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\Bundle\FrameworkBundle\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel; + +class TranslationDebugCommandTest extends TestCase +{ + private $fs; + private $translationDir; + + public function testDebugMissingMessages() + { + $tester = $this->createCommandTester(array('foo' => 'foo')); + $tester->execute(array('locale' => 'en', 'bundle' => 'foo')); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + } + + public function testDebugUnusedMessages() + { + $tester = $this->createCommandTester(array(), array('foo' => 'foo')); + $tester->execute(array('locale' => 'en', 'bundle' => 'foo')); + + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + public function testDebugFallbackMessages() + { + $tester = $this->createCommandTester(array(), array('foo' => 'foo')); + $tester->execute(array('locale' => 'fr', 'bundle' => 'foo')); + + $this->assertRegExp('/fallback/', $tester->getDisplay()); + } + + public function testNoDefinedMessages() + { + $tester = $this->createCommandTester(); + $tester->execute(array('locale' => 'fr', 'bundle' => 'test')); + + $this->assertRegExp('/No defined or extracted messages for locale "fr"/', $tester->getDisplay()); + } + + public function testDebugDefaultDirectory() + { + $tester = $this->createCommandTester(array('foo' => 'foo'), array('bar' => 'bar')); + $tester->execute(array('locale' => 'en')); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + public function testDebugDefaultRootDirectory() + { + $this->fs->remove($this->translationDir); + $this->fs = new Filesystem(); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/translations'); + $this->fs->mkdir($this->translationDir.'/templates'); + + $tester = $this->createCommandTester(array('foo' => 'foo'), array('bar' => 'bar')); + $tester->execute(array('locale' => 'en')); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + public function testDebugCustomDirectory() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel->expects($this->once()) + ->method('getBundle') + ->with($this->equalTo($this->translationDir)) + ->willThrowException(new \InvalidArgumentException()); + + $tester = $this->createCommandTester(array('foo' => 'foo'), array('bar' => 'bar'), $kernel); + $tester->execute(array('locale' => 'en', 'bundle' => $this->translationDir)); + + $this->assertRegExp('/missing/', $tester->getDisplay()); + $this->assertRegExp('/unused/', $tester->getDisplay()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testDebugInvalidDirectory() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel->expects($this->once()) + ->method('getBundle') + ->with($this->equalTo('dir')) + ->will($this->throwException(new \InvalidArgumentException())); + + $tester = $this->createCommandTester(array(), array(), $kernel); + $tester->execute(array('locale' => 'en', 'bundle' => 'dir')); + } + + protected function setUp() + { + $this->fs = new Filesystem(); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/Resources/translations'); + $this->fs->mkdir($this->translationDir.'/Resources/views'); + $this->fs->mkdir($this->translationDir.'/translations'); + $this->fs->mkdir($this->translationDir.'/templates'); + } + + protected function tearDown() + { + $this->fs->remove($this->translationDir); + } + + /** + * @return CommandTester + */ + private function createCommandTester($extractedMessages = array(), $loadedMessages = array(), $kernel = null) + { + $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + + $translator + ->expects($this->any()) + ->method('getFallbackLocales') + ->will($this->returnValue(array('en'))); + + $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); + $extractor + ->expects($this->any()) + ->method('extract') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($extractedMessages) { + $catalogue->add($extractedMessages); + }) + ); + + $loader = $this->getMockBuilder('Symfony\Component\Translation\Reader\TranslationReader')->getMock(); + $loader + ->expects($this->any()) + ->method('read') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($loadedMessages) { + $catalogue->add($loadedMessages); + }) + ); + + if (null === $kernel) { + $returnValues = array( + array('foo', $this->getBundle($this->translationDir)), + array('test', $this->getBundle('test')), + ); + if (HttpKernel\Kernel::VERSION_ID < 40000) { + $returnValues = array( + array('foo', true, $this->getBundle($this->translationDir)), + array('test', true, $this->getBundle('test')), + ); + } + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnValueMap($returnValues)); + } + + $kernel + ->expects($this->any()) + ->method('getRootDir') + ->will($this->returnValue($this->translationDir)); + + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock())); + + $command = new TranslationDebugCommand($translator, $loader, $extractor, $this->translationDir.'/translations', $this->translationDir.'/templates'); + + $application = new Application($kernel); + $application->add($command); + + return new CommandTester($application->find('debug:translation')); + } + + /** + * @group legacy + * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand::__construct() expects an instance of "Symfony\Component\Translation\TranslatorInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. + */ + public function testLegacyDebugCommand() + { + $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); + $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader')->getMock(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container + ->expects($this->any()) + ->method('get') + ->will($this->returnValueMap(array( + array('translation.extractor', 1, $extractor), + array('translation.reader', 1, $loader), + array('translator', 1, $translator), + array('kernel', 1, $kernel), + ))); + + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($container)); + + $command = new TranslationDebugCommand(); + $command->setContainer($container); + + $application = new Application($kernel); + $application->add($command); + + $tester = new CommandTester($application->find('debug:translation')); + $tester->execute(array('locale' => 'en')); + + $this->assertContains('No defined or extracted', $tester->getDisplay()); + } + + private function getBundle($path) + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($path)) + ; + + return $bundle; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/TranslationUpdateCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/TranslationUpdateCommandTest.php new file mode 100644 index 0000000..e92c175 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/TranslationUpdateCommandTest.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\Bundle\FrameworkBundle\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel; + +class TranslationUpdateCommandTest extends TestCase +{ + private $fs; + private $translationDir; + + public function testDumpMessagesAndClean() + { + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true)); + $this->assertRegExp('/foo/', $tester->getDisplay()); + $this->assertRegExp('/1 message was successfully extracted/', $tester->getDisplay()); + } + + public function testDumpMessagesAndCleanInRootDirectory() + { + $this->fs->remove($this->translationDir); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/translations'); + $this->fs->mkdir($this->translationDir.'/templates'); + + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', '--dump-messages' => true, '--clean' => true)); + $this->assertRegExp('/foo/', $tester->getDisplay()); + $this->assertRegExp('/1 message was successfully extracted/', $tester->getDisplay()); + } + + public function testDumpTwoMessagesAndClean() + { + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo', 'bar' => 'bar'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true)); + $this->assertRegExp('/foo/', $tester->getDisplay()); + $this->assertRegExp('/bar/', $tester->getDisplay()); + $this->assertRegExp('/2 messages were successfully extracted/', $tester->getDisplay()); + } + + public function testDumpMessagesForSpecificDomain() + { + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo'), 'mydomain' => array('bar' => 'bar'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--dump-messages' => true, '--clean' => true, '--domain' => 'mydomain')); + $this->assertRegExp('/bar/', $tester->getDisplay()); + $this->assertRegExp('/1 message was successfully extracted/', $tester->getDisplay()); + } + + public function testWriteMessages() + { + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--force' => true)); + $this->assertRegExp('/Translation files were successfully updated./', $tester->getDisplay()); + } + + public function testWriteMessagesInRootDirectory() + { + $this->fs->remove($this->translationDir); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/translations'); + $this->fs->mkdir($this->translationDir.'/templates'); + + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', '--force' => true)); + $this->assertRegExp('/Translation files were successfully updated./', $tester->getDisplay()); + } + + public function testWriteMessagesForSpecificDomain() + { + $tester = $this->createCommandTester(array('messages' => array('foo' => 'foo'), 'mydomain' => array('bar' => 'bar'))); + $tester->execute(array('command' => 'translation:update', 'locale' => 'en', 'bundle' => 'foo', '--force' => true, '--domain' => 'mydomain')); + $this->assertRegExp('/Translation files were successfully updated./', $tester->getDisplay()); + } + + protected function setUp() + { + $this->fs = new Filesystem(); + $this->translationDir = sys_get_temp_dir().'/'.uniqid('sf2_translation', true); + $this->fs->mkdir($this->translationDir.'/Resources/translations'); + $this->fs->mkdir($this->translationDir.'/Resources/views'); + $this->fs->mkdir($this->translationDir.'/translations'); + $this->fs->mkdir($this->translationDir.'/templates'); + } + + protected function tearDown() + { + $this->fs->remove($this->translationDir); + } + + /** + * @return CommandTester + */ + private function createCommandTester($extractedMessages = array(), $loadedMessages = array(), HttpKernel\KernelInterface $kernel = null) + { + $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + + $translator + ->expects($this->any()) + ->method('getFallbackLocales') + ->will($this->returnValue(array('en'))); + + $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); + $extractor + ->expects($this->any()) + ->method('extract') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($extractedMessages) { + foreach ($extractedMessages as $domain => $messages) { + $catalogue->add($messages, $domain); + } + }) + ); + + $loader = $this->getMockBuilder('Symfony\Component\Translation\Reader\TranslationReader')->getMock(); + $loader + ->expects($this->any()) + ->method('read') + ->will( + $this->returnCallback(function ($path, $catalogue) use ($loadedMessages) { + $catalogue->add($loadedMessages); + }) + ); + + $writer = $this->getMockBuilder('Symfony\Component\Translation\Writer\TranslationWriter')->getMock(); + $writer + ->expects($this->any()) + ->method('getFormats') + ->will( + $this->returnValue(array('xlf', 'yml')) + ); + + if (null === $kernel) { + $returnValues = array( + array('foo', $this->getBundle($this->translationDir)), + array('test', $this->getBundle('test')), + ); + if (HttpKernel\Kernel::VERSION_ID < 40000) { + $returnValues = array( + array('foo', true, $this->getBundle($this->translationDir)), + array('test', true, $this->getBundle('test')), + ); + } + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnValueMap($returnValues)); + } + + $kernel + ->expects($this->any()) + ->method('getRootDir') + ->will($this->returnValue($this->translationDir)); + + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock())); + + $command = new TranslationUpdateCommand($writer, $loader, $extractor, 'en', $this->translationDir.'/translations', $this->translationDir.'/templates'); + + $application = new Application($kernel); + $application->add($command); + + return new CommandTester($application->find('translation:update')); + } + + /** + * @group legacy + * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\TranslationUpdateCommand::__construct() expects an instance of "Symfony\Component\Translation\Writer\TranslationWriterInterface" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. + */ + public function testLegacyUpdateCommand() + { + $translator = $this->getMockBuilder('Symfony\Component\Translation\Translator') + ->disableOriginalConstructor() + ->getMock(); + $extractor = $this->getMockBuilder('Symfony\Component\Translation\Extractor\ExtractorInterface')->getMock(); + $loader = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Translation\TranslationLoader')->getMock(); + $writer = $this->getMockBuilder('Symfony\Component\Translation\Writer\TranslationWriter')->getMock(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array())); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container + ->expects($this->any()) + ->method('get') + ->will($this->returnValueMap(array( + array('translation.extractor', 1, $extractor), + array('translation.reader', 1, $loader), + array('translation.writer', 1, $writer), + array('translator', 1, $translator), + array('kernel', 1, $kernel), + ))); + + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($container)); + + $command = new TranslationUpdateCommand(); + $command->setContainer($container); + + $application = new Application($kernel); + $application->add($command); + + $tester = new CommandTester($application->find('translation:update')); + $tester->execute(array('locale' => 'en')); + + $this->assertContains('You must choose one of --force or --dump-messages', $tester->getDisplay()); + } + + private function getBundle($path) + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($path)) + ; + + return $bundle; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Command/YamlLintCommandTest.php b/vendor/symfony/framework-bundle/Tests/Command/YamlLintCommandTest.php new file mode 100644 index 0000000..2daa2e3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Command/YamlLintCommandTest.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Command; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Command\YamlLintCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Application as BaseApplication; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\CommandTester; +use Symfony\Component\HttpKernel\KernelInterface; + +/** + * Tests the YamlLintCommand. + * + * @author Robin Chalas + */ +class YamlLintCommandTest extends TestCase +{ + private $files; + + public function testLintCorrectFile() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile('foo: bar'); + + $tester->execute( + array('filename' => $filename), + array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false) + ); + + $this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success'); + $this->assertContains('OK', trim($tester->getDisplay())); + } + + public function testLintIncorrectFile() + { + $incorrectContent = ' +foo: +bar'; + $tester = $this->createCommandTester(); + $filename = $this->createFile($incorrectContent); + + $tester->execute(array('filename' => $filename), array('decorated' => false)); + + $this->assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error'); + $this->assertContains('Unable to parse at line 3 (near "bar").', trim($tester->getDisplay())); + } + + /** + * @expectedException \RuntimeException + */ + public function testLintFileNotReadable() + { + $tester = $this->createCommandTester(); + $filename = $this->createFile(''); + unlink($filename); + + $tester->execute(array('filename' => $filename), array('decorated' => false)); + } + + public function testGetHelp() + { + $command = new YamlLintCommand(); + $expected = <<%command.name% command lints a YAML file and outputs to STDOUT +the first encountered syntax error. + +You can validates YAML contents passed from STDIN: + + cat filename | php %command.full_name% + +You can also validate the syntax of a file: + + php %command.full_name% filename + +Or of a whole directory: + + php %command.full_name% dirname + php %command.full_name% dirname --format=json + +Or find all files in a bundle: + + php %command.full_name% @AcmeDemoBundle + +EOF; + + $this->assertEquals($expected, $command->getHelp()); + } + + public function testLintFilesFromBundleDirectory() + { + $tester = $this->createCommandTester($this->getKernelAwareApplicationMock()); + $tester->execute( + array('filename' => '@AppBundle/Resources'), + array('verbosity' => OutputInterface::VERBOSITY_VERBOSE, 'decorated' => false) + ); + + $this->assertEquals(0, $tester->getStatusCode(), 'Returns 0 in case of success'); + $this->assertContains('[OK] All 0 YAML files contain valid syntax', trim($tester->getDisplay())); + } + + /** + * @return string Path to the new file + */ + private function createFile($content) + { + $filename = tempnam(sys_get_temp_dir().'/yml-lint-test', 'sf-'); + file_put_contents($filename, $content); + + $this->files[] = $filename; + + return $filename; + } + + /** + * @return CommandTester + */ + private function createCommandTester($application = null) + { + if (!$application) { + $application = new BaseApplication(); + $application->add(new YamlLintCommand()); + } + + $command = $application->find('lint:yaml'); + + if ($application) { + $command->setApplication($application); + } + + return new CommandTester($command); + } + + private function getKernelAwareApplicationMock() + { + $kernel = $this->getMockBuilder(KernelInterface::class) + ->disableOriginalConstructor() + ->getMock(); + + $kernel + ->expects($this->once()) + ->method('locateResource') + ->with('@AppBundle/Resources') + ->willReturn(sys_get_temp_dir().'/yml-lint-test'); + + $application = $this->getMockBuilder(Application::class) + ->disableOriginalConstructor() + ->getMock(); + + $application + ->expects($this->once()) + ->method('getKernel') + ->willReturn($kernel); + + $application + ->expects($this->once()) + ->method('getHelperSet') + ->willReturn(new HelperSet()); + + $application + ->expects($this->any()) + ->method('getDefinition') + ->willReturn(new InputDefinition()); + + $application + ->expects($this->once()) + ->method('find') + ->with('lint:yaml') + ->willReturn(new YamlLintCommand()); + + return $application; + } + + protected function setUp() + { + @mkdir(sys_get_temp_dir().'/yml-lint-test'); + $this->files = array(); + } + + protected function tearDown() + { + foreach ($this->files as $file) { + if (file_exists($file)) { + unlink($file); + } + } + rmdir(sys_get_temp_dir().'/yml-lint-test'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/ApplicationTest.php b/vendor/symfony/framework-bundle/Tests/Console/ApplicationTest.php new file mode 100644 index 0000000..c977bb1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/ApplicationTest.php @@ -0,0 +1,261 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Tester\ApplicationTester; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpKernel\KernelInterface; + +class ApplicationTest extends TestCase +{ + public function testBundleInterfaceImplementation() + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); + + $kernel = $this->getKernel(array($bundle), true); + + $application = new Application($kernel); + $application->doRun(new ArrayInput(array('list')), new NullOutput()); + } + + public function testBundleCommandsAreRegistered() + { + $bundle = $this->createBundleMock(array()); + + $kernel = $this->getKernel(array($bundle), true); + + $application = new Application($kernel); + $application->doRun(new ArrayInput(array('list')), new NullOutput()); + + // Calling twice: registration should only be done once. + $application->doRun(new ArrayInput(array('list')), new NullOutput()); + } + + public function testBundleCommandsAreRetrievable() + { + $bundle = $this->createBundleMock(array()); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + $application->all(); + + // Calling twice: registration should only be done once. + $application->all(); + } + + public function testBundleSingleCommandIsRetrievable() + { + $command = new Command('example'); + + $bundle = $this->createBundleMock(array($command)); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + + $this->assertSame($command, $application->get('example')); + } + + public function testBundleCommandCanBeFound() + { + $command = new Command('example'); + + $bundle = $this->createBundleMock(array($command)); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + + $this->assertSame($command, $application->find('example')); + } + + public function testBundleCommandCanBeFoundByAlias() + { + $command = new Command('example'); + $command->setAliases(array('alias')); + + $bundle = $this->createBundleMock(array($command)); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + + $this->assertSame($command, $application->find('alias')); + } + + public function testBundleCommandsHaveRightContainer() + { + $command = $this->getMockForAbstractClass('Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand', array('foo'), '', true, true, true, array('setContainer')); + $command->setCode(function () {}); + $command->expects($this->exactly(2))->method('setContainer'); + + $application = new Application($this->getKernel(array(), true)); + $application->setAutoExit(false); + $application->setCatchExceptions(false); + $application->add($command); + $tester = new ApplicationTester($application); + + // set container is called here + $tester->run(array('command' => 'foo')); + + // as the container might have change between two runs, setContainer must called again + $tester->run(array('command' => 'foo')); + } + + public function testBundleCommandCanOverriddeAPreExistingCommandWithTheSameName() + { + $command = new Command('example'); + + $bundle = $this->createBundleMock(array($command)); + + $kernel = $this->getKernel(array($bundle)); + + $application = new Application($kernel); + $newCommand = new Command('example'); + $application->add($newCommand); + + $this->assertSame($newCommand, $application->get('example')); + } + + public function testRunOnlyWarnsOnUnregistrableCommand() + { + $container = new ContainerBuilder(); + $container->register('event_dispatcher', EventDispatcher::class); + $container->register(ThrowingCommand::class, ThrowingCommand::class); + $container->setParameter('console.command.ids', array(ThrowingCommand::class => ThrowingCommand::class)); + + $kernel = $this->getMockBuilder(KernelInterface::class)->getMock(); + $kernel + ->method('getBundles') + ->willReturn(array($this->createBundleMock( + array((new Command('fine'))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); })) + ))); + $kernel + ->method('getContainer') + ->willReturn($container); + + $application = new Application($kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'fine')); + $output = $tester->getDisplay(); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertContains('Some commands could not be registered:', $output); + $this->assertContains('throwing', $output); + $this->assertContains('fine', $output); + } + + public function testRegistrationErrorsAreDisplayedOnCommandNotFound() + { + $container = new ContainerBuilder(); + $container->register('event_dispatcher', EventDispatcher::class); + + $kernel = $this->getMockBuilder(KernelInterface::class)->getMock(); + $kernel + ->method('getBundles') + ->willReturn(array($this->createBundleMock( + array((new Command(null))->setCode(function (InputInterface $input, OutputInterface $output) { $output->write('fine'); })) + ))); + $kernel + ->method('getContainer') + ->willReturn($container); + + $application = new Application($kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'fine')); + $output = $tester->getDisplay(); + + $this->assertSame(1, $tester->getStatusCode()); + $this->assertContains('Some commands could not be registered:', $output); + $this->assertContains('Command "fine" is not defined.', $output); + } + + private function getKernel(array $bundles, $useDispatcher = false) + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + + if ($useDispatcher) { + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $dispatcher + ->expects($this->atLeastOnce()) + ->method('dispatch') + ; + $container + ->expects($this->atLeastOnce()) + ->method('get') + ->with($this->equalTo('event_dispatcher')) + ->will($this->returnValue($dispatcher)); + } + + $container + ->expects($this->exactly(2)) + ->method('hasParameter') + ->withConsecutive(array('console.command.ids'), array('console.lazy_command.ids')) + ->willReturnOnConsecutiveCalls(true, true) + ; + $container + ->expects($this->exactly(2)) + ->method('getParameter') + ->withConsecutive(array('console.lazy_command.ids'), array('console.command.ids')) + ->willReturnOnConsecutiveCalls(array(), array()) + ; + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue($bundles)) + ; + $kernel + ->expects($this->any()) + ->method('getContainer') + ->will($this->returnValue($container)) + ; + + return $kernel; + } + + private function createBundleMock(array $commands) + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\Bundle')->getMock(); + $bundle + ->expects($this->once()) + ->method('registerCommands') + ->will($this->returnCallback(function (Application $application) use ($commands) { + $application->addCommands($commands); + })) + ; + + return $bundle; + } +} + +class ThrowingCommand extends Command +{ + public function __construct() + { + throw new \Exception('throwing'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/Descriptor/AbstractDescriptorTest.php b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/AbstractDescriptorTest.php new file mode 100644 index 0000000..ab5c95a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/AbstractDescriptorTest.php @@ -0,0 +1,251 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Style\SymfonyStyle; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +abstract class AbstractDescriptorTest extends TestCase +{ + /** @dataProvider getDescribeRouteCollectionTestData */ + public function testDescribeRouteCollection(RouteCollection $routes, $expectedDescription) + { + $this->assertDescription($expectedDescription, $routes); + } + + public function getDescribeRouteCollectionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getRouteCollections()); + } + + /** @dataProvider getDescribeRouteTestData */ + public function testDescribeRoute(Route $route, $expectedDescription) + { + $this->assertDescription($expectedDescription, $route); + } + + public function getDescribeRouteTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getRoutes()); + } + + /** @dataProvider getDescribeContainerParametersTestData */ + public function testDescribeContainerParameters(ParameterBag $parameters, $expectedDescription) + { + $this->assertDescription($expectedDescription, $parameters); + } + + public function getDescribeContainerParametersTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getContainerParameters()); + } + + /** @dataProvider getDescribeContainerBuilderTestData */ + public function testDescribeContainerBuilder(ContainerBuilder $builder, $expectedDescription, array $options) + { + $this->assertDescription($expectedDescription, $builder, $options); + } + + public function getDescribeContainerBuilderTestData() + { + return $this->getContainerBuilderDescriptionTestData(ObjectsProvider::getContainerBuilders()); + } + + /** @dataProvider getDescribeContainerDefinitionTestData */ + public function testDescribeContainerDefinition(Definition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition); + } + + public function getDescribeContainerDefinitionTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getContainerDefinitions()); + } + + /** @dataProvider getDescribeContainerDefinitionWithArgumentsShownTestData */ + public function testDescribeContainerDefinitionWithArgumentsShown(Definition $definition, $expectedDescription) + { + $this->assertDescription($expectedDescription, $definition, array('show_arguments' => true)); + } + + public function getDescribeContainerDefinitionWithArgumentsShownTestData() + { + $definitions = ObjectsProvider::getContainerDefinitions(); + $definitionsWithArgs = array(); + + foreach ($definitions as $key => $definition) { + $definitionsWithArgs[str_replace('definition_', 'definition_arguments_', $key)] = $definition; + } + + return $this->getDescriptionTestData($definitionsWithArgs); + } + + /** @dataProvider getDescribeContainerAliasTestData */ + public function testDescribeContainerAlias(Alias $alias, $expectedDescription) + { + $this->assertDescription($expectedDescription, $alias); + } + + public function getDescribeContainerAliasTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getContainerAliases()); + } + + /** @dataProvider getDescribeContainerDefinitionWhichIsAnAliasTestData */ + public function testDescribeContainerDefinitionWhichIsAnAlias(Alias $alias, $expectedDescription, ContainerBuilder $builder, $options = array()) + { + $this->assertDescription($expectedDescription, $builder, $options); + } + + public function getDescribeContainerDefinitionWhichIsAnAliasTestData() + { + $builder = current(ObjectsProvider::getContainerBuilders()); + $builder->setDefinition('service_1', $builder->getDefinition('definition_1')); + $builder->setDefinition('service_2', $builder->getDefinition('definition_2')); + + $aliases = ObjectsProvider::getContainerAliases(); + $aliasesWithDefinitions = array(); + foreach ($aliases as $name => $alias) { + $aliasesWithDefinitions[str_replace('alias_', 'alias_with_definition_', $name)] = $alias; + } + + $i = 0; + $data = $this->getDescriptionTestData($aliasesWithDefinitions); + foreach ($aliases as $name => $alias) { + $data[$i][] = $builder; + $data[$i][] = array('id' => $name); + ++$i; + } + + return $data; + } + + /** @dataProvider getDescribeContainerParameterTestData */ + public function testDescribeContainerParameter($parameter, $expectedDescription, array $options) + { + $this->assertDescription($expectedDescription, $parameter, $options); + } + + public function getDescribeContainerParameterTestData() + { + $data = $this->getDescriptionTestData(ObjectsProvider::getContainerParameter()); + + $data[0][] = array('parameter' => 'database_name'); + $data[1][] = array('parameter' => 'twig.form.resources'); + + return $data; + } + + /** @dataProvider getDescribeEventDispatcherTestData */ + public function testDescribeEventDispatcher(EventDispatcher $eventDispatcher, $expectedDescription, array $options) + { + $this->assertDescription($expectedDescription, $eventDispatcher, $options); + } + + public function getDescribeEventDispatcherTestData() + { + return $this->getEventDispatcherDescriptionTestData(ObjectsProvider::getEventDispatchers()); + } + + /** @dataProvider getDescribeCallableTestData */ + public function testDescribeCallable($callable, $expectedDescription) + { + $this->assertDescription($expectedDescription, $callable); + } + + public function getDescribeCallableTestData() + { + return $this->getDescriptionTestData(ObjectsProvider::getCallables()); + } + + abstract protected function getDescriptor(); + + abstract protected function getFormat(); + + private function assertDescription($expectedDescription, $describedObject, array $options = array()) + { + $options['raw_output'] = true; + $options['raw_text'] = true; + $output = new BufferedOutput(BufferedOutput::VERBOSITY_NORMAL, true); + + if ('txt' === $this->getFormat()) { + $options['output'] = new SymfonyStyle(new ArrayInput(array()), $output); + } + + $this->getDescriptor()->describe($output, $describedObject, $options); + + if ('json' === $this->getFormat()) { + $this->assertEquals(json_encode(json_decode($expectedDescription), JSON_PRETTY_PRINT), json_encode(json_decode($output->fetch()), JSON_PRETTY_PRINT)); + } else { + $this->assertEquals(trim($expectedDescription), trim(str_replace(PHP_EOL, "\n", $output->fetch()))); + } + } + + private function getDescriptionTestData(array $objects) + { + $data = array(); + foreach ($objects as $name => $object) { + $description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s.%s', __DIR__, $name, $this->getFormat())); + $data[] = array($object, $description); + } + + return $data; + } + + private function getContainerBuilderDescriptionTestData(array $objects) + { + $variations = array( + 'services' => array('show_private' => true), + 'public' => array('show_private' => false), + 'tag1' => array('show_private' => true, 'tag' => 'tag1'), + 'tags' => array('group_by' => 'tags', 'show_private' => true), + 'arguments' => array('show_private' => false, 'show_arguments' => true), + ); + + $data = array(); + foreach ($objects as $name => $object) { + foreach ($variations as $suffix => $options) { + $description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s_%s.%s', __DIR__, $name, $suffix, $this->getFormat())); + $data[] = array($object, $description, $options); + } + } + + return $data; + } + + private function getEventDispatcherDescriptionTestData(array $objects) + { + $variations = array( + 'events' => array(), + 'event1' => array('event' => 'event1'), + ); + + $data = array(); + foreach ($objects as $name => $object) { + foreach ($variations as $suffix => $options) { + $description = file_get_contents(sprintf('%s/../../Fixtures/Descriptor/%s_%s.%s', __DIR__, $name, $suffix, $this->getFormat())); + $data[] = array($object, $description, $options); + } + } + + return $data; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/Descriptor/JsonDescriptorTest.php b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/JsonDescriptorTest.php new file mode 100644 index 0000000..ee03f65 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/JsonDescriptorTest.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\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\JsonDescriptor; + +class JsonDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new JsonDescriptor(); + } + + protected function getFormat() + { + return 'json'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/MarkdownDescriptorTest.php new file mode 100644 index 0000000..fbb5aaa --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/MarkdownDescriptorTest.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\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\MarkdownDescriptor; + +class MarkdownDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new MarkdownDescriptor(); + } + + protected function getFormat() + { + return 'md'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/Descriptor/ObjectsProvider.php b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/ObjectsProvider.php new file mode 100644 index 0000000..3025c6b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/ObjectsProvider.php @@ -0,0 +1,198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Routing\CompiledRoute; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class ObjectsProvider +{ + public static function getRouteCollections() + { + $collection1 = new RouteCollection(); + foreach (self::getRoutes() as $name => $route) { + $collection1->add($name, $route); + } + + return array('route_collection_1' => $collection1); + } + + public static function getRoutes() + { + return array( + 'route_1' => new RouteStub( + '/hello/{name}', + array('name' => 'Joseph'), + array('name' => '[a-z]+'), + array('opt1' => 'val1', 'opt2' => 'val2'), + 'localhost', + array('http', 'https'), + array('get', 'head') + ), + 'route_2' => new RouteStub( + '/name/add', + array(), + array(), + array('opt1' => 'val1', 'opt2' => 'val2'), + 'localhost', + array('http', 'https'), + array('put', 'post') + ), + ); + } + + public static function getContainerParameters() + { + return array( + 'parameters_1' => new ParameterBag(array( + 'integer' => 12, + 'string' => 'Hello world!', + 'boolean' => true, + 'array' => array(12, 'Hello world!', true), + )), + ); + } + + public static function getContainerParameter() + { + $builder = new ContainerBuilder(); + $builder->setParameter('database_name', 'symfony'); + $builder->setParameter('twig.form.resources', array( + 'bootstrap_3_horizontal_layout.html.twig', + 'bootstrap_3_layout.html.twig', + 'form_div_layout.html.twig', + 'form_table_layout.html.twig', + )); + + return array( + 'parameter' => $builder, + 'array_parameter' => $builder, + ); + } + + public static function getContainerBuilders() + { + $builder1 = new ContainerBuilder(); + $builder1->setDefinitions(self::getContainerDefinitions()); + $builder1->setAliases(self::getContainerAliases()); + + return array('builder_1' => $builder1); + } + + public static function getContainerDefinitions() + { + $definition1 = new Definition('Full\\Qualified\\Class1'); + $definition2 = new Definition('Full\\Qualified\\Class2'); + + return array( + 'definition_1' => $definition1 + ->setPublic(true) + ->setSynthetic(false) + ->setLazy(true) + ->setAbstract(true) + ->addArgument(new Reference('definition2')) + ->addArgument('%parameter%') + ->addArgument(new Definition('inline_service', array('arg1', 'arg2'))) + ->addArgument(array( + 'foo', + new Reference('definition2'), + new Definition('inline_service'), + )) + ->addArgument(new IteratorArgument(array( + new Reference('definition_1'), + new Reference('definition_2'), + ))) + ->setFactory(array('Full\\Qualified\\FactoryClass', 'get')), + 'definition_2' => $definition2 + ->setPublic(false) + ->setSynthetic(true) + ->setFile('/path/to/file') + ->setLazy(false) + ->setAbstract(false) + ->addTag('tag1', array('attr1' => 'val1', 'attr2' => 'val2')) + ->addTag('tag1', array('attr3' => 'val3')) + ->addTag('tag2') + ->addMethodCall('setMailer', array(new Reference('mailer'))) + ->setFactory(array(new Reference('factory.service'), 'get')), + ); + } + + public static function getContainerAliases() + { + return array( + 'alias_1' => new Alias('service_1', true), + 'alias_2' => new Alias('service_2', false), + ); + } + + public static function getEventDispatchers() + { + $eventDispatcher = new EventDispatcher(); + + $eventDispatcher->addListener('event1', 'global_function', 255); + $eventDispatcher->addListener('event1', function () { return 'Closure'; }, -1); + $eventDispatcher->addListener('event2', new CallableClass()); + + return array('event_dispatcher_1' => $eventDispatcher); + } + + public static function getCallables() + { + return array( + 'callable_1' => 'array_key_exists', + 'callable_2' => array('Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass', 'staticMethod'), + 'callable_3' => array(new CallableClass(), 'method'), + 'callable_4' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass::staticMethod', + 'callable_5' => array('Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\ExtendedCallableClass', 'parent::staticMethod'), + 'callable_6' => function () { return 'Closure'; }, + 'callable_7' => new CallableClass(), + ); + } +} + +class CallableClass +{ + public function __invoke() + { + } + + public static function staticMethod() + { + } + + public function method() + { + } +} + +class ExtendedCallableClass extends CallableClass +{ + public static function staticMethod() + { + } +} + +class RouteStub extends Route +{ + public function compile() + { + return new CompiledRoute('', '#PATH_REGEX#', array(), array(), '#HOST_REGEX#'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/Descriptor/TextDescriptorTest.php b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/TextDescriptorTest.php new file mode 100644 index 0000000..e775ac7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/TextDescriptorTest.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\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor; + +class TextDescriptorTest extends AbstractDescriptorTest +{ + protected function setUp() + { + putenv('COLUMNS=121'); + } + + protected function tearDown() + { + putenv('COLUMNS'); + } + + protected function getDescriptor() + { + return new TextDescriptor(); + } + + protected function getFormat() + { + return 'txt'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Console/Descriptor/XmlDescriptorTest.php b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/XmlDescriptorTest.php new file mode 100644 index 0000000..8cb9a71 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Console/Descriptor/XmlDescriptorTest.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\Bundle\FrameworkBundle\Tests\Console\Descriptor; + +use Symfony\Bundle\FrameworkBundle\Console\Descriptor\XmlDescriptor; + +class XmlDescriptorTest extends AbstractDescriptorTest +{ + protected function getDescriptor() + { + return new XmlDescriptor(); + } + + protected function getFormat() + { + return 'xml'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/AbstractControllerTest.php b/vendor/symfony/framework-bundle/Tests/Controller/AbstractControllerTest.php new file mode 100644 index 0000000..6783ec2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/AbstractControllerTest.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\Bundle\FrameworkBundle\Tests\Controller; + +use Psr\Container\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + +class AbstractControllerTest extends ControllerTraitTest +{ + protected function createController() + { + return new TestAbstractController(); + } +} + +class TestAbstractController extends AbstractController +{ + use TestControllerTrait; + + private $throwOnUnexpectedService; + + public function __construct($throwOnUnexpectedService = true) + { + $this->throwOnUnexpectedService = $throwOnUnexpectedService; + } + + public function setContainer(ContainerInterface $container) + { + if (!$this->throwOnUnexpectedService) { + return parent::setContainer($container); + } + + $expected = self::getSubscribedServices(); + + foreach ($container->getServiceIds() as $id) { + if ('service_container' === $id) { + continue; + } + if (!isset($expected[$id])) { + throw new \UnexpectedValueException(sprintf('Service "%s" is not expected, as declared by %s::getSubscribedServices()', $id, AbstractController::class)); + } + $type = substr($expected[$id], 1); + if (!$container->get($id) instanceof $type) { + throw new \UnexpectedValueException(sprintf('Service "%s" is expected to be an instance of "%s", as declared by %s::getSubscribedServices()', $id, $type, AbstractController::class)); + } + } + + return parent::setContainer($container); + } + + public function fooAction() + { + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/ControllerNameParserTest.php b/vendor/symfony/framework-bundle/Tests/Controller/ControllerNameParserTest.php new file mode 100644 index 0000000..b5edad9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/ControllerNameParserTest.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Composer\Autoload\ClassLoader; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\HttpKernel\Kernel; + +class ControllerNameParserTest extends TestCase +{ + protected $loader; + + protected function setUp() + { + $this->loader = new ClassLoader(); + $this->loader->add('TestBundle', __DIR__.'/../Fixtures'); + $this->loader->add('TestApplication', __DIR__.'/../Fixtures'); + $this->loader->register(); + } + + protected function tearDown() + { + $this->loader->unregister(); + $this->loader = null; + } + + public function testParse() + { + $parser = $this->createParser(); + + $this->assertEquals('TestBundle\FooBundle\Controller\DefaultController::indexAction', $parser->parse('FooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction', $parser->parse('FooBundle:Sub\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\Fabpot\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\Sensio\Cms\FooBundle\Controller\DefaultController::indexAction', $parser->parse('SensioCmsFooBundle:Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\FooBundle\Controller\Test\DefaultController::indexAction', $parser->parse('FooBundle:Test\\Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + $this->assertEquals('TestBundle\FooBundle\Controller\Test\DefaultController::indexAction', $parser->parse('FooBundle:Test/Default:index'), '->parse() converts a short a:b:c notation string to a class::method string'); + + try { + $parser->parse('foo:'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an a:b:c string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an a:b:c string'); + } + } + + public function testBuild() + { + $parser = $this->createParser(); + + $this->assertEquals('FoooooBundle:Default:index', $parser->build('TestBundle\FooBundle\Controller\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + $this->assertEquals('FoooooBundle:Sub\Default:index', $parser->build('TestBundle\FooBundle\Controller\Sub\DefaultController::indexAction'), '->parse() converts a class::method string to a short a:b:c notation string'); + + try { + $parser->build('TestBundle\FooBundle\Controller\DefaultController::index'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } + + try { + $parser->build('TestBundle\FooBundle\Controller\Default::indexAction'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } + + try { + $parser->build('Foo\Controller\DefaultController::indexAction'); + $this->fail('->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws an \InvalidArgumentException if the controller is not an aController::cAction string'); + } + } + + /** + * @dataProvider getMissingControllersTest + */ + public function testMissingControllers($name) + { + $parser = $this->createParser(); + + try { + $parser->parse($name); + $this->fail('->parse() throws a \InvalidArgumentException if the class is found but does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws a \InvalidArgumentException if the class is found but does not exist'); + } + } + + public function getMissingControllersTest() + { + // a normal bundle + $bundles = array( + array('FooBundle:Fake:index'), + ); + + // a bundle with children + if (Kernel::VERSION_ID < 40000) { + $bundles[] = array('SensioFooBundle:Fake:index'); + } + + return $bundles; + } + + /** + * @dataProvider getInvalidBundleNameTests + */ + public function testInvalidBundleName($bundleName, $suggestedBundleName) + { + $parser = $this->createParser(); + + try { + $parser->parse($bundleName); + $this->fail('->parse() throws a \InvalidArgumentException if the bundle does not exist'); + } catch (\Exception $e) { + $this->assertInstanceOf('\InvalidArgumentException', $e, '->parse() throws a \InvalidArgumentException if the bundle does not exist'); + + if (false === $suggestedBundleName) { + // make sure we don't have a suggestion + $this->assertNotContains('Did you mean', $e->getMessage()); + } else { + $this->assertContains(sprintf('Did you mean "%s"', $suggestedBundleName), $e->getMessage()); + } + } + } + + public function getInvalidBundleNameTests() + { + return array( + 'Alternative will be found using levenshtein' => array('FoodBundle:Default:index', 'FooBundle:Default:index'), + 'Alternative will be found using partial match' => array('FabpotFooBund:Default:index', 'FabpotFooBundle:Default:index'), + 'Bundle does not exist at all' => array('CrazyBundle:Default:index', false), + ); + } + + private function createParser() + { + $bundles = array( + 'SensioFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), + 'SensioCmsFooBundle' => array($this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle')), + 'FooBundle' => array($this->getBundle('TestBundle\FooBundle', 'FooBundle')), + 'FabpotFooBundle' => array($this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), $this->getBundle('TestBundle\Sensio\FooBundle', 'SensioFooBundle')), + ); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnCallback(function ($bundle) use ($bundles) { + if (!isset($bundles[$bundle])) { + throw new \InvalidArgumentException(sprintf('Invalid bundle name "%s"', $bundle)); + } + + return $bundles[$bundle]; + })) + ; + + $bundles = array( + 'SensioFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), + 'SensioCmsFooBundle' => $this->getBundle('TestBundle\Sensio\Cms\FooBundle', 'SensioCmsFooBundle'), + 'FoooooBundle' => $this->getBundle('TestBundle\FooBundle', 'FoooooBundle'), + 'FooBundle' => $this->getBundle('TestBundle\FooBundle', 'FooBundle'), + 'FabpotFooBundle' => $this->getBundle('TestBundle\Fabpot\FooBundle', 'FabpotFooBundle'), + ); + $kernel + ->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue($bundles)) + ; + + return new ControllerNameParser($kernel); + } + + private function getBundle($namespace, $name) + { + $bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock(); + $bundle->expects($this->any())->method('getName')->will($this->returnValue($name)); + $bundle->expects($this->any())->method('getNamespace')->will($this->returnValue($namespace)); + + return $bundle; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/ControllerResolverTest.php b/vendor/symfony/framework-bundle/Tests/Controller/ControllerResolverTest.php new file mode 100644 index 0000000..5880ee0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/ControllerResolverTest.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\Bundle\FrameworkBundle\Tests\Controller; + +use Psr\Container\ContainerInterface as Psr11ContainerInterface; +use Psr\Log\LoggerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Tests\Controller\ContainerControllerResolverTest; + +class ControllerResolverTest extends ContainerControllerResolverTest +{ + public function testGetControllerOnContainerAware() + { + $resolver = $this->createControllerResolver(); + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerInterface', $controller[0]->getContainer()); + $this->assertSame('testAction', $controller[1]); + } + + public function testGetControllerOnContainerAwareInvokable() + { + $resolver = $this->createControllerResolver(); + $request = Request::create('/'); + $request->attributes->set('_controller', 'Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController'); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController', $controller); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerInterface', $controller->getContainer()); + } + + public function testGetControllerWithBundleNotation() + { + $shortName = 'FooBundle:Default:test'; + $parser = $this->createMockParser(); + $parser->expects($this->once()) + ->method('parse') + ->with($shortName) + ->will($this->returnValue('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController::testAction')) + ; + + $resolver = $this->createControllerResolver(null, null, $parser); + $request = Request::create('/'); + $request->attributes->set('_controller', $shortName); + + $controller = $resolver->getController($request); + + $this->assertInstanceOf('Symfony\Bundle\FrameworkBundle\Tests\Controller\ContainerAwareController', $controller[0]); + $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerInterface', $controller[0]->getContainer()); + $this->assertSame('testAction', $controller[1]); + } + + public function testContainerAwareControllerGetsContainerWhenNotSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new ContainerAwareController(); + + $container = new Container(); + $container->set(TestAbstractController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', TestAbstractController::class.':testAction'); + + $this->assertSame(array($controller, 'testAction'), $resolver->getController($request)); + $this->assertSame($container, $controller->getContainer()); + } + + public function testAbstractControllerGetsContainerWhenNotSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new TestAbstractController(false); + + $container = new Container(); + $container->set(TestAbstractController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); + + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($container, $controller->setContainer($container)); + } + + public function testAbstractControllerServiceWithFcqnIdGetsContainerWhenNotSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new DummyController(); + + $container = new Container(); + $container->set(DummyController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', DummyController::class.':fooAction'); + + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($container, $controller->getContainer()); + } + + public function testAbstractControllerGetsNoContainerWhenSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new TestAbstractController(false); + $controllerContainer = new Container(); + $controller->setContainer($controllerContainer); + + $container = new Container(); + $container->set(TestAbstractController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); + + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($controllerContainer, $controller->setContainer($container)); + } + + public function testAbstractControllerServiceWithFcqnIdGetsNoContainerWhenSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new DummyController(); + $controllerContainer = new Container(); + $controller->setContainer($controllerContainer); + + $container = new Container(); + $container->set(DummyController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', DummyController::class.':fooAction'); + + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($controllerContainer, $controller->getContainer()); + } + + protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null, ControllerNameParser $parser = null) + { + if (!$parser) { + $parser = $this->createMockParser(); + } + + if (!$container) { + $container = $this->createMockContainer(); + } + + return new ControllerResolver($container, $parser, $logger); + } + + protected function createMockParser() + { + return $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser')->disableOriginalConstructor()->getMock(); + } + + protected function createMockContainer() + { + return $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + } +} + +class ContainerAwareController implements ContainerAwareInterface +{ + private $container; + + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } + + public function getContainer() + { + return $this->container; + } + + public function testAction() + { + } + + public function __invoke() + { + } +} + +class DummyController extends AbstractController +{ + public function getContainer() + { + return $this->container; + } + + public function fooAction() + { + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/ControllerTest.php b/vendor/symfony/framework-bundle/Tests/Controller/ControllerTest.php new file mode 100644 index 0000000..452845c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/ControllerTest.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\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\Controller; + +class ControllerTest extends ControllerTraitTest +{ + protected function createController() + { + return new TestController(); + } +} + +class TestController extends Controller +{ + use TestControllerTrait; +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/ControllerTraitTest.php b/vendor/symfony/framework-bundle/Tests/Controller/ControllerTraitTest.php new file mode 100644 index 0000000..e72f536 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/ControllerTraitTest.php @@ -0,0 +1,556 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\File\File; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\User\User; +use Symfony\Component\Serializer\SerializerInterface; + +abstract class ControllerTraitTest extends TestCase +{ + abstract protected function createController(); + + public function testForward() + { + $request = Request::create('/'); + $request->setLocale('fr'); + $request->setRequestFormat('xml'); + + $requestStack = new RequestStack(); + $requestStack->push($request); + + $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->getLocale()); + })); + + $container = new Container(); + $container->set('request_stack', $requestStack); + $container->set('http_kernel', $kernel); + + $controller = $this->createController(); + $controller->setContainer($container); + + $response = $controller->forward('a_controller'); + $this->assertEquals('xml--fr', $response->getContent()); + } + + public function testGetUser() + { + $user = new User('user', 'pass'); + $token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')); + + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); + + $this->assertSame($controller->getUser(), $user); + } + + public function testGetUserAnonymousUserConvertedToNull() + { + $token = new AnonymousToken('default', 'anon.'); + + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); + + $this->assertNull($controller->getUser()); + } + + public function testGetUserWithEmptyTokenStorage() + { + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage(null)); + + $this->assertNull($controller->getUser()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage The SecurityBundle is not registered in your application. + */ + public function testGetUserWithEmptyContainer() + { + $controller = $this->createController(); + $controller->setContainer(new Container()); + + $controller->getUser(); + } + + /** + * @param $token + * + * @return Container + */ + private function getContainerWithTokenStorage($token = null) + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage')->getMock(); + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token)); + + $container = new Container(); + $container->set('security.token_storage', $tokenStorage); + + return $container; + } + + public function testJson() + { + $controller = $this->createController(); + $controller->setContainer(new Container()); + + $response = $controller->json(array()); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); + } + + public function testJsonWithSerializer() + { + $container = new Container(); + + $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); + $serializer + ->expects($this->once()) + ->method('serialize') + ->with(array(), 'json', array('json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS)) + ->will($this->returnValue('[]')); + + $container->set('serializer', $serializer); + + $controller = $this->createController(); + $controller->setContainer($container); + + $response = $controller->json(array()); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); + } + + public function testJsonWithSerializerContextOverride() + { + $container = new Container(); + + $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); + $serializer + ->expects($this->once()) + ->method('serialize') + ->with(array(), 'json', array('json_encode_options' => 0, 'other' => 'context')) + ->will($this->returnValue('[]')); + + $container->set('serializer', $serializer); + + $controller = $this->createController(); + $controller->setContainer($container); + + $response = $controller->json(array(), 200, array(), array('json_encode_options' => 0, 'other' => 'context')); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); + $response->setEncodingOptions(JSON_FORCE_OBJECT); + $this->assertEquals('{}', $response->getContent()); + } + + public function testFile() + { + $container = new Container(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $container->set('http_kernel', $kernel); + + $controller = $this->createController(); + $controller->setContainer($container); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(new File(__FILE__)); + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileAsInline() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileWithOwnFileName() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $fileName = 'test.php'; + $response = $controller->file(new File(__FILE__), $fileName); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains($fileName, $response->headers->get('content-disposition')); + } + + public function testFileWithOwnFileNameAsInline() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $fileName = 'test.php'; + $response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); + $this->assertContains($fileName, $response->headers->get('content-disposition')); + } + + public function testFileFromPath() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(__FILE__); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileFromPathWithCustomizedFileName() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(__FILE__, 'test.php'); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains('test.php', $response->headers->get('content-disposition')); + } + + /** + * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException + */ + public function testFileWhichDoesNotExist() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file('some-file.txt', 'test.php'); + } + + public function testIsGranted() + { + $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); + $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true); + + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertTrue($controller->isGranted('foo')); + } + + /** + * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException + */ + public function testdenyAccessUnlessGranted() + { + $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); + $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); + + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); + + $controller->denyAccessUnlessGranted('foo'); + } + + public function testRenderViewTwig() + { + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->renderView('foo')); + } + + public function testRenderTwig() + { + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->render('foo')->getContent()); + } + + public function testStreamTwig() + { + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); + } + + public function testRedirectToRoute() + { + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router->expects($this->once())->method('generate')->willReturn('/foo'); + + $container = new Container(); + $container->set('router', $router); + + $controller = $this->createController(); + $controller->setContainer($container); + $response = $controller->redirectToRoute('foo'); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame('/foo', $response->getTargetUrl()); + $this->assertSame(302, $response->getStatusCode()); + } + + /** + * @runInSeparateProcess + */ + public function testAddFlash() + { + $flashBag = new FlashBag(); + $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->disableOriginalConstructor()->getMock(); + $session->expects($this->once())->method('getFlashBag')->willReturn($flashBag); + + $container = new Container(); + $container->set('session', $session); + + $controller = $this->createController(); + $controller->setContainer($container); + $controller->addFlash('foo', 'bar'); + + $this->assertSame(array('bar'), $flashBag->get('foo')); + } + + public function testCreateAccessDeniedException() + { + $controller = $this->createController(); + + $this->assertInstanceOf('Symfony\Component\Security\Core\Exception\AccessDeniedException', $controller->createAccessDeniedException()); + } + + public function testIsCsrfTokenValid() + { + $tokenManager = $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock(); + $tokenManager->expects($this->once())->method('isTokenValid')->willReturn(true); + + $container = new Container(); + $container->set('security.csrf.token_manager', $tokenManager); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertTrue($controller->isCsrfTokenValid('foo', 'bar')); + } + + public function testGenerateUrl() + { + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router->expects($this->once())->method('generate')->willReturn('/foo'); + + $container = new Container(); + $container->set('router', $router); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('/foo', $controller->generateUrl('foo')); + } + + public function testRedirect() + { + $controller = $this->createController(); + $response = $controller->redirect('http://dunglas.fr', 301); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame('http://dunglas.fr', $response->getTargetUrl()); + $this->assertSame(301, $response->getStatusCode()); + } + + public function testRenderViewTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->renderView('foo')); + } + + public function testRenderTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->render('foo')->getContent()); + } + + public function testStreamTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); + } + + public function testCreateNotFoundException() + { + $controller = $this->createController(); + + $this->assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $controller->createNotFoundException()); + } + + public function testCreateForm() + { + $form = $this->getMockBuilder('Symfony\Component\Form\FormInterface')->getMock(); + + $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); + $formFactory->expects($this->once())->method('create')->willReturn($form); + + $container = new Container(); + $container->set('form.factory', $formFactory); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals($form, $controller->createForm('foo')); + } + + public function testCreateFormBuilder() + { + $formBuilder = $this->getMockBuilder('Symfony\Component\Form\FormBuilderInterface')->getMock(); + + $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); + $formFactory->expects($this->once())->method('createBuilder')->willReturn($formBuilder); + + $container = new Container(); + $container->set('form.factory', $formFactory); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals($formBuilder, $controller->createFormBuilder('foo')); + } + + public function testGetDoctrine() + { + $doctrine = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); + + $container = new Container(); + $container->set('doctrine', $doctrine); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals($doctrine, $controller->getDoctrine()); + } +} + +trait TestControllerTrait +{ + use ControllerTrait { + generateUrl as public; + redirect as public; + forward as public; + getUser as public; + json as public; + file as public; + isGranted as public; + denyAccessUnlessGranted as public; + redirectToRoute as public; + addFlash as public; + isCsrfTokenValid as public; + renderView as public; + render as public; + stream as public; + createNotFoundException as public; + createAccessDeniedException as public; + createForm as public; + createFormBuilder as public; + getDoctrine as public; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/RedirectControllerTest.php b/vendor/symfony/framework-bundle/Tests/Controller/RedirectControllerTest.php new file mode 100644 index 0000000..9a1883b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/RedirectControllerTest.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\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\RedirectController; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * @author Marcin Sikon + */ +class RedirectControllerTest extends TestCase +{ + public function testEmptyRoute() + { + $request = new Request(); + $controller = new RedirectController(); + + try { + $controller->redirectAction($request, '', true); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(410, $e->getStatusCode()); + } + + try { + $controller->redirectAction($request, '', false); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(404, $e->getStatusCode()); + } + } + + /** + * @dataProvider provider + */ + public function testRoute($permanent, $ignoreAttributes, $expectedCode, $expectedAttributes) + { + $request = new Request(); + + $route = 'new-route'; + $url = '/redirect-url'; + $attributes = array( + 'route' => $route, + 'permanent' => $permanent, + '_route' => 'current-route', + '_route_params' => array( + 'route' => $route, + 'permanent' => $permanent, + 'additional-parameter' => 'value', + 'ignoreAttributes' => $ignoreAttributes, + ), + ); + + $request->attributes = new ParameterBag($attributes); + + $router = $this->getMockBuilder(UrlGeneratorInterface::class)->getMock(); + $router + ->expects($this->once()) + ->method('generate') + ->with($this->equalTo($route), $this->equalTo($expectedAttributes)) + ->will($this->returnValue($url)); + + $controller = new RedirectController($router); + + $returnResponse = $controller->redirectAction($request, $route, $permanent, $ignoreAttributes); + + $this->assertRedirectUrl($returnResponse, $url); + $this->assertEquals($expectedCode, $returnResponse->getStatusCode()); + } + + public function provider() + { + return array( + array(true, false, 301, array('additional-parameter' => 'value')), + array(false, false, 302, array('additional-parameter' => 'value')), + array(false, true, 302, array()), + array(false, array('additional-parameter'), 302, array()), + ); + } + + public function testEmptyPath() + { + $request = new Request(); + $controller = new RedirectController(); + + try { + $controller->urlRedirectAction($request, '', true); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(410, $e->getStatusCode()); + } + + try { + $controller->urlRedirectAction($request, '', false); + $this->fail('Expected Symfony\Component\HttpKernel\Exception\HttpException to be thrown'); + } catch (HttpException $e) { + $this->assertSame(404, $e->getStatusCode()); + } + } + + public function testFullURL() + { + $request = new Request(); + $controller = new RedirectController(); + $returnResponse = $controller->urlRedirectAction($request, 'http://foo.bar/'); + + $this->assertRedirectUrl($returnResponse, 'http://foo.bar/'); + $this->assertEquals(302, $returnResponse->getStatusCode()); + } + + public function testUrlRedirectDefaultPorts() + { + $host = 'www.example.com'; + $baseUrl = '/base'; + $path = '/redirect-path'; + $httpPort = 1080; + $httpsPort = 1443; + + $expectedUrl = "https://$host:$httpsPort$baseUrl$path"; + $request = $this->createRequestObject('http', $host, $httpPort, $baseUrl); + $controller = $this->createRedirectController(null, $httpsPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'https'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + + $expectedUrl = "http://$host:$httpPort$baseUrl$path"; + $request = $this->createRequestObject('https', $host, $httpPort, $baseUrl); + $controller = $this->createRedirectController($httpPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'http'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + /** + * @group legacy + */ + public function testUrlRedirectDefaultPortParameters() + { + $host = 'www.example.com'; + $baseUrl = '/base'; + $path = '/redirect-path'; + $httpPort = 1080; + $httpsPort = 1443; + + $expectedUrl = "https://$host:$httpsPort$baseUrl$path"; + $request = $this->createRequestObject('http', $host, $httpPort, $baseUrl); + $controller = $this->createLegacyRedirectController(null, $httpsPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'https'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + + $expectedUrl = "http://$host:$httpPort$baseUrl$path"; + $request = $this->createRequestObject('https', $host, $httpPort, $baseUrl); + $controller = $this->createLegacyRedirectController($httpPort); + $returnValue = $controller->urlRedirectAction($request, $path, false, 'http'); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + public function urlRedirectProvider() + { + return array( + // Standard ports + array('http', null, null, 'http', 80, ''), + array('http', 80, null, 'http', 80, ''), + array('https', null, null, 'http', 80, ''), + array('https', 80, null, 'http', 80, ''), + + array('http', null, null, 'https', 443, ''), + array('http', null, 443, 'https', 443, ''), + array('https', null, null, 'https', 443, ''), + array('https', null, 443, 'https', 443, ''), + + // Non-standard ports + array('http', null, null, 'http', 8080, ':8080'), + array('http', 4080, null, 'http', 8080, ':4080'), + array('http', 80, null, 'http', 8080, ''), + array('https', null, null, 'http', 8080, ''), + array('https', null, 8443, 'http', 8080, ':8443'), + array('https', null, 443, 'http', 8080, ''), + + array('https', null, null, 'https', 8443, ':8443'), + array('https', null, 4443, 'https', 8443, ':4443'), + array('https', null, 443, 'https', 8443, ''), + array('http', null, null, 'https', 8443, ''), + array('http', 8080, 4443, 'https', 8443, ':8080'), + array('http', 80, 4443, 'https', 8443, ''), + ); + } + + /** + * @dataProvider urlRedirectProvider + */ + public function testUrlRedirect($scheme, $httpPort, $httpsPort, $requestScheme, $requestPort, $expectedPort) + { + $host = 'www.example.com'; + $baseUrl = '/base'; + $path = '/redirect-path'; + $expectedUrl = "$scheme://$host$expectedPort$baseUrl$path"; + + $request = $this->createRequestObject($requestScheme, $host, $requestPort, $baseUrl); + $controller = $this->createRedirectController(); + + $returnValue = $controller->urlRedirectAction($request, $path, false, $scheme, $httpPort, $httpsPort); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + public function pathQueryParamsProvider() + { + return array( + array('http://www.example.com/base/redirect-path', '/redirect-path', ''), + array('http://www.example.com/base/redirect-path?foo=bar', '/redirect-path?foo=bar', ''), + array('http://www.example.com/base/redirect-path?foo=bar', '/redirect-path', 'foo=bar'), + array('http://www.example.com/base/redirect-path?foo=bar&abc=example', '/redirect-path?foo=bar', 'abc=example'), + array('http://www.example.com/base/redirect-path?foo=bar&abc=example&baz=def', '/redirect-path?foo=bar', 'abc=example&baz=def'), + ); + } + + /** + * @dataProvider pathQueryParamsProvider + */ + public function testPathQueryParams($expectedUrl, $path, $queryString) + { + $scheme = 'http'; + $host = 'www.example.com'; + $baseUrl = '/base'; + $port = 80; + + $request = $this->createRequestObject($scheme, $host, $port, $baseUrl, $queryString); + + $controller = $this->createRedirectController(); + + $returnValue = $controller->urlRedirectAction($request, $path, false, $scheme, $port, null); + $this->assertRedirectUrl($returnValue, $expectedUrl); + } + + private function createRequestObject($scheme, $host, $port, $baseUrl, $queryString = '') + { + $request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')->getMock(); + $request + ->expects($this->any()) + ->method('getScheme') + ->will($this->returnValue($scheme)); + $request + ->expects($this->any()) + ->method('getHost') + ->will($this->returnValue($host)); + $request + ->expects($this->any()) + ->method('getPort') + ->will($this->returnValue($port)); + $request + ->expects($this->any()) + ->method('getBaseUrl') + ->will($this->returnValue($baseUrl)); + $request + ->expects($this->any()) + ->method('getQueryString') + ->will($this->returnValue($queryString)); + + return $request; + } + + private function createRedirectController($httpPort = null, $httpsPort = null) + { + return new RedirectController(null, $httpPort, $httpsPort); + } + + /** + * @deprecated + */ + private function createLegacyRedirectController($httpPort = null, $httpsPort = null) + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + + if (null !== $httpPort) { + $container + ->expects($this->once()) + ->method('hasParameter') + ->with($this->equalTo('request_listener.http_port')) + ->will($this->returnValue(true)); + $container + ->expects($this->once()) + ->method('getParameter') + ->with($this->equalTo('request_listener.http_port')) + ->will($this->returnValue($httpPort)); + } + if (null !== $httpsPort) { + $container + ->expects($this->once()) + ->method('hasParameter') + ->with($this->equalTo('request_listener.https_port')) + ->will($this->returnValue(true)); + $container + ->expects($this->once()) + ->method('getParameter') + ->with($this->equalTo('request_listener.https_port')) + ->will($this->returnValue($httpsPort)); + } + + $controller = new RedirectController(); + $controller->setContainer($container); + + return $controller; + } + + private function assertRedirectUrl(Response $returnResponse, $expectedUrl) + { + $this->assertTrue($returnResponse->isRedirect($expectedUrl), "Expected: $expectedUrl\nGot: ".$returnResponse->headers->get('Location')); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Controller/TemplateControllerTest.php b/vendor/symfony/framework-bundle/Tests/Controller/TemplateControllerTest.php new file mode 100644 index 0000000..497c112 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Controller/TemplateControllerTest.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\Bundle\FrameworkBundle\Tests\Controller; + +use Symfony\Bundle\FrameworkBundle\Controller\TemplateController; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +/** + * @author Kévin Dunglas + */ +class TemplateControllerTest extends TestCase +{ + public function testTwig() + { + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $controller = new TemplateController($twig); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + public function testTemplating() + { + $templating = $this->getMockBuilder(EngineInterface::class)->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $controller = new TemplateController(null, $templating); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + /** + * @group legacy + */ + public function testLegacyTwig() + { + $twig = $this->getMockBuilder('Twig\Environment')->disableOriginalConstructor()->getMock(); + $twig->expects($this->once())->method('render')->willReturn('bar'); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); + $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); + $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); + + $controller = new TemplateController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + /** + * @group legacy + */ + public function testLegacyTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container->expects($this->at(0))->method('has')->willReturn(true); + $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); + + $controller = new TemplateController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->templateAction('mytemplate')->getContent()); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage You can not use the TemplateController if the Templating Component or the Twig Bundle are not available. + */ + public function testNoTwigNorTemplating() + { + $controller = new TemplateController(); + + $controller->templateAction('mytemplate')->getContent(); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.php new file mode 100644 index 0000000..08eaaed --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddCacheWarmerPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddCacheWarmerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @group legacy + */ +class AddCacheWarmerPassTest extends TestCase +{ + public function testThatCacheWarmersAreProcessedInPriorityOrder() + { + $container = new ContainerBuilder(); + $cacheWarmerDefinition = $container->register('cache_warmer')->addArgument(array()); + $container->register('my_cache_warmer_service1')->addTag('kernel.cache_warmer', array('priority' => 100)); + $container->register('my_cache_warmer_service2')->addTag('kernel.cache_warmer', array('priority' => 200)); + $container->register('my_cache_warmer_service3')->addTag('kernel.cache_warmer'); + + $addCacheWarmerPass = new AddCacheWarmerPass(); + $addCacheWarmerPass->process($container); + + $this->assertEquals( + array( + new Reference('my_cache_warmer_service2'), + new Reference('my_cache_warmer_service1'), + new Reference('my_cache_warmer_service3'), + ), + $cacheWarmerDefinition->getArgument(0) + ); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNoCacheWarmerDefinition() + { + $container = new ContainerBuilder(); + + $addCacheWarmerPass = new AddCacheWarmerPass(); + $addCacheWarmerPass->process($container); + + // we just check that the pass does not break if no cache warmer is registered + $this->addToAssertionCount(1); + } + + public function testThatCacheWarmersMightBeNotDefined() + { + $container = new ContainerBuilder(); + $cacheWarmerDefinition = $container->register('cache_warmer')->addArgument(array()); + + $addCacheWarmerPass = new AddCacheWarmerPass(); + $addCacheWarmerPass->process($container); + + $this->assertSame(array(), $cacheWarmerDefinition->getArgument(0)); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.php new file mode 100644 index 0000000..58a0da4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConsoleCommandPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * @group legacy + */ +class AddConsoleCommandPassTest extends TestCase +{ + /** + * @dataProvider visibilityProvider + */ + public function testProcess($public) + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + $container->setParameter('my-command.class', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); + + $definition = new Definition('%my-command.class%'); + $definition->setPublic($public); + $definition->addTag('console.command'); + $container->setDefinition('my-command', $definition); + + $container->compile(); + + $alias = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; + + if ($public) { + $this->assertFalse($container->hasAlias($alias)); + $id = 'my-command'; + } else { + $id = $alias; + // The alias is replaced by a Definition by the ReplaceAliasByActualDefinitionPass + // in case the original service is private + $this->assertFalse($container->hasDefinition('my-command')); + $this->assertTrue($container->hasDefinition($alias)); + } + + $this->assertTrue($container->hasParameter('console.command.ids')); + $this->assertSame(array($alias => $id), $container->getParameter('console.command.ids')); + } + + public function visibilityProvider() + { + return array( + array(true), + array(false), + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my-command" tagged "console.command" must not be abstract. + */ + public function testProcessThrowAnExceptionIfTheServiceIsAbstract() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + + $definition = new Definition('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'); + $definition->addTag('console.command'); + $definition->setAbstract(true); + $container->setDefinition('my-command', $definition); + + $container->compile(); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my-command" tagged "console.command" must be a subclass of "Symfony\Component\Console\Command\Command". + */ + public function testProcessThrowAnExceptionIfTheServiceIsNotASubclassOfCommand() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddConsoleCommandPass()); + + $definition = new Definition('SplObjectStorage'); + $definition->addTag('console.command'); + $container->setDefinition('my-command', $definition); + + $container->compile(); + } + + public function testProcessPrivateServicesWithSameCommand() + { + $container = new ContainerBuilder(); + $className = 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\MyCommand'; + + $definition1 = new Definition($className); + $definition1->addTag('console.command')->setPublic(false); + + $definition2 = new Definition($className); + $definition2->addTag('console.command')->setPublic(false); + + $container->setDefinition('my-command1', $definition1); + $container->setDefinition('my-command2', $definition2); + + (new AddConsoleCommandPass())->process($container); + + $alias1 = 'console.command.symfony_bundle_frameworkbundle_tests_dependencyinjection_compiler_mycommand'; + $alias2 = $alias1.'_my-command2'; + $this->assertTrue($container->hasAlias($alias1)); + $this->assertTrue($container->hasAlias($alias2)); + } +} + +class MyCommand extends Command +{ +} + +class ExtensionPresentBundle extends Bundle +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.php new file mode 100644 index 0000000..a529111 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddConstraintValidatorsPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; + +/** + * @group legacy + */ +class AddConstraintValidatorsPassTest extends TestCase +{ + public function testThatConstraintValidatorServicesAreProcessed() + { + $container = new ContainerBuilder(); + $validatorFactory = $container->register('validator.validator_factory') + ->addArgument(array()); + + $container->register('my_constraint_validator_service1', Validator1::class) + ->addTag('validator.constraint_validator', array('alias' => 'my_constraint_validator_alias1')); + $container->register('my_constraint_validator_service2', Validator2::class) + ->addTag('validator.constraint_validator'); + + $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); + $addConstraintValidatorsPass->process($container); + + $expected = (new Definition(ServiceLocator::class, array(array( + Validator1::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), + 'my_constraint_validator_alias1' => new ServiceClosureArgument(new Reference('my_constraint_validator_service1')), + Validator2::class => new ServiceClosureArgument(new Reference('my_constraint_validator_service2')), + ))))->addTag('container.service_locator')->setPublic(false); + $this->assertEquals($expected, $container->getDefinition((string) $validatorFactory->getArgument(0))); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "my_abstract_constraint_validator" tagged "validator.constraint_validator" must not be abstract. + */ + public function testAbstractConstraintValidator() + { + $container = new ContainerBuilder(); + $validatorFactory = $container->register('validator.validator_factory') + ->addArgument(array()); + + $container->register('my_abstract_constraint_validator') + ->setAbstract(true) + ->addTag('validator.constraint_validator'); + + $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); + $addConstraintValidatorsPass->process($container); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNoConstraintValidatorFactoryDefinition() + { + $addConstraintValidatorsPass = new AddConstraintValidatorsPass(); + $addConstraintValidatorsPass->process(new ContainerBuilder()); + + // we just check that the pass does not fail if no constraint validator factory is registered + $this->addToAssertionCount(1); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.php new file mode 100644 index 0000000..7f9724b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/AddExpressionLanguageProvidersPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddExpressionLanguageProvidersPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class AddExpressionLanguageProvidersPassTest extends TestCase +{ + public function testProcessForRouter() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('\stdClass'); + $definition->addTag('routing.expression_language_provider'); + $container->setDefinition('some_routing_provider', $definition->setPublic(true)); + + $container->register('router', '\stdClass')->setPublic(true); + $container->compile(); + + $router = $container->getDefinition('router'); + $calls = $router->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_routing_provider'), $calls[0][1][0]); + } + + public function testProcessForRouterAlias() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('\stdClass'); + $definition->addTag('routing.expression_language_provider'); + $container->setDefinition('some_routing_provider', $definition->setPublic(true)); + + $container->register('my_router', '\stdClass')->setPublic(true); + $container->setAlias('router', 'my_router'); + $container->compile(); + + $router = $container->getDefinition('my_router'); + $calls = $router->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('addExpressionLanguageProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_routing_provider'), $calls[0][1][0]); + } + + public function testProcessForSecurity() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('\stdClass'); + $definition->addTag('security.expression_language_provider'); + $container->setDefinition('some_security_provider', $definition->setPublic(true)); + + $container->register('security.expression_language', '\stdClass')->setPublic(true); + $container->compile(); + + $calls = $container->getDefinition('security.expression_language')->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('registerProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_security_provider'), $calls[0][1][0]); + } + + public function testProcessForSecurityAlias() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new AddExpressionLanguageProvidersPass()); + + $definition = new Definition('\stdClass'); + $definition->addTag('security.expression_language_provider'); + $container->setDefinition('some_security_provider', $definition->setPublic(true)); + + $container->register('my_security.expression_language', '\stdClass')->setPublic(true); + $container->setAlias('security.expression_language', 'my_security.expression_language'); + $container->compile(); + + $calls = $container->getDefinition('my_security.expression_language')->getMethodCalls(); + $this->assertCount(1, $calls); + $this->assertEquals('registerProvider', $calls[0][0]); + $this->assertEquals(new Reference('some_security_provider'), $calls[0][1][0]); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CacheCollectorPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CacheCollectorPassTest.php new file mode 100644 index 0000000..6a9438e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CacheCollectorPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CacheCollectorPass; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapter; +use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; +use Symfony\Component\Cache\DataCollector\CacheDataCollector; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class CacheCollectorPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container + ->register('fs', FilesystemAdapter::class) + ->addTag('cache.pool'); + $container + ->register('tagged_fs', TagAwareAdapter::class) + ->addArgument(new Reference('fs')) + ->addTag('cache.pool'); + + $collector = $container->register('data_collector.cache', CacheDataCollector::class); + (new CacheCollectorPass())->process($container); + + $this->assertEquals(array( + array('addInstance', array('fs', new Reference('fs'))), + array('addInstance', array('tagged_fs', new Reference('tagged_fs'))), + ), $collector->getMethodCalls()); + + $this->assertSame(TraceableAdapter::class, $container->findDefinition('fs')->getClass()); + $this->assertSame(TraceableTagAwareAdapter::class, $container->getDefinition('tagged_fs')->getClass()); + $this->assertFalse($collector->isPublic(), 'The "data_collector.cache" should be private after processing'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.php new file mode 100644 index 0000000..9230405 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolClearerPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolClearerPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; +use Symfony\Component\DependencyInjection\Compiler\RemoveUnusedDefinitionsPass; +use Symfony\Component\DependencyInjection\Compiler\RepeatedPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; + +class CachePoolClearerPassTest extends TestCase +{ + public function testPoolRefsAreWeak() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.root_dir', 'foo'); + + $globalClearer = new Definition(Psr6CacheClearer::class); + $container->setDefinition('cache.global_clearer', $globalClearer); + + $publicPool = new Definition(); + $publicPool->addArgument('namespace'); + $publicPool->addTag('cache.pool', array('clearer' => 'clearer_alias')); + $container->setDefinition('public.pool', $publicPool); + + $privatePool = new Definition(); + $privatePool->setPublic(false); + $privatePool->addArgument('namespace'); + $privatePool->addTag('cache.pool', array('clearer' => 'clearer_alias')); + $container->setDefinition('private.pool', $privatePool); + + $clearer = new Definition(); + $container->setDefinition('clearer', $clearer); + $container->setAlias('clearer_alias', 'clearer'); + + $pass = new RemoveUnusedDefinitionsPass(); + $pass->setRepeatedPass(new RepeatedPass(array($pass))); + foreach (array(new CachePoolPass(), $pass, new CachePoolClearerPass()) as $pass) { + $pass->process($container); + } + + $this->assertEquals(array(array('public.pool' => new Reference('public.pool'))), $clearer->getArguments()); + $this->assertEquals(array(array('public.pool' => new Reference('public.pool'))), $globalClearer->getArguments()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php new file mode 100644 index 0000000..4619301 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class CachePoolPassTest extends TestCase +{ + private $cachePoolPass; + + protected function setUp() + { + $this->cachePoolPass = new CachePoolPass(); + } + + public function testNamespaceArgumentIsReplaced() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.root_dir', 'foo'); + $adapter = new Definition(); + $adapter->setAbstract(true); + $adapter->addTag('cache.pool'); + $container->setDefinition('app.cache_adapter', $adapter); + $container->setAlias('app.cache_adapter_alias', 'app.cache_adapter'); + $cachePool = new ChildDefinition('app.cache_adapter_alias'); + $cachePool->addArgument(null); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertSame('D07rhFx97S', $cachePool->getArgument(0)); + } + + public function testNamespaceArgumentIsNotReplacedIfArrayAdapterIsUsed() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.root_dir', 'foo'); + + $container->register('cache.adapter.array', ArrayAdapter::class)->addArgument(0); + + $cachePool = new ChildDefinition('cache.adapter.array'); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertCount(0, $container->getDefinition('app.cache_pool')->getArguments()); + } + + public function testArgsAreReplaced() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('cache.prefix.seed', 'foo'); + $cachePool = new Definition(); + $cachePool->addTag('cache.pool', array( + 'provider' => 'foobar', + 'default_lifetime' => 3, + )); + $cachePool->addArgument(null); + $cachePool->addArgument(null); + $cachePool->addArgument(null); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertInstanceOf(Reference::class, $cachePool->getArgument(0)); + $this->assertSame('foobar', (string) $cachePool->getArgument(0)); + $this->assertSame('itantF+pIq', $cachePool->getArgument(1)); + $this->assertSame(3, $cachePool->getArgument(2)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid "cache.pool" tag for service "app.cache_pool": accepted attributes are + */ + public function testThrowsExceptionWhenCachePoolTagHasUnknownAttributes() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.root_dir', 'foo'); + $adapter = new Definition(); + $adapter->setAbstract(true); + $adapter->addTag('cache.pool'); + $container->setDefinition('app.cache_adapter', $adapter); + $cachePool = new ChildDefinition('app.cache_adapter'); + $cachePool->addTag('cache.pool', array('foobar' => 123)); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPrunerPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPrunerPassTest.php new file mode 100644 index 0000000..df9a49b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/CachePoolPrunerPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPrunerPass; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\PhpFilesAdapter; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class CachePoolPrunerPassTest extends TestCase +{ + public function testCompilerPassReplacesCommandArgument() + { + $container = new ContainerBuilder(); + $container->register('console.command.cache_pool_prune')->addArgument(array()); + $container->register('pool.foo', FilesystemAdapter::class)->addTag('cache.pool'); + $container->register('pool.bar', PhpFilesAdapter::class)->addTag('cache.pool'); + + $pass = new CachePoolPrunerPass(); + $pass->process($container); + + $expected = array( + 'pool.foo' => new Reference('pool.foo'), + 'pool.bar' => new Reference('pool.bar'), + ); + $argument = $container->getDefinition('console.command.cache_pool_prune')->getArgument(0); + + $this->assertInstanceOf(IteratorArgument::class, $argument); + $this->assertEquals($expected, $argument->getValues()); + } + + public function testCompilePassIsIgnoredIfCommandDoesNotExist() + { + $container = new ContainerBuilder(); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $pass = new CachePoolPrunerPass(); + $pass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Class "Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\NotFound" used for service "pool.not-found" cannot be found. + */ + public function testCompilerPassThrowsOnInvalidDefinitionClass() + { + $container = new ContainerBuilder(); + $container->register('console.command.cache_pool_prune')->addArgument(array()); + $container->register('pool.not-found', NotFound::class)->addTag('cache.pool'); + + $pass = new CachePoolPrunerPass(); + $pass->process($container); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.php new file mode 100644 index 0000000..482d881 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ConfigCachePassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConfigCachePass; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @group legacy + */ +class ConfigCachePassTest extends TestCase +{ + public function testThatCheckersAreProcessedInPriorityOrder() + { + $container = new ContainerBuilder(); + + $definition = $container->register('config_cache_factory')->addArgument(null); + $container->register('checker_2')->addTag('config_cache.resource_checker', array('priority' => 100)); + $container->register('checker_1')->addTag('config_cache.resource_checker', array('priority' => 200)); + $container->register('checker_3')->addTag('config_cache.resource_checker'); + + $pass = new ConfigCachePass(); + $pass->process($container); + + $expected = new IteratorArgument(array( + new Reference('checker_1'), + new Reference('checker_2'), + new Reference('checker_3'), + )); + $this->assertEquals($expected, $definition->getArgument(0)); + } + + public function testThatCheckersCanBeMissing() + { + $container = new ContainerBuilder(); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $pass = new ConfigCachePass(); + $pass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.php new file mode 100644 index 0000000..1adfdf2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ControllerArgumentValueResolverPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\HttpKernel\Controller\ArgumentResolver; + +/** + * @group legacy + */ +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/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.php new file mode 100644 index 0000000..2d6e8ca --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/DataCollectorTranslatorPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\DataCollectorTranslatorPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Translation\TranslatorInterface; + +class DataCollectorTranslatorPassTest extends TestCase +{ + private $container; + private $dataCollectorTranslatorPass; + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->dataCollectorTranslatorPass = new DataCollectorTranslatorPass(); + + $this->container->setParameter('translator_implementing_bag', 'Symfony\Component\Translation\Translator'); + $this->container->setParameter('translator_not_implementing_bag', 'Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'); + + $this->container->register('translator.data_collector', 'Symfony\Component\Translation\DataCollectorTranslator') + ->setPublic(false) + ->setDecoratedService('translator') + ->setArguments(array(new Reference('translator.data_collector.inner'))) + ; + + $this->container->register('data_collector.translation', 'Symfony\Component\Translation\DataCollector\TranslationDataCollector') + ->setArguments(array(new Reference('translator.data_collector'))) + ; + } + + /** + * @dataProvider getImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessKeepsDataCollectorTranslatorIfItImplementsTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('translator.data_collector')); + } + + /** + * @dataProvider getImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessKeepsDataCollectorIfTranslatorImplementsTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertTrue($this->container->hasDefinition('data_collector.translation')); + } + + public function getImplementingTranslatorBagInterfaceTranslatorClassNames() + { + return array( + array('Symfony\Component\Translation\Translator'), + array('%translator_implementing_bag%'), + ); + } + + /** + * @dataProvider getNotImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessRemovesDataCollectorTranslatorIfItDoesNotImplementTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertFalse($this->container->hasDefinition('translator.data_collector')); + } + + /** + * @dataProvider getNotImplementingTranslatorBagInterfaceTranslatorClassNames + */ + public function testProcessRemovesDataCollectorIfTranslatorDoesNotImplementTranslatorBagInterface($class) + { + $this->container->register('translator', $class); + + $this->dataCollectorTranslatorPass->process($this->container); + + $this->assertFalse($this->container->hasDefinition('data_collector.translation')); + } + + public function getNotImplementingTranslatorBagInterfaceTranslatorClassNames() + { + return array( + array('Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler\TranslatorWithTranslatorBag'), + array('%translator_not_implementing_bag%'), + ); + } +} + +class TranslatorWithTranslatorBag implements TranslatorInterface +{ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + } + + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + } + + public function setLocale($locale) + { + } + + public function getLocale() + { + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/FormPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/FormPassTest.php new file mode 100644 index 0000000..58dbd2e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/FormPassTest.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Form\AbstractType; + +/** + * @group legacy + * + * @author Bernhard Schussek + */ +class FormPassTest extends TestCase +{ + public function testDoNothingIfFormExtensionNotLoaded() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $container->compile(); + + $this->assertFalse($container->hasDefinition('form.extension')); + } + + public function testAddTaggedTypes() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $extDefinition->setPublic(true); + $extDefinition->setArguments(array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $container->setDefinition('form.extension', $extDefinition); + $container->register('my.type1', __CLASS__.'_Type1')->addTag('form.type')->setPublic(true); + $container->register('my.type2', __CLASS__.'_Type2')->addTag('form.type')->setPublic(true); + + $container->compile(); + + $extDefinition = $container->getDefinition('form.extension'); + + $this->assertEquals(array( + __CLASS__.'_Type1' => 'my.type1', + __CLASS__.'_Type2' => 'my.type2', + ), $extDefinition->getArgument(1)); + } + + /** + * @dataProvider addTaggedTypeExtensionsDataProvider + */ + public function testAddTaggedTypeExtensions(array $extensions, array $expectedRegisteredExtensions) + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( + new Reference('service_container'), + array(), + array(), + array(), + )); + $extDefinition->setPublic(true); + + $container->setDefinition('form.extension', $extDefinition); + + foreach ($extensions as $serviceId => $tag) { + $container->register($serviceId, 'stdClass')->addTag('form.type_extension', $tag); + } + + $container->compile(); + + $extDefinition = $container->getDefinition('form.extension'); + $this->assertSame($expectedRegisteredExtensions, $extDefinition->getArgument(2)); + } + + /** + * @return array + */ + public function addTaggedTypeExtensionsDataProvider() + { + return array( + array( + array( + 'my.type_extension1' => array('extended_type' => 'type1'), + 'my.type_extension2' => array('extended_type' => 'type1'), + 'my.type_extension3' => array('extended_type' => 'type2'), + ), + array( + 'type1' => array('my.type_extension1', 'my.type_extension2'), + 'type2' => array('my.type_extension3'), + ), + ), + array( + array( + 'my.type_extension1' => array('extended_type' => 'type1', 'priority' => 1), + 'my.type_extension2' => array('extended_type' => 'type1', 'priority' => 2), + 'my.type_extension3' => array('extended_type' => 'type1', 'priority' => -1), + 'my.type_extension4' => array('extended_type' => 'type2', 'priority' => 2), + 'my.type_extension5' => array('extended_type' => 'type2', 'priority' => 1), + 'my.type_extension6' => array('extended_type' => 'type2', 'priority' => 1), + ), + array( + 'type1' => array('my.type_extension2', 'my.type_extension1', 'my.type_extension3'), + 'type2' => array('my.type_extension4', 'my.type_extension5', 'my.type_extension6'), + ), + ), + ); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage extended-type attribute, none was configured for the "my.type_extension" service + */ + public function testAddTaggedFormTypeExtensionWithoutExtendedTypeAttribute() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension', array( + new Reference('service_container'), + array(), + array(), + array(), + )); + $extDefinition->setPublic(true); + + $container->setDefinition('form.extension', $extDefinition); + $container->register('my.type_extension', 'stdClass') + ->addTag('form.type_extension'); + + $container->compile(); + } + + public function testAddTaggedGuessers() + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $extDefinition->setPublic(true); + $extDefinition->setArguments(array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $definition1 = new Definition('stdClass'); + $definition1->addTag('form.type_guesser'); + $definition2 = new Definition('stdClass'); + $definition2->addTag('form.type_guesser'); + + $container->setDefinition('form.extension', $extDefinition); + $container->setDefinition('my.guesser1', $definition1); + $container->setDefinition('my.guesser2', $definition2); + + $container->compile(); + + $extDefinition = $container->getDefinition('form.extension'); + + $this->assertSame(array( + 'my.guesser1', + 'my.guesser2', + ), $extDefinition->getArgument(3)); + } + + /** + * @dataProvider privateTaggedServicesProvider + */ + public function testPrivateTaggedServices($id, $tagName) + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new FormPass()); + + $extDefinition = new Definition('Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension'); + $extDefinition->setArguments(array( + new Reference('service_container'), + array(), + array(), + array(), + )); + + $container->setDefinition('form.extension', $extDefinition); + $container->register($id, 'stdClass')->setPublic(false)->addTag($tagName, array('extended_type' => 'Foo')); + + $container->compile(); + $this->assertTrue($container->getDefinition($id)->isPublic()); + } + + public function privateTaggedServicesProvider() + { + return array( + array('my.type', 'form.type'), + array('my.type_extension', 'form.type_extension'), + array('my.guesser', 'form.type_guesser'), + ); + } +} + +class FormPassTest_Type1 extends AbstractType +{ +} + +class FormPassTest_Type2 extends AbstractType +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php new file mode 100644 index 0000000..7788df0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\LoggingTranslatorPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class LoggingTranslatorPassTest extends TestCase +{ + public function testProcess() + { + $container = new ContainerBuilder(); + $container->setParameter('translator.logging', true); + $container->setParameter('translator.class', 'Symfony\Component\Translation\Translator'); + $container->register('monolog.logger'); + $container->setAlias('logger', 'monolog.logger'); + $container->register('translator.default', '%translator.class%'); + $container->register('translator.logging', '%translator.class%'); + $container->setAlias('translator', 'translator.default'); + $translationWarmerDefinition = $container->register('translation.warmer') + ->addArgument(new Reference('translator')) + ->addTag('container.service_subscriber', array('id' => 'translator')) + ->addTag('container.service_subscriber', array('id' => 'foo')); + + $pass = new LoggingTranslatorPass(); + $pass->process($container); + + $this->assertEquals( + array('container.service_subscriber' => array( + array('id' => 'foo'), + array('key' => 'translator', 'id' => 'translator.logging.inner'), + )), + $translationWarmerDefinition->getTags() + ); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNotLoggerDefinition() + { + $container = new ContainerBuilder(); + $container->register('identity_translator'); + $container->setAlias('translator', 'identity_translator'); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $pass = new LoggingTranslatorPass(); + $pass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } + + public function testThatCompilerPassIsIgnoredIfThereIsNotTranslatorDefinition() + { + $container = new ContainerBuilder(); + $container->register('monolog.logger'); + $container->setAlias('logger', 'monolog.logger'); + + $definitionsBefore = \count($container->getDefinitions()); + $aliasesBefore = \count($container->getAliases()); + + $pass = new LoggingTranslatorPass(); + $pass->process($container); + + // the container is untouched (i.e. no new definitions or aliases) + $this->assertCount($definitionsBefore, $container->getDefinitions()); + $this->assertCount($aliasesBefore, $container->getAliases()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.php new file mode 100644 index 0000000..f46779a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/ProfilerPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ProfilerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class ProfilerPassTest extends TestCase +{ + /** + * Tests that collectors that specify a template but no "id" will throw + * an exception (both are needed if the template is specified). + * + * Thus, a fully-valid tag looks something like this: + * + * + * + * @expectedException \InvalidArgumentException + */ + public function testTemplateNoIdThrowsException() + { + $builder = new ContainerBuilder(); + $builder->register('profiler', 'ProfilerClass'); + $builder->register('my_collector_service') + ->addTag('data_collector', array('template' => 'foo')); + + $profilerPass = new ProfilerPass(); + $profilerPass->process($builder); + } + + public function testValidCollector() + { + $container = new ContainerBuilder(); + $profilerDefinition = $container->register('profiler', 'ProfilerClass'); + $container->register('my_collector_service') + ->addTag('data_collector', array('template' => 'foo', 'id' => 'my_collector')); + + $profilerPass = new ProfilerPass(); + $profilerPass->process($container); + + $this->assertSame(array('my_collector_service' => array('my_collector', 'foo')), $container->getParameter('data_collector.templates')); + + // grab the method calls off of the "profiler" definition + $methodCalls = $profilerDefinition->getMethodCalls(); + $this->assertCount(1, $methodCalls); + $this->assertEquals('add', $methodCalls[0][0]); // grab the method part of the first call + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.php new file mode 100644 index 0000000..c7b2e39 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/PropertyInfoPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @group legacy + */ +class PropertyInfoPassTest extends TestCase +{ + /** + * @dataProvider provideTags + */ + public function testServicesAreOrderedAccordingToPriority($index, $tag) + { + $container = new ContainerBuilder(); + + $definition = $container->register('property_info')->setArguments(array(null, null, null, null)); + $container->register('n2')->addTag($tag, array('priority' => 100)); + $container->register('n1')->addTag($tag, array('priority' => 200)); + $container->register('n3')->addTag($tag); + + $propertyInfoPass = new PropertyInfoPass(); + $propertyInfoPass->process($container); + + $expected = new IteratorArgument(array( + new Reference('n1'), + new Reference('n2'), + new Reference('n3'), + )); + $this->assertEquals($expected, $definition->getArgument($index)); + } + + public function provideTags() + { + return array( + array(0, 'property_info.list_extractor'), + array(1, 'property_info.type_extractor'), + array(2, 'property_info.description_extractor'), + array(3, 'property_info.access_extractor'), + ); + } + + public function testReturningEmptyArrayWhenNoService() + { + $container = new ContainerBuilder(); + $propertyInfoExtractorDefinition = $container->register('property_info') + ->setArguments(array(array(), array(), array(), array())); + + $propertyInfoPass = new PropertyInfoPass(); + $propertyInfoPass->process($container); + + $this->assertEquals(new IteratorArgument(array()), $propertyInfoExtractorDefinition->getArgument(0)); + $this->assertEquals(new IteratorArgument(array()), $propertyInfoExtractorDefinition->getArgument(1)); + $this->assertEquals(new IteratorArgument(array()), $propertyInfoExtractorDefinition->getArgument(2)); + $this->assertEquals(new IteratorArgument(array()), $propertyInfoExtractorDefinition->getArgument(3)); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/SerializerPassTest.php new file mode 100644 index 0000000..d259f81 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/SerializerPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Tests for the SerializerPass class. + * + * @group legacy + * + * @author Javier Lopez + */ +class SerializerPassTest extends TestCase +{ + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must tag at least one service as "serializer.normalizer" to use the "serializer" service + */ + public function testThrowExceptionWhenNoNormalizers() + { + $container = new ContainerBuilder(); + $container->register('serializer'); + + $serializerPass = new SerializerPass(); + $serializerPass->process($container); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage You must tag at least one service as "serializer.encoder" to use the "serializer" service + */ + public function testThrowExceptionWhenNoEncoders() + { + $container = new ContainerBuilder(); + $container->register('serializer') + ->addArgument(array()) + ->addArgument(array()); + $container->register('normalizer')->addTag('serializer.normalizer'); + + $serializerPass = new SerializerPass(); + $serializerPass->process($container); + } + + public function testServicesAreOrderedAccordingToPriority() + { + $container = new ContainerBuilder(); + + $definition = $container->register('serializer')->setArguments(array(null, null)); + $container->register('n2')->addTag('serializer.normalizer', array('priority' => 100))->addTag('serializer.encoder', array('priority' => 100)); + $container->register('n1')->addTag('serializer.normalizer', array('priority' => 200))->addTag('serializer.encoder', array('priority' => 200)); + $container->register('n3')->addTag('serializer.normalizer')->addTag('serializer.encoder'); + + $serializerPass = new SerializerPass(); + $serializerPass->process($container); + + $expected = array( + new Reference('n1'), + new Reference('n2'), + new Reference('n3'), + ); + $this->assertEquals($expected, $definition->getArgument(0)); + $this->assertEquals($expected, $definition->getArgument(1)); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.php new file mode 100644 index 0000000..d31fb18 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/TranslatorPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TranslatorPass; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * @group legacy + */ +class TranslatorPassTest extends TestCase +{ + public function testValidCollector() + { + $loader = (new Definition()) + ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')); + + $translator = (new Definition()) + ->setArguments(array(null, null, null, null)); + + $container = new ContainerBuilder(); + $container->setDefinition('translator.default', $translator); + $container->setDefinition('translation.loader', $loader); + + $pass = new TranslatorPass(); + $pass->process($container); + + $expected = (new Definition()) + ->addTag('translation.loader', array('alias' => 'xliff', 'legacy-alias' => 'xlf')) + ->addMethodCall('addLoader', array('xliff', new Reference('translation.loader'))) + ->addMethodCall('addLoader', array('xlf', new Reference('translation.loader'))) + ; + $this->assertEquals($expected, $loader); + + $this->assertSame(array('translation.loader' => array('xliff', 'xlf')), $translator->getArgument(3)); + + $expected = array('translation.loader' => new ServiceClosureArgument(new Reference('translation.loader'))); + $this->assertEquals($expected, $container->getDefinition((string) $translator->getArgument(0))->getArgument(0)); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.php new file mode 100644 index 0000000..d91c806 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/UnusedTagsPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\UnusedTagsPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class UnusedTagsPassTest extends TestCase +{ + public function testProcess() + { + $pass = new UnusedTagsPass(); + + $container = new ContainerBuilder(); + $container->register('foo') + ->addTag('kenrel.event_subscriber'); + $container->register('bar') + ->addTag('kenrel.event_subscriber'); + + $pass->process($container); + + $this->assertSame(array(sprintf('%s: Tag "kenrel.event_subscriber" was defined on service(s) "foo", "bar", but was never used. Did you mean "kernel.event_subscriber"?', UnusedTagsPass::class)), $container->getCompiler()->getLog()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.php new file mode 100644 index 0000000..44c87b1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Compiler/WorkflowGuardListenerPassTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\WorkflowGuardListenerPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Core\Role\RoleHierarchy; +use Symfony\Component\Validator\Validator\ValidatorInterface; + +class WorkflowGuardListenerPassTest extends TestCase +{ + private $container; + private $compilerPass; + + protected function setUp() + { + $this->container = new ContainerBuilder(); + $this->compilerPass = new WorkflowGuardListenerPass(); + } + + public function testNoExeptionIfParameterIsNotSet() + { + $this->compilerPass->process($this->container); + + $this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners')); + } + + public function testNoExeptionIfAllDependenciesArePresent() + { + $this->container->setParameter('workflow.has_guard_listeners', true); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + $this->container->register('validator', ValidatorInterface::class); + + $this->compilerPass->process($this->container); + + $this->assertFalse($this->container->hasParameter('workflow.has_guard_listeners')); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.token_storage" service is needed to be able to use the workflow guard listener. + */ + public function testExceptionIfTheTokenStorageServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', true); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.authorization_checker" service is needed to be able to use the workflow guard listener. + */ + public function testExceptionIfTheAuthorizationCheckerServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', true); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.authentication.trust_resolver" service is needed to be able to use the workflow guard listener. + */ + public function testExceptionIfTheAuthenticationTrustResolverServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', true); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.role_hierarchy', RoleHierarchy::class); + + $this->compilerPass->process($this->container); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException + * @expectedExceptionMessage The "security.role_hierarchy" service is needed to be able to use the workflow guard listener. + */ + public function testExceptionIfTheRoleHierarchyServiceIsNotPresent() + { + $this->container->setParameter('workflow.has_guard_listeners', true); + $this->container->register('security.token_storage', TokenStorageInterface::class); + $this->container->register('security.authorization_checker', AuthorizationCheckerInterface::class); + $this->container->register('security.authentication.trust_resolver', AuthenticationTrustResolverInterface::class); + + $this->compilerPass->process($this->container); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/ConfigurationTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/ConfigurationTest.php new file mode 100644 index 0000000..0c01676 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/ConfigurationTest.php @@ -0,0 +1,408 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Configuration; +use Symfony\Bundle\FullStack; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Definition\Processor; +use Symfony\Component\Lock\Store\SemaphoreStore; + +class ConfigurationTest extends TestCase +{ + public function testDefaultConfig() + { + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(true), array(array('secret' => 's3cr3t'))); + + $this->assertEquals( + array_merge(array('secret' => 's3cr3t', 'trusted_hosts' => array()), self::getBundleDefaultConfig()), + $config + ); + } + + public function testDoNoDuplicateDefaultFormResources() + { + $input = array('templating' => array( + 'form' => array('resources' => array('FrameworkBundle:Form')), + 'engines' => array('php'), + )); + + $processor = new Processor(); + $config = $processor->processConfiguration(new Configuration(true), array($input)); + + $this->assertEquals(array('FrameworkBundle:Form'), $config['templating']['form']['resources']); + } + + /** + * @group legacy + * @expectedDeprecation The "framework.trusted_proxies" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead. + */ + public function testTrustedProxiesSetToNullIsDeprecated() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array(array('trusted_proxies' => null))); + } + + /** + * @group legacy + * @expectedDeprecation The "framework.trusted_proxies" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead. + */ + public function testTrustedProxiesSetToEmptyArrayIsDeprecated() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array(array('trusted_proxies' => array()))); + } + + /** + * @group legacy + * @expectedDeprecation The "framework.trusted_proxies" configuration key has been deprecated in Symfony 3.3. Use the Request::setTrustedProxies() method in your front controller instead. + */ + public function testTrustedProxiesSetToNonEmptyArrayIsInvalid() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array(array('trusted_proxies' => array('127.0.0.1')))); + } + + /** + * @group legacy + * @dataProvider getTestValidSessionName + */ + public function testValidSessionName($sessionName) + { + $processor = new Processor(); + $config = $processor->processConfiguration( + new Configuration(true), + array(array('session' => array('name' => $sessionName))) + ); + + $this->assertEquals($sessionName, $config['session']['name']); + } + + public function getTestValidSessionName() + { + return array( + array(null), + array('PHPSESSID'), + array('a&b'), + array(',_-!@#$%^*(){}:<>/?'), + ); + } + + /** + * @dataProvider getTestInvalidSessionName + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidSessionName($sessionName) + { + $processor = new Processor(); + $processor->processConfiguration( + new Configuration(true), + array(array('session' => array('name' => $sessionName))) + ); + } + + public function getTestInvalidSessionName() + { + return array( + array('a.b'), + array('a['), + array('a[]'), + array('a[b]'), + array('a=b'), + array('a+b'), + ); + } + + /** + * @dataProvider getTestValidTrustedProxiesData + * @group legacy + */ + public function testValidTrustedProxies($trustedProxies, $processedProxies) + { + $processor = new Processor(); + $configuration = new Configuration(true); + $config = $processor->processConfiguration($configuration, array(array( + 'secret' => 's3cr3t', + 'trusted_proxies' => $trustedProxies, + ))); + + $this->assertEquals($processedProxies, $config['trusted_proxies']); + } + + public function getTestValidTrustedProxiesData() + { + return array( + array(array('127.0.0.1'), array('127.0.0.1')), + array(array('::1'), array('::1')), + array(array('127.0.0.1', '::1'), array('127.0.0.1', '::1')), + array(null, array()), + array(false, array()), + array(array(), array()), + array(array('10.0.0.0/8'), array('10.0.0.0/8')), + array(array('::ffff:0:0/96'), array('::ffff:0:0/96')), + array(array('0.0.0.0/0'), array('0.0.0.0/0')), + ); + } + + /** + * @group legacy + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidTypeTrustedProxies() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => 'Not an IP address', + ), + )); + } + + /** + * @group legacy + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testInvalidValueTrustedProxies() + { + $processor = new Processor(); + $configuration = new Configuration(true); + + $processor->processConfiguration($configuration, array( + array( + 'secret' => 's3cr3t', + 'trusted_proxies' => array('Not an IP address'), + ), + )); + } + + public function testAssetsCanBeEnabled() + { + $processor = new Processor(); + $configuration = new Configuration(true); + $config = $processor->processConfiguration($configuration, array(array('assets' => null))); + + $defaultConfig = array( + 'enabled' => true, + 'version_strategy' => null, + 'version' => null, + 'version_format' => '%%s?%%s', + 'base_path' => '', + 'base_urls' => array(), + 'packages' => array(), + 'json_manifest_path' => null, + ); + + $this->assertEquals($defaultConfig, $config['assets']); + } + + /** + * @dataProvider provideInvalidAssetConfigurationTests + */ + public function testInvalidAssetsConfiguration(array $assetConfig, $expectedMessage) + { + if (method_exists($this, 'expectException')) { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage($expectedMessage); + } else { + $this->setExpectedException(InvalidConfigurationException::class, $expectedMessage); + } + + $processor = new Processor(); + $configuration = new Configuration(true); + $processor->processConfiguration($configuration, array( + array( + 'assets' => $assetConfig, + ), + )); + } + + public function provideInvalidAssetConfigurationTests() + { + // helper to turn config into embedded package config + $createPackageConfig = function (array $packageConfig) { + return array( + 'base_urls' => '//example.com', + 'version' => 1, + 'packages' => array( + 'foo' => $packageConfig, + ), + ); + }; + + $config = array( + 'version' => 1, + 'version_strategy' => 'foo', + ); + yield array($config, 'You cannot use both "version_strategy" and "version" at the same time under "assets".'); + yield array($createPackageConfig($config), 'You cannot use both "version_strategy" and "version" at the same time under "assets" packages.'); + + $config = array( + 'json_manifest_path' => '/foo.json', + 'version_strategy' => 'foo', + ); + yield array($config, 'You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets".'); + yield array($createPackageConfig($config), 'You cannot use both "version_strategy" and "json_manifest_path" at the same time under "assets" packages.'); + + $config = array( + 'json_manifest_path' => '/foo.json', + 'version' => '1', + ); + yield array($config, 'You cannot use both "version" and "json_manifest_path" at the same time under "assets".'); + yield array($createPackageConfig($config), 'You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.'); + } + + protected static function getBundleDefaultConfig() + { + return array( + 'http_method_override' => true, + 'trusted_proxies' => array(), + 'ide' => null, + 'default_locale' => 'en', + 'csrf_protection' => array( + 'enabled' => false, + ), + 'form' => array( + 'enabled' => !class_exists(FullStack::class), + 'csrf_protection' => array( + 'enabled' => null, // defaults to csrf_protection.enabled + 'field_name' => '_token', + ), + ), + 'esi' => array('enabled' => false), + 'ssi' => array('enabled' => false), + 'fragments' => array( + 'enabled' => false, + 'path' => '/_fragment', + ), + 'profiler' => array( + 'enabled' => false, + 'only_exceptions' => false, + 'only_master_requests' => false, + 'dsn' => 'file:%kernel.cache_dir%/profiler', + 'collect' => true, + 'matcher' => array( + 'enabled' => false, + 'ips' => array(), + ), + ), + 'translator' => array( + 'enabled' => !class_exists(FullStack::class), + 'fallbacks' => array('en'), + 'logging' => true, + 'formatter' => 'translator.formatter.default', + 'paths' => array(), + 'default_path' => '%kernel.project_dir%/translations', + ), + 'validation' => array( + 'enabled' => !class_exists(FullStack::class), + 'enable_annotations' => !class_exists(FullStack::class), + 'static_method' => array('loadValidatorMetadata'), + 'translation_domain' => 'validators', + 'strict_email' => false, + 'mapping' => array( + 'paths' => array(), + ), + ), + 'annotations' => array( + 'cache' => 'php_array', + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + 'debug' => true, + 'enabled' => true, + ), + 'serializer' => array( + 'enabled' => !class_exists(FullStack::class), + 'enable_annotations' => !class_exists(FullStack::class), + 'mapping' => array('paths' => array()), + ), + 'property_access' => array( + 'magic_call' => false, + 'throw_exception_on_invalid_index' => false, + ), + 'property_info' => array( + 'enabled' => false, + ), + 'router' => array( + 'enabled' => false, + 'http_port' => 80, + 'https_port' => 443, + 'strict_requirements' => true, + ), + 'session' => array( + 'enabled' => false, + 'storage_id' => 'session.storage.native', + 'handler_id' => 'session.handler.native_file', + 'cookie_httponly' => true, + 'gc_probability' => 1, + 'save_path' => '%kernel.cache_dir%/sessions', + 'metadata_update_threshold' => '0', + 'use_strict_mode' => true, + ), + 'request' => array( + 'enabled' => false, + 'formats' => array(), + ), + 'templating' => array( + 'enabled' => false, + 'hinclude_default_template' => null, + 'form' => array( + 'resources' => array('FrameworkBundle:Form'), + ), + 'engines' => array(), + 'loaders' => array(), + ), + 'assets' => array( + 'enabled' => !class_exists(FullStack::class), + 'version_strategy' => null, + 'version' => null, + 'version_format' => '%%s?%%s', + 'base_path' => '', + 'base_urls' => array(), + 'packages' => array(), + 'json_manifest_path' => null, + ), + 'cache' => array( + 'pools' => array(), + 'app' => 'cache.adapter.filesystem', + 'system' => 'cache.adapter.system', + 'directory' => '%kernel.cache_dir%/pools', + 'default_redis_provider' => 'redis://localhost', + 'default_memcached_provider' => 'memcached://localhost', + ), + 'workflows' => array( + 'enabled' => false, + 'workflows' => array(), + ), + 'php_errors' => array( + 'log' => true, + 'throw' => true, + ), + 'web_link' => array( + 'enabled' => !class_exists(FullStack::class), + ), + 'lock' => array( + 'enabled' => !class_exists(FullStack::class), + 'resources' => array( + 'default' => array( + class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphore' : 'flock', + ), + ), + ), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/Resources/config/validation.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.php new file mode 100644 index 0000000..166b606 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/CustomPathBundle/src/CustomPathBundle.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\Bundle\FrameworkBundle\Tests; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class CustomPathBundle extends Bundle +{ + public function getPath() + { + return __DIR__.'/..'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serialization.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/files/foo.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/files/foo.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.php new file mode 100644 index 0000000..2f090b2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/TestBundle/TestBundle.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\Bundle\FrameworkBundle\Tests; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class TestBundle extends Bundle +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets.php new file mode 100644 index 0000000..dc6bf7b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets.php @@ -0,0 +1,32 @@ +loadFromExtension('framework', array( + 'assets' => array( + 'version' => 'SomeVersionScheme', + 'base_urls' => 'http://cdn.example.com', + 'version_format' => '%%s?version=%%s', + 'packages' => array( + 'images_path' => array( + 'base_path' => '/foo', + ), + 'images' => array( + 'version' => '1.0.0', + 'base_urls' => array('http://images1.example.com', 'http://images2.example.com'), + ), + 'foo' => array( + 'version' => '1.0.0', + 'version_format' => '%%s-%%s', + ), + 'bar' => array( + 'base_urls' => array('https://bar2.example.com'), + ), + 'bar_version_strategy' => array( + 'base_urls' => array('https://bar2.example.com'), + 'version_strategy' => 'assets.custom_version_strategy', + ), + 'json_manifest_strategy' => array( + 'json_manifest_path' => '/path/to/manifest.json', + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php new file mode 100644 index 0000000..3ade704 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'assets' => array( + 'enabled' => false, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_version_strategy_as_service.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_version_strategy_as_service.php new file mode 100644 index 0000000..4f9123a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/assets_version_strategy_as_service.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'assets' => array( + 'version_strategy' => 'assets.custom_version_strategy', + 'base_urls' => 'http://cdn.example.com', + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache.php new file mode 100644 index 0000000..ef7a1be --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache.php @@ -0,0 +1,29 @@ +loadFromExtension('framework', array( + 'cache' => array( + 'pools' => array( + 'cache.foo' => array( + 'adapter' => 'cache.adapter.apcu', + 'default_lifetime' => 30, + ), + 'cache.bar' => array( + 'adapter' => 'cache.adapter.doctrine', + 'default_lifetime' => 5, + 'provider' => 'app.doctrine_cache_provider', + ), + 'cache.baz' => array( + 'adapter' => 'cache.adapter.filesystem', + 'default_lifetime' => 7, + ), + 'cache.foobar' => array( + 'adapter' => 'cache.adapter.psr6', + 'default_lifetime' => 10, + 'provider' => 'app.cache_pool', + ), + 'cache.def' => array( + 'default_lifetime' => 11, + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php new file mode 100644 index 0000000..b93ade8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/cache_env_var.php @@ -0,0 +1,9 @@ +setParameter('env(REDIS_URL)', 'redis://paas.com'); + +$container->loadFromExtension('framework', array( + 'cache' => array( + 'default_redis_provider' => '%env(REDIS_URL)%', + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf.php new file mode 100644 index 0000000..497ceb2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'csrf_protection' => true, + 'form' => true, + 'session' => array( + 'handler_id' => null, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php new file mode 100644 index 0000000..d1df1c5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/csrf_needs_session.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'csrf_protection' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/default_config.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/default_config.php new file mode 100644 index 0000000..cd2e56b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/default_config.php @@ -0,0 +1,3 @@ +loadFromExtension('framework', array()); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_and_ssi_without_fragments.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_and_ssi_without_fragments.php new file mode 100644 index 0000000..b8f3aa0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_and_ssi_without_fragments.php @@ -0,0 +1,13 @@ +loadFromExtension('framework', array( + 'fragments' => array( + 'enabled' => false, + ), + 'esi' => array( + 'enabled' => true, + ), + 'ssi' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_disabled.php new file mode 100644 index 0000000..1fb5936 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/esi_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'esi' => array( + 'enabled' => false, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php new file mode 100644 index 0000000..7360c49 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/form_no_csrf.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'form' => array( + 'csrf_protection' => array( + 'enabled' => false, + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/full.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/full.php new file mode 100644 index 0000000..64de930 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -0,0 +1,84 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'default_locale' => 'fr', + 'csrf_protection' => true, + 'form' => array( + 'csrf_protection' => array( + 'field_name' => '_csrf', + ), + ), + 'http_method_override' => false, + 'esi' => array( + 'enabled' => true, + ), + 'ssi' => array( + 'enabled' => true, + ), + 'profiler' => array( + 'only_exceptions' => true, + 'enabled' => false, + ), + 'router' => array( + 'resource' => '%kernel.project_dir%/config/routing.xml', + 'type' => 'xml', + ), + 'session' => array( + 'storage_id' => 'session.storage.native', + 'handler_id' => 'session.handler.native_file', + 'name' => '_SYMFONY', + 'cookie_lifetime' => 86400, + 'cookie_path' => '/', + 'cookie_domain' => 'example.com', + 'cookie_secure' => true, + 'cookie_httponly' => false, + 'use_cookies' => true, + 'gc_maxlifetime' => 90000, + 'gc_divisor' => 108, + 'gc_probability' => 1, + 'save_path' => '/path/to/sessions', + ), + 'templating' => array( + 'cache' => '/path/to/cache', + 'engines' => array('php', 'twig'), + 'loader' => array('loader.foo', 'loader.bar'), + 'form' => array( + 'resources' => array('theme1', 'theme2'), + ), + 'hinclude_default_template' => 'global_hinclude_template', + ), + 'assets' => array( + 'version' => 'v1', + ), + 'translator' => array( + 'enabled' => true, + 'fallback' => 'fr', + 'paths' => array('%kernel.project_dir%/Fixtures/translations'), + ), + 'validation' => array( + 'enabled' => true, + ), + 'annotations' => array( + 'cache' => 'file', + 'debug' => true, + 'file_cache_dir' => '%kernel.cache_dir%/annotations', + ), + 'serializer' => array( + 'enabled' => true, + 'enable_annotations' => true, + 'name_converter' => 'serializer.name_converter.camel_case_to_snake_case', + 'circular_reference_handler' => 'my.circular.reference.handler', + ), + 'property_info' => true, + 'ide' => 'file%%link%%format', + 'request' => array( + 'formats' => array( + 'csv' => array( + 'text/csv', + 'text/plain', + ), + 'pdf' => 'application/pdf', + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_disabled.php new file mode 100644 index 0000000..1338ec5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_disabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'php_errors' => array( + 'log' => false, + 'throw' => false, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_enabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_enabled.php new file mode 100644 index 0000000..a33875e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/php_errors_enabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'php_errors' => array( + 'log' => true, + 'throw' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/profiler.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/profiler.php new file mode 100644 index 0000000..6615aa7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/profiler.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'profiler' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php new file mode 100644 index 0000000..4340e61 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'property_access' => array( + 'magic_call' => true, + 'throw_exception_on_invalid_index' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_info.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_info.php new file mode 100644 index 0000000..847e15f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/property_info.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'property_info' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/request.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/request.php new file mode 100644 index 0000000..1e7cb29 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/request.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'request' => array( + 'formats' => array(), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php new file mode 100644 index 0000000..dedd090 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'serializer' => array( + 'enabled' => false, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php new file mode 100644 index 0000000..eadad57 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_enabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'serializer' => array( + 'enabled' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_legacy_cache.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_legacy_cache.php new file mode 100644 index 0000000..65ddd32 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_legacy_cache.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'serializer' => array( + 'enabled' => true, + 'cache' => 'foo', + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php new file mode 100644 index 0000000..f2b928f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/serializer_mapping.php @@ -0,0 +1,15 @@ +loadFromExtension('framework', array( + 'annotations' => array('enabled' => true), + 'serializer' => array( + 'enable_annotations' => true, + 'mapping' => array( + 'paths' => array( + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files', + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml', + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml', + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/session.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/session.php new file mode 100644 index 0000000..1041837 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/session.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'session' => array( + 'handler_id' => null, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/ssi_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/ssi_disabled.php new file mode 100644 index 0000000..4d61d82 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/ssi_disabled.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'ssi' => array( + 'enabled' => false, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_disabled.php new file mode 100644 index 0000000..f76d8ad --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_disabled.php @@ -0,0 +1,5 @@ +loadFromExtension('framework', array( + 'templating' => false, +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_no_assets.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_no_assets.php new file mode 100644 index 0000000..bf12a8b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_no_assets.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'templating' => array( + 'engines' => array('php', 'twig'), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_assets_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_assets_disabled.php new file mode 100644 index 0000000..535a9a2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_assets_disabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'assets' => false, + 'templating' => array( + 'engines' => array('php'), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php new file mode 100644 index 0000000..4fb2aec --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_disabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'translator' => false, + 'templating' => array( + 'engines' => array('php'), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php new file mode 100644 index 0000000..b8053c8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/templating_php_translator_enabled.php @@ -0,0 +1,8 @@ +loadFromExtension('framework', array( + 'translator' => true, + 'templating' => array( + 'engines' => array('php'), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php new file mode 100644 index 0000000..0abe3a4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/translator_fallbacks.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'translator' => array( + 'fallbacks' => array('en', 'fr'), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php new file mode 100644 index 0000000..35a0b3d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_annotations.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'validation' => array( + 'enabled' => true, + 'enable_annotations' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php new file mode 100644 index 0000000..5d44c6c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_mapping.php @@ -0,0 +1,13 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'mapping' => array( + 'paths' => array( + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files', + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml', + '%kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml', + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php new file mode 100644 index 0000000..476da79 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_multiple_static_methods.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'validation' => array( + 'enabled' => true, + 'static_method' => array('loadFoo', 'loadBar'), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php new file mode 100644 index 0000000..b428e06 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_no_static_method.php @@ -0,0 +1,9 @@ +loadFromExtension('framework', array( + 'secret' => 's3cr3t', + 'validation' => array( + 'enabled' => true, + 'static_method' => false, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email.php new file mode 100644 index 0000000..64a47a2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_strict_email.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'strict_email' => true, + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_translation_domain.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_translation_domain.php new file mode 100644 index 0000000..40a81d4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/validation_translation_domain.php @@ -0,0 +1,7 @@ +loadFromExtension('framework', array( + 'validation' => array( + 'translation_domain' => 'messages', + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/web_link.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/web_link.php new file mode 100644 index 0000000..990064c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/web_link.php @@ -0,0 +1,5 @@ +loadFromExtension('framework', array( + 'web_link' => array('enabled' => true), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_arguments_and_service.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_arguments_and_service.php new file mode 100644 index 0000000..d97f970 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_arguments_and_service.php @@ -0,0 +1,31 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'my_workflow' => array( + 'marking_store' => array( + 'arguments' => array('a', 'b'), + 'service' => 'workflow_service', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => array( + 'first', + ), + 'to' => array( + 'last', + ), + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php new file mode 100644 index 0000000..2619a2d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_multiple_transitions_with_same_name.php @@ -0,0 +1,49 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'article' => array( + 'type' => 'workflow', + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'initial_place' => 'draft', + 'places' => array( + 'draft', + 'wait_for_journalist', + 'approved_by_journalist', + 'wait_for_spellchecker', + 'approved_by_spellchecker', + 'published', + ), + 'transitions' => array( + 'request_review' => array( + 'from' => 'draft', + 'to' => array('wait_for_journalist', 'wait_for_spellchecker'), + ), + 'journalist_approval' => array( + 'from' => 'wait_for_journalist', + 'to' => 'approved_by_journalist', + ), + 'spellchecker_approval' => array( + 'from' => 'wait_for_spellchecker', + 'to' => 'approved_by_spellchecker', + ), + 'publish' => array( + 'from' => array('approved_by_journalist', 'approved_by_spellchecker'), + 'to' => 'published', + ), + 'publish_editor_in_chief' => array( + 'name' => 'publish', + 'from' => 'draft', + 'to' => 'published', + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php new file mode 100644 index 0000000..062fdb9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_support_and_support_strategy.php @@ -0,0 +1,31 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'my_workflow' => array( + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'support_strategy' => 'foobar', + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => array( + 'first', + ), + 'to' => array( + 'last', + ), + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_type_and_service.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_type_and_service.php new file mode 100644 index 0000000..7d9e596 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_with_type_and_service.php @@ -0,0 +1,31 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'my_workflow' => array( + 'marking_store' => array( + 'type' => 'multiple_state', + 'service' => 'workflow_service', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => array( + 'first', + ), + 'to' => array( + 'last', + ), + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php new file mode 100644 index 0000000..0694878 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflow_without_support_and_support_strategy.php @@ -0,0 +1,27 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'my_workflow' => array( + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => array( + 'first', + ), + 'to' => array( + 'last', + ), + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows.php new file mode 100644 index 0000000..c527606 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows.php @@ -0,0 +1,111 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'article' => array( + 'type' => 'workflow', + 'marking_store' => array( + 'type' => 'multiple_state', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'initial_place' => 'draft', + 'places' => array( + 'draft', + 'wait_for_journalist', + 'approved_by_journalist', + 'wait_for_spellchecker', + 'approved_by_spellchecker', + 'published', + ), + 'transitions' => array( + 'request_review' => array( + 'from' => 'draft', + 'to' => array('wait_for_journalist', 'wait_for_spellchecker'), + ), + 'journalist_approval' => array( + 'from' => 'wait_for_journalist', + 'to' => 'approved_by_journalist', + ), + 'spellchecker_approval' => array( + 'from' => 'wait_for_spellchecker', + 'to' => 'approved_by_spellchecker', + ), + 'publish' => array( + 'from' => array('approved_by_journalist', 'approved_by_spellchecker'), + 'to' => 'published', + ), + ), + ), + 'pull_request' => array( + 'type' => 'state_machine', + 'marking_store' => array( + 'type' => 'single_state', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'initial_place' => 'start', + 'places' => array( + 'start', + 'coding', + 'travis', + 'review', + 'merged', + 'closed', + ), + 'transitions' => array( + 'submit' => array( + 'from' => 'start', + 'to' => 'travis', + ), + 'update' => array( + 'from' => array('coding', 'travis', 'review'), + 'to' => 'travis', + ), + 'wait_for_review' => array( + 'from' => 'travis', + 'to' => 'review', + ), + 'request_change' => array( + 'from' => 'review', + 'to' => 'coding', + ), + 'accept' => array( + 'from' => 'review', + 'to' => 'merged', + ), + 'reject' => array( + 'from' => 'review', + 'to' => 'closed', + ), + 'reopen' => array( + 'from' => 'closed', + 'to' => 'review', + ), + ), + ), + 'service_marking_store_workflow' => array( + 'type' => 'workflow', + 'marking_store' => array( + 'service' => 'workflow_service', + ), + 'supports' => array( + FrameworkExtensionTest::class, + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => 'first', + 'to' => 'last', + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_enabled.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_enabled.php new file mode 100644 index 0000000..9a2fe91 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_enabled.php @@ -0,0 +1,5 @@ +loadFromExtension('framework', array( + 'workflows' => null, +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php new file mode 100644 index 0000000..63e8317 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/php/workflows_without_type.php @@ -0,0 +1,26 @@ +loadFromExtension('framework', array( + 'workflows' => array( + 'missing_type' => array( + 'marking_store' => array( + 'service' => 'workflow_service', + ), + 'supports' => array( + \stdClass::class, + ), + 'places' => array( + 'first', + 'last', + ), + 'transitions' => array( + 'go' => array( + 'from' => 'first', + 'to' => 'last', + ), + ), + ), + ), +)); diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml new file mode 100644 index 0000000..d4e682c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/translations/test_paths.en.yml @@ -0,0 +1,2 @@ +custom: + paths: test diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets.xml new file mode 100644 index 0000000..a907a5b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets.xml @@ -0,0 +1,27 @@ + + + + + + + http://cdn.example.com + + + http://images1.example.com + http://images2.example.com + + + + https://bar2.example.com + + + https://bar_version_strategy.example.com + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml new file mode 100644 index 0000000..0ce70d2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_disabled.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_version_strategy_as_service.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_version_strategy_as_service.xml new file mode 100644 index 0000000..353ac48 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/assets_version_strategy_as_service.xml @@ -0,0 +1,14 @@ + + + + + + + http://cdn.example.com + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache.xml new file mode 100644 index 0000000..79ee2a4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml new file mode 100644 index 0000000..8f03cb1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/cache_env_var.xml @@ -0,0 +1,17 @@ + + + + + redis://paas.com + + + + + %env(REDIS_URL)% + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml new file mode 100644 index 0000000..586d741 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml new file mode 100644 index 0000000..635557f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_disabled.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml new file mode 100644 index 0000000..0a08d23 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/csrf_needs_session.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml new file mode 100644 index 0000000..af727ab --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/default_config.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_and_ssi_without_fragments.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_and_ssi_without_fragments.xml new file mode 100644 index 0000000..7742141 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_and_ssi_without_fragments.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_disabled.xml new file mode 100644 index 0000000..c358fed --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/esi_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml new file mode 100644 index 0000000..589ec78 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_sets_field_name.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml new file mode 100644 index 0000000..c896acb --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_csrf_under_form_sets_field_name.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml new file mode 100644 index 0000000..fdeb60a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/form_no_csrf.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/full.xml new file mode 100644 index 0000000..f083b9c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + text/csv + text/plain + + + application/pdf + + + + loader.foo + loader.bar + php + twig + + theme1 + theme2 + + + + + %kernel.project_dir%/Fixtures/translations + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock.xml new file mode 100644 index 0000000..fc2bf06 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock_named.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock_named.xml new file mode 100644 index 0000000..d36c482 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/lock_named.xml @@ -0,0 +1,22 @@ + + + + + + redis://paas.com + + + + + semaphore + flock + semaphore + flock + %env(REDIS_URL)% + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_disabled.xml new file mode 100644 index 0000000..b7da5df --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_enabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_enabled.xml new file mode 100644 index 0000000..ef13b90 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/php_errors_enabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml new file mode 100644 index 0000000..f3b3095 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/profiler.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml new file mode 100644 index 0000000..d7db78a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml new file mode 100644 index 0000000..825bc46 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/property_info.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/request.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/request.xml new file mode 100644 index 0000000..320d1b1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/request.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml new file mode 100644 index 0000000..73f1dcc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml new file mode 100644 index 0000000..e202fc1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_enabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_legacy_cache.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_legacy_cache.xml new file mode 100644 index 0000000..237e31e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_legacy_cache.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml new file mode 100644 index 0000000..1ae06c8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/serializer_mapping.xml @@ -0,0 +1,17 @@ + + + + + + + + + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/session.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/session.xml new file mode 100644 index 0000000..118a08c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/session.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/ssi_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/ssi_disabled.xml new file mode 100644 index 0000000..e7e1880 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/ssi_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_disabled.xml new file mode 100644 index 0000000..1078976 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_disabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_no_assets.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_no_assets.xml new file mode 100644 index 0000000..d579ed4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_no_assets.xml @@ -0,0 +1,14 @@ + + + + + + php + twig + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml new file mode 100644 index 0000000..72a78aa --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_disabled.xml @@ -0,0 +1,14 @@ + + + + + + + php + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml new file mode 100644 index 0000000..036afa3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/templating_php_translator_enabled.xml @@ -0,0 +1,14 @@ + + + + + + + php + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml new file mode 100644 index 0000000..a0e4f4e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/translator_fallbacks.xml @@ -0,0 +1,15 @@ + + + + + + + en + fr + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml new file mode 100644 index 0000000..22f1536 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_annotations.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml new file mode 100644 index 0000000..8d74ebb --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_mapping.xml @@ -0,0 +1,16 @@ + + + + + + + + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml + %kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml new file mode 100644 index 0000000..053f574 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_multiple_static_methods.xml @@ -0,0 +1,15 @@ + + + + + + + loadFoo + loadBar + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml new file mode 100644 index 0000000..d26c7a2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_no_static_method.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email.xml new file mode 100644 index 0000000..5b4aba1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_strict_email.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_translation_domain.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_translation_domain.xml new file mode 100644 index 0000000..733d5fa --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/validation_translation_domain.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml new file mode 100644 index 0000000..a061f0b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/web_link.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_arguments_and_service.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_arguments_and_service.xml new file mode 100644 index 0000000..0250229 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_arguments_and_service.xml @@ -0,0 +1,24 @@ + + + + + + + + a + a + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + first + last + + a + a + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml new file mode 100644 index 0000000..d52aed8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_multiple_transitions_with_same_name.xml @@ -0,0 +1,46 @@ + + + + + + + + a + a + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + draft + wait_for_journalist + approved_by_journalist + wait_for_spellchecker + approved_by_spellchecker + published + + draft + wait_for_journalist + wait_for_spellchecker + + + wait_for_journalist + approved_by_journalist + + + wait_for_spellchecker + approved_by_spellchecker + + + approved_by_journalist + approved_by_spellchecker + published + + + draft + published + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml new file mode 100644 index 0000000..92e26ff --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_support_and_support_strategy.xml @@ -0,0 +1,21 @@ + + + + + + + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + first + last + + a + a + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_type_and_service.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_type_and_service.xml new file mode 100644 index 0000000..7ec450f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_with_type_and_service.xml @@ -0,0 +1,21 @@ + + + + + + + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + first + last + + a + a + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml new file mode 100644 index 0000000..14bb287 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflow_without_support_and_support_strategy.xml @@ -0,0 +1,20 @@ + + + + + + + + first + last + + a + a + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml new file mode 100644 index 0000000..be065c4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows.xml @@ -0,0 +1,94 @@ + + + + + + + + a + a + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + draft + wait_for_journalist + approved_by_journalist + wait_for_spellchecker + approved_by_spellchecker + published + + draft + wait_for_journalist + wait_for_spellchecker + + + wait_for_journalist + approved_by_journalist + + + wait_for_spellcheker + approved_by_spellchker + + + approved_by_journalist + approved_by_spellchker + published + + + + + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + start + coding + travis + review + merged + closed + + start + travis + + + coding + travis + review + travis + + + travis + review + + + review + coding + + + review + merged + + + review + closed + + + closed + review + + + + + + Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + first + last + + first + last + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_enabled.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_enabled.xml new file mode 100644 index 0000000..51da644 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_enabled.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml new file mode 100644 index 0000000..2e6ebad --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/xml/workflows_without_type.xml @@ -0,0 +1,21 @@ + + + + + + + + stdClass + first + last + + first + last + + + + diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets.yml new file mode 100644 index 0000000..a1679e3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets.yml @@ -0,0 +1,21 @@ +framework: + assets: + version: SomeVersionScheme + version_format: '%%s?version=%%s' + base_urls: http://cdn.example.com + packages: + images_path: + base_path: '/foo' + images: + version: 1.0.0 + base_urls: ["http://images1.example.com", "http://images2.example.com"] + foo: + version: 1.0.0 + version_format: '%%s-%%s' + bar: + base_urls: ["https://bar2.example.com"] + bar_version_strategy: + base_urls: ["https://bar_version_strategy.example.com"] + version_strategy: assets.custom_version_strategy + json_manifest_strategy: + json_manifest_path: '/path/to/manifest.json' diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml new file mode 100644 index 0000000..17ba4e9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_disabled.yml @@ -0,0 +1,3 @@ +framework: + assets: + enabled: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_version_strategy_as_service.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_version_strategy_as_service.yml new file mode 100644 index 0000000..2528462 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/assets_version_strategy_as_service.yml @@ -0,0 +1,4 @@ +framework: + assets: + version_strategy: assets.custom_version_strategy + base_urls: http://cdn.example.com diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache.yml new file mode 100644 index 0000000..514e782 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache.yml @@ -0,0 +1,19 @@ +framework: + cache: + pools: + cache.foo: + adapter: cache.adapter.apcu + default_lifetime: 30 + cache.bar: + adapter: cache.adapter.doctrine + default_lifetime: 5 + provider: app.doctrine_cache_provider + cache.baz: + adapter: cache.adapter.filesystem + default_lifetime: 7 + cache.foobar: + adapter: cache.adapter.psr6 + default_lifetime: 10 + provider: app.cache_pool + cache.def: + default_lifetime: 11 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml new file mode 100644 index 0000000..1d9ce5f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/cache_env_var.yml @@ -0,0 +1,6 @@ +parameters: + env(REDIS_URL): redis://paas.com + +framework: + cache: + default_redis_provider: "%env(REDIS_URL)%" diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml new file mode 100644 index 0000000..dbdd495 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + csrf_protection: ~ + form: ~ + session: ~ diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml new file mode 100644 index 0000000..b8065b6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/csrf_needs_session.yml @@ -0,0 +1,2 @@ +framework: + csrf_protection: ~ diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml new file mode 100644 index 0000000..00874fb --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/default_config.yml @@ -0,0 +1 @@ +framework: ~ diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_and_ssi_without_fragments.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_and_ssi_without_fragments.yml new file mode 100644 index 0000000..49d63c8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_and_ssi_without_fragments.yml @@ -0,0 +1,7 @@ +framework: + fragments: + enabled: false + esi: + enabled: true + ssi: + enabled: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_disabled.yml new file mode 100644 index 0000000..2a78e6d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/esi_disabled.yml @@ -0,0 +1,3 @@ +framework: + esi: + enabled: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml new file mode 100644 index 0000000..e3ac7e8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/form_no_csrf.yml @@ -0,0 +1,4 @@ +framework: + form: + csrf_protection: + enabled: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/full.yml new file mode 100644 index 0000000..f54c3e6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -0,0 +1,63 @@ +framework: + secret: s3cr3t + default_locale: fr + csrf_protection: true + form: + csrf_protection: + field_name: _csrf + http_method_override: false + esi: + enabled: true + ssi: + enabled: true + profiler: + only_exceptions: true + enabled: false + router: + resource: '%kernel.project_dir%/config/routing.xml' + type: xml + session: + storage_id: session.storage.native + handler_id: session.handler.native_file + name: _SYMFONY + cookie_lifetime: 86400 + cookie_path: / + cookie_domain: example.com + cookie_secure: true + cookie_httponly: false + use_cookies: true + gc_probability: 1 + gc_divisor: 108 + gc_maxlifetime: 90000 + save_path: /path/to/sessions + templating: + engines: [php, twig] + loader: [loader.foo, loader.bar] + cache: /path/to/cache + form: + resources: [theme1, theme2] + hinclude_default_template: global_hinclude_template + assets: + version: v1 + translator: + enabled: true + fallback: fr + default_path: '%kernel.project_dir%/translations' + paths: ['%kernel.project_dir%/Fixtures/translations'] + validation: + enabled: true + annotations: + cache: file + debug: true + file_cache_dir: '%kernel.cache_dir%/annotations' + serializer: + enabled: true + enable_annotations: true + name_converter: serializer.name_converter.camel_case_to_snake_case + circular_reference_handler: my.circular.reference.handler + property_info: ~ + ide: file%%link%%format + request: + formats: + csv: ['text/csv', 'text/plain'] + pdf: 'application/pdf' diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock.yml new file mode 100644 index 0000000..70f578a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock.yml @@ -0,0 +1,2 @@ +framework: + lock: ~ diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock_named.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock_named.yml new file mode 100644 index 0000000..6d0cb5c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/lock_named.yml @@ -0,0 +1,9 @@ +parameters: + env(REDIS_DSN): redis://paas.com + +framework: + lock: + foo: semaphore + bar: flock + baz: [semaphore, flock] + qux: "%env(REDIS_DSN)%" diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_disabled.yml new file mode 100644 index 0000000..958f756 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_disabled.yml @@ -0,0 +1,3 @@ +framework: + php_errors: + throw: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_enabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_enabled.yml new file mode 100644 index 0000000..f485310 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/php_errors_enabled.yml @@ -0,0 +1,4 @@ +framework: + php_errors: + log: true + throw: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml new file mode 100644 index 0000000..9052a2b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/profiler.yml @@ -0,0 +1,3 @@ +framework: + profiler: + enabled: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml new file mode 100644 index 0000000..b5fd271 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml @@ -0,0 +1,4 @@ +framework: + property_access: + magic_call: true + throw_exception_on_invalid_index: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml new file mode 100644 index 0000000..fbdf7a7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/property_info.yml @@ -0,0 +1,3 @@ +framework: + property_info: + enabled: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/request.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/request.yml new file mode 100644 index 0000000..9beae1d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/request.yml @@ -0,0 +1,3 @@ +framework: + request: + formats: ~ diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml new file mode 100644 index 0000000..330e19a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_disabled.yml @@ -0,0 +1,3 @@ +framework: + serializer: + enabled: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml new file mode 100644 index 0000000..40a1ff7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_enabled.yml @@ -0,0 +1,3 @@ +framework: + serializer: + enabled: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_legacy_cache.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_legacy_cache.yml new file mode 100644 index 0000000..5fadc88 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_legacy_cache.yml @@ -0,0 +1,4 @@ +framework: + serializer: + enabled: true + cache: foo diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml new file mode 100644 index 0000000..77c9d51 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/serializer_mapping.yml @@ -0,0 +1,10 @@ +framework: + annotations: + enabled: true + serializer: + enable_annotations: true + mapping: + paths: + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/files" + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yml" + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/serializer_mapping/serialization.yaml" diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/session.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/session.yml new file mode 100644 index 0000000..d91b0c3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/session.yml @@ -0,0 +1,3 @@ +framework: + session: + handler_id: null diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/ssi_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/ssi_disabled.yml new file mode 100644 index 0000000..3a8a820 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/ssi_disabled.yml @@ -0,0 +1,3 @@ +framework: + ssi: + enabled: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_disabled.yml new file mode 100644 index 0000000..1e548b8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_disabled.yml @@ -0,0 +1,2 @@ +framework: + templating: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_no_assets.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_no_assets.yml new file mode 100644 index 0000000..393477a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_no_assets.yml @@ -0,0 +1,3 @@ +framework: + templating: + engines: [php, twig] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_assets_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_assets_disabled.yml new file mode 100644 index 0000000..7ef6b3e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_assets_disabled.yml @@ -0,0 +1,4 @@ +framework: + assets: false + templating: + engines: [php] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml new file mode 100644 index 0000000..fe0f3e8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_disabled.yml @@ -0,0 +1,4 @@ +framework: + translator: false + templating: + engines: [php] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml new file mode 100644 index 0000000..0991a20 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/templating_php_translator_enabled.yml @@ -0,0 +1,4 @@ +framework: + translator: true + templating: + engines: [php] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml new file mode 100644 index 0000000..271d781 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/translator_fallbacks.yml @@ -0,0 +1,3 @@ +framework: + translator: + fallbacks: [en, fr] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml new file mode 100644 index 0000000..41f1796 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_annotations.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + validation: + enabled: true + enable_annotations: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml new file mode 100644 index 0000000..f05e33b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_mapping.yml @@ -0,0 +1,7 @@ +framework: + validation: + mapping: + paths: + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/files" + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yml" + - "%kernel.project_dir%/Fixtures/TestBundle/Resources/config/validation_mapping/validation.yaml" diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml new file mode 100644 index 0000000..6ca3433 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_multiple_static_methods.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + validation: + enabled: true + static_method: [loadFoo, loadBar] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml new file mode 100644 index 0000000..ca52149 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_no_static_method.yml @@ -0,0 +1,5 @@ +framework: + secret: s3cr3t + validation: + enabled: true + static_method: false diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email.yml new file mode 100644 index 0000000..1c805f9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_strict_email.yml @@ -0,0 +1,3 @@ +framework: + validation: + strict_email: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_translation_domain.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_translation_domain.yml new file mode 100644 index 0000000..167b5fc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/validation_translation_domain.yml @@ -0,0 +1,3 @@ +framework: + validation: + translation_domain: messages diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml new file mode 100644 index 0000000..4276aac --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/web_link.yml @@ -0,0 +1,3 @@ +framework: + web_link: + enabled: true diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_arguments_and_service.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_arguments_and_service.yml new file mode 100644 index 0000000..a46d4b6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_arguments_and_service.yml @@ -0,0 +1,19 @@ +framework: + workflows: + my_workflow: + marking_store: + arguments: + - a + - b + service: workflow_service + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml new file mode 100644 index 0000000..36d00de --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_multiple_transitions_with_same_name.yml @@ -0,0 +1,33 @@ +framework: + workflows: + article: + type: workflow + marking_store: + type: multiple_state + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + initial_place: draft + places: + - draft + - wait_for_journalist + - approved_by_journalist + - wait_for_spellchecker + - approved_by_spellchecker + - published + transitions: + request_review: + from: [draft] + to: [wait_for_journalist, wait_for_spellchecker] + journalist_approval: + from: [wait_for_journalist] + to: [approved_by_journalist] + spellchecker_approval: + from: [wait_for_spellchecker] + to: [approved_by_spellchecker] + publish: + from: [approved_by_journalist, approved_by_spellchecker] + to: [published] + publish_editor_in_chief: + name: publish + from: [draft] + to: [published] diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml new file mode 100644 index 0000000..7437084 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_support_and_support_strategy.yml @@ -0,0 +1,17 @@ +framework: + workflows: + my_workflow: + marking_store: + type: multiple_state + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + support_strategy: foobar + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_type_and_service.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_type_and_service.yml new file mode 100644 index 0000000..000ba10 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_with_type_and_service.yml @@ -0,0 +1,17 @@ +framework: + workflows: + my_workflow: + marking_store: + type: multiple_state + service: workflow_service + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml new file mode 100644 index 0000000..6dc848d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflow_without_support_and_support_strategy.yml @@ -0,0 +1,14 @@ +framework: + workflows: + my_workflow: + marking_store: + type: multiple_state + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml new file mode 100644 index 0000000..36b84f7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows.yml @@ -0,0 +1,80 @@ +framework: + workflows: + article: + type: workflow + marking_store: + type: multiple_state + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + initial_place: draft + places: + - draft + - wait_for_journalist + - approved_by_journalist + - wait_for_spellchecker + - approved_by_spellchecker + - published + transitions: + request_review: + from: [draft] + to: [wait_for_journalist, wait_for_spellchecker] + journalist_approval: + from: [wait_for_journalist] + to: [approved_by_journalist] + spellchecker_approval: + from: [wait_for_spellchecker] + to: [approved_by_spellchecker] + publish: + from: [approved_by_journalist, approved_by_spellchecker] + to: [published] + pull_request: + type: state_machine + marking_store: + type: single_state + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + initial_place: start + places: + - start + - coding + - travis + - review + - merged + - closed + transitions: + submit: + from: start + to: travis + update: + from: [coding, travis, review] + to: travis + wait_for_review: + from: travis + to: review + request_change: + from: review + to: coding + accept: + from: review + to: merged + reject: + from: review + to: closed + reopen: + from: closed + to: review + service_marking_store_workflow: + type: workflow + marking_store: + service: workflow_service + supports: + - Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\FrameworkExtensionTest + places: + - first + - last + transitions: + go: + from: + - first + to: + - last diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_enabled.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_enabled.yml new file mode 100644 index 0000000..2a716ff --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_enabled.yml @@ -0,0 +1,2 @@ +framework: + workflows: ~ diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml new file mode 100644 index 0000000..41b8168 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/Fixtures/yml/workflows_without_type.yml @@ -0,0 +1,7 @@ +framework: + workflows: + missing_type: + supports: stdClass + places: [ first, second ] + transitions: + go: {from: first, to: last } diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/FrameworkExtensionTest.php new file mode 100644 index 0000000..06e709c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -0,0 +1,1198 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Doctrine\Common\Annotations\Annotation; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddAnnotationsCachedReaderPass; +use Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FullStack; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ApcuAdapter; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\ChainAdapter; +use Symfony\Component\Cache\Adapter\DoctrineAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\ProxyAdapter; +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; +use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; +use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; +use Symfony\Component\Serializer\Normalizer\DateIntervalNormalizer; +use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; +use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Translation\DependencyInjection\TranslatorPass; +use Symfony\Component\Validator\DependencyInjection\AddConstraintValidatorsPass; +use Symfony\Component\Workflow\Registry; + +abstract class FrameworkExtensionTest extends TestCase +{ + private static $containerCache = array(); + + abstract protected function loadFromFile(ContainerBuilder $container, $file); + + public function testFormCsrfProtection() + { + $container = $this->createContainerFromFile('full'); + + $def = $container->getDefinition('form.type_extension.csrf'); + + $this->assertTrue($container->getParameter('form.type_extension.csrf.enabled')); + $this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(1)); + $this->assertEquals('_csrf', $container->getParameter('form.type_extension.csrf.field_name')); + $this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(2)); + } + + public function testPropertyAccessWithDefaultValue() + { + $container = $this->createContainerFromFile('full'); + + $def = $container->getDefinition('property_accessor'); + $this->assertFalse($def->getArgument(0)); + $this->assertFalse($def->getArgument(1)); + } + + public function testPropertyAccessWithOverriddenValues() + { + $container = $this->createContainerFromFile('property_accessor'); + $def = $container->getDefinition('property_accessor'); + $this->assertTrue($def->getArgument(0)); + $this->assertTrue($def->getArgument(1)); + } + + public function testPropertyAccessCache() + { + $container = $this->createContainerFromFile('property_accessor'); + + if (!method_exists(PropertyAccessor::class, 'createCache')) { + return $this->assertFalse($container->hasDefinition('cache.property_access')); + } + + $cache = $container->getDefinition('cache.property_access'); + $this->assertSame(array(PropertyAccessor::class, 'createCache'), $cache->getFactory(), 'PropertyAccessor::createCache() should be used in non-debug mode'); + $this->assertSame(AdapterInterface::class, $cache->getClass()); + } + + public function testPropertyAccessCacheWithDebug() + { + $container = $this->createContainerFromFile('property_accessor', array('kernel.debug' => true)); + + if (!method_exists(PropertyAccessor::class, 'createCache')) { + return $this->assertFalse($container->hasDefinition('cache.property_access')); + } + + $cache = $container->getDefinition('cache.property_access'); + $this->assertNull($cache->getFactory()); + $this->assertSame(ArrayAdapter::class, $cache->getClass(), 'ArrayAdapter should be used in debug mode'); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage CSRF protection needs sessions to be enabled. + */ + public function testCsrfProtectionNeedsSessionToBeEnabled() + { + $this->createContainerFromFile('csrf_needs_session'); + } + + public function testCsrfProtectionForFormsEnablesCsrfProtectionAutomatically() + { + $container = $this->createContainerFromFile('csrf'); + + $this->assertTrue($container->hasDefinition('security.csrf.token_manager')); + } + + public function testHttpMethodOverride() + { + $container = $this->createContainerFromFile('full'); + + $this->assertFalse($container->getParameter('kernel.http_method_override')); + } + + public function testEsi() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('esi'), '->registerEsiConfiguration() loads esi.xml'); + $this->assertTrue($container->hasDefinition('fragment.renderer.esi'), 'The ESI fragment renderer is registered'); + } + + public function testEsiDisabled() + { + $container = $this->createContainerFromFile('esi_disabled'); + + $this->assertFalse($container->hasDefinition('fragment.renderer.esi'), 'The ESI fragment renderer is not registered'); + $this->assertFalse($container->hasDefinition('esi')); + } + + public function testSsi() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('ssi'), '->registerSsiConfiguration() loads ssi.xml'); + $this->assertTrue($container->hasDefinition('fragment.renderer.ssi'), 'The SSI fragment renderer is registered'); + } + + public function testSsiDisabled() + { + $container = $this->createContainerFromFile('ssi_disabled'); + + $this->assertFalse($container->hasDefinition('fragment.renderer.ssi'), 'The SSI fragment renderer is not registered'); + $this->assertFalse($container->hasDefinition('ssi')); + } + + public function testEsiAndSsiWithoutFragments() + { + $container = $this->createContainerFromFile('esi_and_ssi_without_fragments'); + + $this->assertFalse($container->hasDefinition('fragment.renderer.hinclude'), 'The HInclude fragment renderer is not registered'); + $this->assertTrue($container->hasDefinition('fragment.renderer.esi'), 'The ESI fragment renderer is registered'); + $this->assertTrue($container->hasDefinition('fragment.renderer.ssi'), 'The SSI fragment renderer is registered'); + } + + public function testEnabledProfiler() + { + $container = $this->createContainerFromFile('profiler'); + + $this->assertTrue($container->hasDefinition('profiler'), '->registerProfilerConfiguration() loads profiling.xml'); + $this->assertTrue($container->hasDefinition('data_collector.config'), '->registerProfilerConfiguration() loads collectors.xml'); + } + + public function testDisabledProfiler() + { + $container = $this->createContainerFromFile('full'); + + $this->assertFalse($container->hasDefinition('profiler'), '->registerProfilerConfiguration() does not load profiling.xml'); + $this->assertFalse($container->hasDefinition('data_collector.config'), '->registerProfilerConfiguration() does not load collectors.xml'); + } + + public function testWorkflows() + { + $container = $this->createContainerFromFile('workflows'); + + $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); + $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); + + $workflowDefinition = $container->getDefinition('workflow.article.definition'); + + $this->assertSame( + array( + 'draft', + 'wait_for_journalist', + 'approved_by_journalist', + 'wait_for_spellchecker', + 'approved_by_spellchecker', + 'published', + ), + $workflowDefinition->getArgument(0), + 'Places are passed to the workflow definition' + ); + $this->assertSame(array('workflow.definition' => array(array('name' => 'article', 'type' => 'workflow', 'marking_store' => 'multiple_state'))), $workflowDefinition->getTags()); + + $this->assertTrue($container->hasDefinition('state_machine.pull_request'), 'State machine is registered as a service'); + $this->assertTrue($container->hasDefinition('state_machine.pull_request.definition'), 'State machine definition is registered as a service'); + $this->assertCount(4, $workflowDefinition->getArgument(1)); + $this->assertSame('draft', $workflowDefinition->getArgument(2)); + + $stateMachineDefinition = $container->getDefinition('state_machine.pull_request.definition'); + + $this->assertSame( + array( + 'start', + 'coding', + 'travis', + 'review', + 'merged', + 'closed', + ), + $stateMachineDefinition->getArgument(0), + 'Places are passed to the state machine definition' + ); + $this->assertSame(array('workflow.definition' => array(array('name' => 'pull_request', 'type' => 'state_machine', 'marking_store' => 'single_state'))), $stateMachineDefinition->getTags()); + $this->assertCount(9, $stateMachineDefinition->getArgument(1)); + $this->assertSame('start', $stateMachineDefinition->getArgument(2)); + + $serviceMarkingStoreWorkflowDefinition = $container->getDefinition('workflow.service_marking_store_workflow'); + /** @var Reference $markingStoreRef */ + $markingStoreRef = $serviceMarkingStoreWorkflowDefinition->getArgument(1); + $this->assertInstanceOf(Reference::class, $markingStoreRef); + $this->assertEquals('workflow_service', (string) $markingStoreRef); + + $this->assertTrue($container->hasDefinition('workflow.registry', 'Workflow registry is registered as a service')); + $registryDefinition = $container->getDefinition('workflow.registry'); + $this->assertGreaterThan(0, \count($registryDefinition->getMethodCalls())); + } + + /** + * @group legacy + * @expectedDeprecation The "type" option of the "framework.workflows.missing_type" configuration entry must be defined since Symfony 3.3. The default value will be "state_machine" in Symfony 4.0. + */ + public function testDeprecatedWorkflowMissingType() + { + $container = $this->createContainerFromFile('workflows_without_type'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "type" and "service" cannot be used together. + */ + public function testWorkflowCannotHaveBothTypeAndService() + { + $this->createContainerFromFile('workflow_with_type_and_service'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "supports" and "support_strategy" cannot be used together. + */ + public function testWorkflowCannotHaveBothSupportsAndSupportStrategy() + { + $this->createContainerFromFile('workflow_with_support_and_support_strategy'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "supports" or "support_strategy" should be configured. + */ + public function testWorkflowShouldHaveOneOfSupportsAndSupportStrategy() + { + $this->createContainerFromFile('workflow_without_support_and_support_strategy'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + * @expectedExceptionMessage "arguments" and "service" cannot be used together. + */ + public function testWorkflowCannotHaveBothArgumentsAndService() + { + $this->createContainerFromFile('workflow_with_arguments_and_service'); + } + + public function testWorkflowMultipleTransitionsWithSameName() + { + $container = $this->createContainerFromFile('workflow_with_multiple_transitions_with_same_name'); + + $this->assertTrue($container->hasDefinition('workflow.article'), 'Workflow is registered as a service'); + $this->assertTrue($container->hasDefinition('workflow.article.definition'), 'Workflow definition is registered as a service'); + + $workflowDefinition = $container->getDefinition('workflow.article.definition'); + + $transitions = $workflowDefinition->getArgument(1); + + $this->assertCount(5, $transitions); + + $this->assertSame('request_review', $transitions[0]->getArgument(0)); + $this->assertSame('journalist_approval', $transitions[1]->getArgument(0)); + $this->assertSame('spellchecker_approval', $transitions[2]->getArgument(0)); + $this->assertSame('publish', $transitions[3]->getArgument(0)); + $this->assertSame('publish', $transitions[4]->getArgument(0)); + + $this->assertSame(array('approved_by_journalist', 'approved_by_spellchecker'), $transitions[3]->getArgument(1)); + $this->assertSame(array('draft'), $transitions[4]->getArgument(1)); + } + + public function testWorkflowServicesCanBeEnabled() + { + $container = $this->createContainerFromFile('workflows_enabled'); + + $this->assertTrue($container->has(Registry::class)); + $this->assertTrue($container->hasDefinition('console.command.workflow_dump')); + } + + public function testEnabledPhpErrorsConfig() + { + $container = $this->createContainerFromFile('php_errors_enabled'); + + $this->assertEquals(new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE), $container->getDefinition('debug.debug_handlers_listener')->getArgument(1)); + $this->assertSame(-1, $container->getParameter('debug.error_handler.throw_at')); + } + + public function testDisabledPhpErrorsConfig() + { + $container = $this->createContainerFromFile('php_errors_disabled'); + + $this->assertNull($container->getDefinition('debug.debug_handlers_listener')->getArgument(1)); + $this->assertSame(0, $container->getParameter('debug.error_handler.throw_at')); + } + + public function testRouter() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->has('router'), '->registerRouterConfiguration() loads routing.xml'); + $arguments = $container->findDefinition('router')->getArguments(); + $this->assertEquals($container->getParameter('kernel.project_dir').'/config/routing.xml', $container->getParameter('router.resource'), '->registerRouterConfiguration() sets routing resource'); + $this->assertEquals('%router.resource%', $arguments[1], '->registerRouterConfiguration() sets routing resource'); + $this->assertEquals('xml', $arguments[2]['resource_type'], '->registerRouterConfiguration() sets routing resource type'); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testRouterRequiresResourceOption() + { + $container = $this->createContainer(); + $loader = new FrameworkExtension(); + $loader->load(array(array('router' => true)), $container); + } + + public function testSession() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml'); + $this->assertEquals('fr', $container->getParameter('kernel.default_locale')); + $this->assertEquals('session.storage.native', (string) $container->getAlias('session.storage')); + $this->assertEquals('session.handler.native_file', (string) $container->getAlias('session.handler')); + + $options = $container->getParameter('session.storage.options'); + $this->assertEquals('_SYMFONY', $options['name']); + $this->assertEquals(86400, $options['cookie_lifetime']); + $this->assertEquals('/', $options['cookie_path']); + $this->assertEquals('example.com', $options['cookie_domain']); + $this->assertTrue($options['cookie_secure']); + $this->assertFalse($options['cookie_httponly']); + $this->assertTrue($options['use_cookies']); + $this->assertEquals(108, $options['gc_divisor']); + $this->assertEquals(1, $options['gc_probability']); + $this->assertEquals(90000, $options['gc_maxlifetime']); + + $this->assertEquals('/path/to/sessions', $container->getParameter('session.save_path')); + } + + public function testNullSessionHandler() + { + $container = $this->createContainerFromFile('session'); + + $this->assertTrue($container->hasDefinition('session'), '->registerSessionConfiguration() loads session.xml'); + $this->assertNull($container->getDefinition('session.storage.native')->getArgument(1)); + $this->assertNull($container->getDefinition('session.storage.php_bridge')->getArgument(0)); + } + + public function testRequest() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('request.add_request_formats_listener'), '->registerRequestConfiguration() loads request.xml'); + $listenerDef = $container->getDefinition('request.add_request_formats_listener'); + $this->assertEquals(array('csv' => array('text/csv', 'text/plain'), 'pdf' => array('application/pdf')), $listenerDef->getArgument(0)); + } + + public function testEmptyRequestFormats() + { + $container = $this->createContainerFromFile('request'); + + $this->assertFalse($container->hasDefinition('request.add_request_formats_listener'), '->registerRequestConfiguration() does not load request.xml when no request formats are defined'); + } + + public function testTemplating() + { + $container = $this->createContainerFromFile('full'); + + $this->assertTrue($container->hasDefinition('templating.name_parser'), '->registerTemplatingConfiguration() loads templating.xml'); + + $this->assertEquals('templating.engine.delegating', (string) $container->getAlias('templating'), '->registerTemplatingConfiguration() configures delegating loader if multiple engines are provided'); + + $this->assertEquals($container->getDefinition('templating.loader.chain'), $container->getDefinition('templating.loader.wrapped'), '->registerTemplatingConfiguration() configures loader chain if multiple loaders are provided'); + + $this->assertEquals($container->getDefinition('templating.loader'), $container->getDefinition('templating.loader.cache'), '->registerTemplatingConfiguration() configures the loader to use cache'); + + $this->assertEquals('%templating.loader.cache.path%', $container->getDefinition('templating.loader.cache')->getArgument(1)); + $this->assertEquals('/path/to/cache', $container->getParameter('templating.loader.cache.path')); + + $this->assertEquals(array('php', 'twig'), $container->getParameter('templating.engines'), '->registerTemplatingConfiguration() sets a templating.engines parameter'); + + $this->assertEquals(array('FrameworkBundle:Form', 'theme1', 'theme2'), $container->getParameter('templating.helper.form.resources'), '->registerTemplatingConfiguration() registers the theme and adds the base theme'); + $this->assertEquals('global_hinclude_template', $container->getParameter('fragment.renderer.hinclude.global_template'), '->registerTemplatingConfiguration() registers the global hinclude.js template'); + } + + public function testTemplatingCanBeDisabled() + { + $container = $this->createContainerFromFile('templating_disabled'); + + $this->assertFalse($container->hasParameter('templating.engines'), '"templating.engines" container parameter is not registered when templating is disabled.'); + } + + public function testAssets() + { + $container = $this->createContainerFromFile('assets'); + $packages = $container->getDefinition('assets.packages'); + + // default package + $defaultPackage = $container->getDefinition((string) $packages->getArgument(0)); + $this->assertUrlPackage($container, $defaultPackage, array('http://cdn.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); + + // packages + $packages = $packages->getArgument(1); + $this->assertCount(6, $packages); + + $package = $container->getDefinition((string) $packages['images_path']); + $this->assertPathPackage($container, $package, '/foo', 'SomeVersionScheme', '%%s?version=%%s'); + + $package = $container->getDefinition((string) $packages['images']); + $this->assertUrlPackage($container, $package, array('http://images1.example.com', 'http://images2.example.com'), '1.0.0', '%%s?version=%%s'); + + $package = $container->getDefinition((string) $packages['foo']); + $this->assertPathPackage($container, $package, '', '1.0.0', '%%s-%%s'); + + $package = $container->getDefinition((string) $packages['bar']); + $this->assertUrlPackage($container, $package, array('https://bar2.example.com'), 'SomeVersionScheme', '%%s?version=%%s'); + + $package = $container->getDefinition((string) $packages['bar_version_strategy']); + $this->assertEquals('assets.custom_version_strategy', (string) $package->getArgument(1)); + + $package = $container->getDefinition((string) $packages['json_manifest_strategy']); + $versionStrategy = $container->getDefinition((string) $package->getArgument(1)); + $this->assertEquals('assets.json_manifest_version_strategy', $versionStrategy->getParent()); + $this->assertEquals('/path/to/manifest.json', $versionStrategy->getArgument(0)); + } + + public function testAssetsDefaultVersionStrategyAsService() + { + $container = $this->createContainerFromFile('assets_version_strategy_as_service'); + $packages = $container->getDefinition('assets.packages'); + + // default package + $defaultPackage = $container->getDefinition((string) $packages->getArgument(0)); + $this->assertEquals('assets.custom_version_strategy', (string) $defaultPackage->getArgument(1)); + } + + public function testAssetsCanBeDisabled() + { + $container = $this->createContainerFromFile('assets_disabled'); + + $this->assertFalse($container->has('templating.helper.assets'), 'The templating.helper.assets helper service is removed when assets are disabled.'); + } + + public function testWebLink() + { + $container = $this->createContainerFromFile('web_link'); + $this->assertTrue($container->hasDefinition('web_link.add_link_header_listener')); + } + + public function testTranslator() + { + $container = $this->createContainerFromFile('full'); + $this->assertTrue($container->hasDefinition('translator.default'), '->registerTranslatorConfiguration() loads translation.xml'); + $this->assertEquals('translator.default', (string) $container->getAlias('translator'), '->registerTranslatorConfiguration() redefines translator service from identity to real translator'); + $options = $container->getDefinition('translator.default')->getArgument(4); + + $files = array_map('realpath', $options['resource_files']['en']); + $ref = new \ReflectionClass('Symfony\Component\Validator\Validation'); + $this->assertContains( + strtr(\dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', \DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Validator translation resources' + ); + $ref = new \ReflectionClass('Symfony\Component\Form\Form'); + $this->assertContains( + strtr(\dirname($ref->getFileName()).'/Resources/translations/validators.en.xlf', '/', \DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Form translation resources' + ); + $ref = new \ReflectionClass('Symfony\Component\Security\Core\Security'); + $this->assertContains( + strtr(\dirname($ref->getFileName()).'/Resources/translations/security.en.xlf', '/', \DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds Security translation resources' + ); + $this->assertContains( + strtr(__DIR__.'/Fixtures/translations/test_paths.en.yml', '/', \DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds translation resources in custom paths' + ); + $this->assertContains( + strtr(__DIR__.'/translations/test_default.en.xlf', '/', \DIRECTORY_SEPARATOR), + $files, + '->registerTranslatorConfiguration() finds translation resources in default path' + ); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + $this->assertEquals(array('fr'), $calls[1][1][0]); + } + + public function testTranslatorMultipleFallbacks() + { + $container = $this->createContainerFromFile('translator_fallbacks'); + + $calls = $container->getDefinition('translator.default')->getMethodCalls(); + $this->assertEquals(array('en', 'fr'), $calls[1][1][0]); + } + + public function testTranslatorHelperIsRegisteredWhenTranslatorIsEnabled() + { + $container = $this->createContainerFromFile('templating_php_translator_enabled'); + + $this->assertTrue($container->has('templating.helper.translator')); + } + + public function testTranslatorHelperIsNotRegisteredWhenTranslatorIsDisabled() + { + $container = $this->createContainerFromFile('templating_php_translator_disabled'); + + $this->assertFalse($container->has('templating.helper.translator')); + } + + /** + * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException + */ + public function testTemplatingRequiresAtLeastOneEngine() + { + $container = $this->createContainer(); + $loader = new FrameworkExtension(); + $loader->load(array(array('templating' => null)), $container); + } + + public function testValidation() + { + $container = $this->createContainerFromFile('full'); + $projectDir = $container->getParameter('kernel.project_dir'); + + $ref = new \ReflectionClass('Symfony\Component\Form\Form'); + $xmlMappings = array( + \dirname($ref->getFileName()).'/Resources/config/validation.xml', + strtr($projectDir.'/config/validator/foo.xml', '/', \DIRECTORY_SEPARATOR), + ); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $annotations = !class_exists(FullStack::class) && class_exists(Annotation::class); + + $this->assertCount($annotations ? 7 : 6, $calls); + $this->assertSame('setConstraintValidatorFactory', $calls[0][0]); + $this->assertEquals(array(new Reference('validator.validator_factory')), $calls[0][1]); + $this->assertSame('setTranslator', $calls[1][0]); + $this->assertEquals(array(new Reference('translator')), $calls[1][1]); + $this->assertSame('setTranslationDomain', $calls[2][0]); + $this->assertSame(array('%validator.translation_domain%'), $calls[2][1]); + $this->assertSame('addXmlMappings', $calls[3][0]); + $this->assertSame(array($xmlMappings), $calls[3][1]); + $i = 3; + if ($annotations) { + $this->assertSame('enableAnnotationMapping', $calls[++$i][0]); + } + $this->assertSame('addMethodMapping', $calls[++$i][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[$i][1]); + $this->assertSame('setMetadataCache', $calls[++$i][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[$i][1]); + } + + public function testValidationService() + { + $container = $this->createContainerFromFile('validation_annotations', array('kernel.charset' => 'UTF-8'), false); + + $this->assertInstanceOf('Symfony\Component\Validator\Validator\ValidatorInterface', $container->get('validator')); + } + + public function testAnnotations() + { + $container = $this->createContainerFromFile('full', array(), true, false); + $container->addCompilerPass(new TestAnnotationsPass()); + $container->compile(); + + $this->assertEquals($container->getParameter('kernel.cache_dir').'/annotations', $container->getDefinition('annotations.filesystem_cache')->getArgument(0)); + $this->assertSame('annotations.filesystem_cache', (string) $container->getDefinition('annotation_reader')->getArgument(1)); + } + + public function testFileLinkFormat() + { + if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) { + $this->markTestSkipped('A custom file_link_format is defined.'); + } + + $container = $this->createContainerFromFile('full'); + + $this->assertEquals('file%link%format', $container->getParameter('debug.file_link_format')); + } + + public function testValidationAnnotations() + { + $container = $this->createContainerFromFile('validation_annotations'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertCount(7, $calls); + $this->assertSame('enableAnnotationMapping', $calls[4][0]); + $this->assertEquals(array(new Reference('annotation_reader')), $calls[4][1]); + $this->assertSame('addMethodMapping', $calls[5][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[5][1]); + $this->assertSame('setMetadataCache', $calls[6][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[6][1]); + // no cache this time + } + + public function testValidationPaths() + { + require_once __DIR__.'/Fixtures/TestBundle/TestBundle.php'; + + $container = $this->createContainerFromFile('validation_annotations', array( + 'kernel.bundles' => array('TestBundle' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\TestBundle'), + 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'parent' => null, 'path' => __DIR__.'/Fixtures/TestBundle')), + )); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertCount(8, $calls); + $this->assertSame('addXmlMappings', $calls[3][0]); + $this->assertSame('addYamlMappings', $calls[4][0]); + $this->assertSame('enableAnnotationMapping', $calls[5][0]); + $this->assertSame('addMethodMapping', $calls[6][0]); + $this->assertSame(array('loadValidatorMetadata'), $calls[6][1]); + $this->assertSame('setMetadataCache', $calls[7][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[7][1]); + + $xmlMappings = $calls[3][1][0]; + $this->assertCount(3, $xmlMappings); + try { + // Testing symfony/symfony + $this->assertStringEndsWith('Component'.\DIRECTORY_SEPARATOR.'Form/Resources/config/validation.xml', $xmlMappings[0]); + } catch (\Exception $e) { + // Testing symfony/framework-bundle with deps=high + $this->assertStringEndsWith('symfony'.\DIRECTORY_SEPARATOR.'form/Resources/config/validation.xml', $xmlMappings[0]); + } + $this->assertStringEndsWith('TestBundle/Resources/config/validation.xml', $xmlMappings[1]); + + $yamlMappings = $calls[4][1][0]; + $this->assertCount(1, $yamlMappings); + $this->assertStringEndsWith('TestBundle/Resources/config/validation.yml', $yamlMappings[0]); + } + + public function testValidationPathsUsingCustomBundlePath() + { + require_once __DIR__.'/Fixtures/CustomPathBundle/src/CustomPathBundle.php'; + + $container = $this->createContainerFromFile('validation_annotations', array( + 'kernel.bundles' => array('CustomPathBundle' => 'Symfony\\Bundle\\FrameworkBundle\\Tests\\CustomPathBundle'), + 'kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'parent' => null, 'path' => __DIR__.'/Fixtures/CustomPathBundle')), + )); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + $xmlMappings = $calls[3][1][0]; + $this->assertCount(3, $xmlMappings); + + try { + // Testing symfony/symfony + $this->assertStringEndsWith('Component'.\DIRECTORY_SEPARATOR.'Form/Resources/config/validation.xml', $xmlMappings[0]); + } catch (\Exception $e) { + // Testing symfony/framework-bundle with deps=high + $this->assertStringEndsWith('symfony'.\DIRECTORY_SEPARATOR.'form/Resources/config/validation.xml', $xmlMappings[0]); + } + $this->assertStringEndsWith('CustomPathBundle/Resources/config/validation.xml', $xmlMappings[1]); + + $yamlMappings = $calls[4][1][0]; + $this->assertCount(1, $yamlMappings); + $this->assertStringEndsWith('CustomPathBundle/Resources/config/validation.yml', $yamlMappings[0]); + } + + public function testValidationNoStaticMethod() + { + $container = $this->createContainerFromFile('validation_no_static_method'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $annotations = !class_exists(FullStack::class) && class_exists(Annotation::class); + + $this->assertCount($annotations ? 6 : 5, $calls); + $this->assertSame('addXmlMappings', $calls[3][0]); + $i = 3; + if ($annotations) { + $this->assertSame('enableAnnotationMapping', $calls[++$i][0]); + } + $this->assertSame('setMetadataCache', $calls[++$i][0]); + $this->assertEquals(array(new Reference('validator.mapping.cache.symfony')), $calls[$i][1]); + // no cache, no annotations, no static methods + } + + public function testValidationTranslationDomain() + { + $container = $this->createContainerFromFile('validation_translation_domain'); + + $this->assertSame('messages', $container->getParameter('validator.translation_domain')); + } + + public function testValidationStrictEmail() + { + $container = $this->createContainerFromFile('validation_strict_email'); + + $this->assertTrue($container->getDefinition('validator.email')->getArgument(0)); + } + + public function testValidationMapping() + { + $container = $this->createContainerFromFile('validation_mapping'); + + $calls = $container->getDefinition('validator.builder')->getMethodCalls(); + + $this->assertSame('addXmlMappings', $calls[3][0]); + $this->assertCount(3, $calls[3][1][0]); + + $this->assertSame('addYamlMappings', $calls[4][0]); + $this->assertCount(3, $calls[4][1][0]); + $this->assertContains('foo.yml', $calls[4][1][0][0]); + $this->assertContains('validation.yml', $calls[4][1][0][1]); + $this->assertContains('validation.yaml', $calls[4][1][0][2]); + } + + public function testFormsCanBeEnabledWithoutCsrfProtection() + { + $container = $this->createContainerFromFile('form_no_csrf'); + + $this->assertFalse($container->getParameter('form.type_extension.csrf.enabled')); + } + + public function testStopwatchEnabledWithDebugModeEnabled() + { + $container = $this->createContainerFromFile('default_config', array( + 'kernel.container_class' => 'foo', + 'kernel.debug' => true, + )); + + $this->assertTrue($container->has('debug.stopwatch')); + } + + public function testStopwatchEnabledWithDebugModeDisabled() + { + $container = $this->createContainerFromFile('default_config', array( + 'kernel.container_class' => 'foo', + )); + + $this->assertTrue($container->has('debug.stopwatch')); + } + + public function testSerializerDisabled() + { + $container = $this->createContainerFromFile('default_config'); + $this->assertSame(!class_exists(FullStack::class) && class_exists(Serializer::class), $container->has('serializer')); + } + + public function testSerializerEnabled() + { + $container = $this->createContainerFromFile('full'); + $this->assertTrue($container->has('serializer')); + + $argument = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0); + + $this->assertCount(2, $argument); + $this->assertEquals('Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader', $argument[0]->getClass()); + $this->assertNull($container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1)); + $this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.normalizer.object')->getArgument(1)); + $this->assertEquals(new Reference('property_info', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $container->getDefinition('serializer.normalizer.object')->getArgument(3)); + $this->assertEquals(array('setCircularReferenceHandler', array(new Reference('my.circular.reference.handler'))), $container->getDefinition('serializer.normalizer.object')->getMethodCalls()[0]); + } + + public function testRegisterSerializerExtractor() + { + $container = $this->createContainerFromFile('full'); + + $serializerExtractorDefinition = $container->getDefinition('property_info.serializer_extractor'); + + $this->assertEquals('serializer.mapping.class_metadata_factory', $serializerExtractorDefinition->getArgument(0)->__toString()); + $this->assertFalse($serializerExtractorDefinition->isPublic()); + $tag = $serializerExtractorDefinition->getTag('property_info.list_extractor'); + $this->assertEquals(array('priority' => -999), $tag[0]); + } + + public function testDataUriNormalizerRegistered() + { + $container = $this->createContainerFromFile('full'); + + $definition = $container->getDefinition('serializer.normalizer.data_uri'); + $tag = $definition->getTag('serializer.normalizer'); + + $this->assertEquals(DataUriNormalizer::class, $definition->getClass()); + $this->assertEquals(-920, $tag[0]['priority']); + } + + public function testDateIntervalNormalizerRegistered() + { + if (!class_exists(DateIntervalNormalizer::class)) { + $this->markTestSkipped('The DateIntervalNormalizer has been introduced in the Serializer Component version 3.4.'); + } + + $container = $this->createContainerFromFile('full'); + + $definition = $container->getDefinition('serializer.normalizer.dateinterval'); + $tag = $definition->getTag('serializer.normalizer'); + + $this->assertEquals(DateIntervalNormalizer::class, $definition->getClass()); + $this->assertEquals(-915, $tag[0]['priority']); + } + + public function testDateTimeNormalizerRegistered() + { + $container = $this->createContainerFromFile('full'); + + $definition = $container->getDefinition('serializer.normalizer.datetime'); + $tag = $definition->getTag('serializer.normalizer'); + + $this->assertEquals(DateTimeNormalizer::class, $definition->getClass()); + $this->assertEquals(-910, $tag[0]['priority']); + } + + public function testJsonSerializableNormalizerRegistered() + { + $container = $this->createContainerFromFile('full'); + + $definition = $container->getDefinition('serializer.normalizer.json_serializable'); + $tag = $definition->getTag('serializer.normalizer'); + + $this->assertEquals(JsonSerializableNormalizer::class, $definition->getClass()); + $this->assertEquals(-900, $tag[0]['priority']); + } + + public function testObjectNormalizerRegistered() + { + $container = $this->createContainerFromFile('full'); + + $definition = $container->getDefinition('serializer.normalizer.object'); + $tag = $definition->getTag('serializer.normalizer'); + + $this->assertEquals('Symfony\Component\Serializer\Normalizer\ObjectNormalizer', $definition->getClass()); + $this->assertEquals(-1000, $tag[0]['priority']); + } + + public function testSerializerCacheActivated() + { + $container = $this->createContainerFromFile('serializer_enabled'); + + $this->assertTrue($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); + + $cache = $container->getDefinition('serializer.mapping.cache_class_metadata_factory')->getArgument(1); + $this->assertEquals(new Reference('serializer.mapping.cache.symfony'), $cache); + } + + public function testSerializerCacheDisabled() + { + $container = $this->createContainerFromFile('serializer_enabled', array('kernel.debug' => true, 'kernel.container_class' => __CLASS__)); + $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); + } + + /** + * @group legacy + * @expectedDeprecation The "framework.serializer.cache" option is deprecated %s. + */ + public function testDeprecatedSerializerCacheOption() + { + $container = $this->createContainerFromFile('serializer_legacy_cache', array('kernel.debug' => true, 'kernel.container_class' => __CLASS__)); + + $this->assertFalse($container->hasDefinition('serializer.mapping.cache_class_metadata_factory')); + $this->assertTrue($container->hasDefinition('serializer.mapping.class_metadata_factory')); + + $cache = $container->getDefinition('serializer.mapping.class_metadata_factory')->getArgument(1); + $this->assertEquals(new Reference('foo'), $cache); + } + + public function testSerializerMapping() + { + $container = $this->createContainerFromFile('serializer_mapping', array('kernel.bundles_metadata' => array('TestBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle\\Tests', 'path' => __DIR__.'/Fixtures/TestBundle', 'parent' => null)))); + $projectDir = $container->getParameter('kernel.project_dir'); + $configDir = __DIR__.'/Fixtures/TestBundle/Resources/config'; + $expectedLoaders = array( + new Definition(AnnotationLoader::class, array(new Reference('annotation_reader'))), + new Definition(XmlFileLoader::class, array($configDir.'/serialization.xml')), + new Definition(YamlFileLoader::class, array($configDir.'/serialization.yml')), + new Definition(YamlFileLoader::class, array($projectDir.'/config/serializer/foo.yml')), + new Definition(XmlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.xml')), + new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/files/foo.yml')), + new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yml')), + new Definition(YamlFileLoader::class, array($configDir.'/serializer_mapping/serialization.yaml')), + ); + + foreach ($expectedLoaders as $definition) { + if (is_file($arg = $definition->getArgument(0))) { + $definition->replaceArgument(0, strtr($arg, '/', \DIRECTORY_SEPARATOR)); + } + $definition->setPublic(false); + } + + $loaders = $container->getDefinition('serializer.mapping.chain_loader')->getArgument(0); + foreach ($loaders as $loader) { + if (is_file($arg = $loader->getArgument(0))) { + $loader->replaceArgument(0, strtr($arg, '/', \DIRECTORY_SEPARATOR)); + } + } + $this->assertEquals($expectedLoaders, $loaders); + } + + public function testAssetHelperWhenAssetsAreEnabled() + { + $container = $this->createContainerFromFile('full'); + $packages = $container->getDefinition('templating.helper.assets')->getArgument(0); + + $this->assertSame('assets.packages', (string) $packages); + } + + public function testAssetHelperWhenTemplatesAreEnabledAndNoAssetsConfiguration() + { + $container = $this->createContainerFromFile('templating_no_assets'); + $packages = $container->getDefinition('templating.helper.assets')->getArgument(0); + + $this->assertSame('assets.packages', (string) $packages); + } + + public function testAssetsHelperIsRemovedWhenPhpTemplatingEngineIsEnabledAndAssetsAreDisabled() + { + $container = $this->createContainerFromFile('templating_php_assets_disabled'); + + $this->assertTrue(!$container->has('templating.helper.assets'), 'The templating.helper.assets helper service is removed when assets are disabled.'); + } + + public function testAssetHelperWhenAssetsAndTemplatesAreDisabled() + { + $container = $this->createContainerFromFile('default_config'); + + $this->assertFalse($container->hasDefinition('templating.helper.assets')); + } + + public function testSerializerServiceIsRegisteredWhenEnabled() + { + $container = $this->createContainerFromFile('serializer_enabled'); + + $this->assertTrue($container->hasDefinition('serializer')); + } + + public function testSerializerServiceIsNotRegisteredWhenDisabled() + { + $container = $this->createContainerFromFile('serializer_disabled'); + + $this->assertFalse($container->hasDefinition('serializer')); + } + + public function testPropertyInfoDisabled() + { + $container = $this->createContainerFromFile('default_config'); + $this->assertFalse($container->has('property_info')); + } + + public function testPropertyInfoEnabled() + { + $container = $this->createContainerFromFile('property_info'); + $this->assertTrue($container->has('property_info')); + } + + public function testEventDispatcherService() + { + $container = $this->createContainer(array('kernel.charset' => 'UTF-8', 'kernel.secret' => 'secret')); + $container->registerExtension(new FrameworkExtension()); + $this->loadFromFile($container, 'default_config'); + $container + ->register('foo', \stdClass::class) + ->setPublic(true) + ->setProperty('dispatcher', new Reference('event_dispatcher')); + $container->compile(); + $this->assertInstanceOf(EventDispatcherInterface::class, $container->get('foo')->dispatcher); + } + + public function testCacheDefaultRedisProvider() + { + $container = $this->createContainerFromFile('cache'); + + $redisUrl = 'redis://localhost'; + $providerId = 'cache_connection.'.ContainerBuilder::hash($redisUrl); + + $this->assertTrue($container->hasDefinition($providerId)); + + $url = $container->getDefinition($providerId)->getArgument(0); + + $this->assertSame($redisUrl, $url); + } + + public function testCacheDefaultRedisProviderWithEnvVar() + { + $container = $this->createContainerFromFile('cache_env_var'); + + $redisUrl = 'redis://paas.com'; + $providerId = 'cache_connection.'.ContainerBuilder::hash($redisUrl); + + $this->assertTrue($container->hasDefinition($providerId)); + + $url = $container->getDefinition($providerId)->getArgument(0); + + $this->assertSame($redisUrl, $url); + } + + public function testCachePoolServices() + { + $container = $this->createContainerFromFile('cache'); + + $this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.foo', 'cache.adapter.apcu', 30); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.bar', 'cache.adapter.doctrine', 5); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.baz', 'cache.adapter.filesystem', 7); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.foobar', 'cache.adapter.psr6', 10); + $this->assertCachePoolServiceDefinitionIsCreated($container, 'cache.def', 'cache.app', 11); + } + + public function testRemovesResourceCheckerConfigCacheFactoryArgumentOnlyIfNoDebug() + { + $container = $this->createContainer(array('kernel.debug' => true)); + (new FrameworkExtension())->load(array(), $container); + $this->assertCount(1, $container->getDefinition('config_cache_factory')->getArguments()); + + $container = $this->createContainer(array('kernel.debug' => false)); + (new FrameworkExtension())->load(array(), $container); + $this->assertEmpty($container->getDefinition('config_cache_factory')->getArguments()); + } + + protected function createContainer(array $data = array()) + { + return new ContainerBuilder(new ParameterBag(array_merge(array( + 'kernel.bundles' => array('FrameworkBundle' => 'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'), + 'kernel.bundles_metadata' => array('FrameworkBundle' => array('namespace' => 'Symfony\\Bundle\\FrameworkBundle', 'path' => __DIR__.'/../..', 'parent' => null)), + 'kernel.cache_dir' => __DIR__, + 'kernel.project_dir' => __DIR__, + 'kernel.debug' => false, + 'kernel.environment' => 'test', + 'kernel.name' => 'kernel', + 'kernel.root_dir' => __DIR__, + 'kernel.container_class' => 'testContainer', + 'container.build_hash' => 'Abc1234', + 'container.build_id' => hash('crc32', 'Abc123423456789'), + 'container.build_time' => 23456789, + ), $data))); + } + + protected function createContainerFromFile($file, $data = array(), $resetCompilerPasses = true, $compile = true) + { + $cacheKey = md5(\get_class($this).$file.serialize($data)); + if ($compile && isset(self::$containerCache[$cacheKey])) { + return self::$containerCache[$cacheKey]; + } + $container = $this->createContainer($data); + $container->registerExtension(new FrameworkExtension()); + $this->loadFromFile($container, $file); + + if ($resetCompilerPasses) { + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + } + $container->getCompilerPassConfig()->setBeforeRemovingPasses(array(new AddConstraintValidatorsPass(), new TranslatorPass('translator.default', 'translation.reader'))); + $container->getCompilerPassConfig()->setAfterRemovingPasses(array(new AddAnnotationsCachedReaderPass())); + + if (!$compile) { + return $container; + } + $container->compile(); + + return self::$containerCache[$cacheKey] = $container; + } + + protected function createContainerFromClosure($closure, $data = array()) + { + $container = $this->createContainer($data); + $container->registerExtension(new FrameworkExtension()); + $loader = new ClosureLoader($container); + $loader->load($closure); + + $container->getCompilerPassConfig()->setOptimizationPasses(array()); + $container->getCompilerPassConfig()->setRemovingPasses(array()); + $container->compile(); + + return $container; + } + + private function assertPathPackage(ContainerBuilder $container, ChildDefinition $package, $basePath, $version, $format) + { + $this->assertEquals('assets.path_package', $package->getParent()); + $this->assertEquals($basePath, $package->getArgument(0)); + $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); + } + + private function assertUrlPackage(ContainerBuilder $container, ChildDefinition $package, $baseUrls, $version, $format) + { + $this->assertEquals('assets.url_package', $package->getParent()); + $this->assertEquals($baseUrls, $package->getArgument(0)); + $this->assertVersionStrategy($container, $package->getArgument(1), $version, $format); + } + + private function assertVersionStrategy(ContainerBuilder $container, Reference $reference, $version, $format) + { + $versionStrategy = $container->getDefinition((string) $reference); + if (null === $version) { + $this->assertEquals('assets.empty_version_strategy', (string) $reference); + } else { + $this->assertEquals('assets.static_version_strategy', $versionStrategy->getParent()); + $this->assertEquals($version, $versionStrategy->getArgument(0)); + $this->assertEquals($format, $versionStrategy->getArgument(1)); + } + } + + private function assertCachePoolServiceDefinitionIsCreated(ContainerBuilder $container, $id, $adapter, $defaultLifetime) + { + $this->assertTrue($container->has($id), sprintf('Service definition "%s" for cache pool of type "%s" is registered', $id, $adapter)); + + $poolDefinition = $container->getDefinition($id); + + $this->assertInstanceOf(ChildDefinition::class, $poolDefinition, sprintf('Cache pool "%s" is based on an abstract cache pool.', $id)); + + $this->assertTrue($poolDefinition->hasTag('cache.pool'), sprintf('Service definition "%s" is tagged with the "cache.pool" tag.', $id)); + $this->assertFalse($poolDefinition->isAbstract(), sprintf('Service definition "%s" is not abstract.', $id)); + + $tag = $poolDefinition->getTag('cache.pool'); + $this->assertArrayHasKey('default_lifetime', $tag[0], 'The default lifetime is stored as an attribute of the "cache.pool" tag.'); + $this->assertSame($defaultLifetime, $tag[0]['default_lifetime'], 'The default lifetime is stored as an attribute of the "cache.pool" tag.'); + + $parentDefinition = $poolDefinition; + do { + $parentId = $parentDefinition->getParent(); + $parentDefinition = $container->findDefinition($parentId); + } while ($parentDefinition instanceof ChildDefinition); + + switch ($adapter) { + case 'cache.adapter.apcu': + $this->assertSame(ApcuAdapter::class, $parentDefinition->getClass()); + break; + case 'cache.adapter.doctrine': + $this->assertSame(DoctrineAdapter::class, $parentDefinition->getClass()); + break; + case 'cache.app': + if (ChainAdapter::class === $parentDefinition->getClass()) { + break; + } + // no break + case 'cache.adapter.filesystem': + $this->assertSame(FilesystemAdapter::class, $parentDefinition->getClass()); + break; + case 'cache.adapter.psr6': + $this->assertSame(ProxyAdapter::class, $parentDefinition->getClass()); + break; + case 'cache.adapter.redis': + $this->assertSame(RedisAdapter::class, $parentDefinition->getClass()); + break; + default: + $this->fail('Unresolved adapter: '.$adapter); + } + } +} + +/** + * Simulates ReplaceAliasByActualDefinitionPass. + */ +class TestAnnotationsPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $container->setDefinition('annotation_reader', $container->getDefinition('annotations.cached_reader')); + $container->removeDefinition('annotations.cached_reader'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.php new file mode 100644 index 0000000..c554532 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/PhpFrameworkExtensionTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; + +class PhpFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/Fixtures/php')); + $loader->load($file.'.php'); + } + + /** + * @expectedException \LogicException + */ + public function testAssetsCannotHavePathAndUrl() + { + $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', array( + 'assets' => array( + 'base_urls' => 'http://cdn.example.com', + 'base_path' => '/foo', + ), + )); + }); + } + + /** + * @expectedException \LogicException + */ + public function testAssetPackageCannotHavePathAndUrl() + { + $this->createContainerFromClosure(function ($container) { + $container->loadFromExtension('framework', array( + 'assets' => array( + 'packages' => array( + 'impossible' => array( + 'base_urls' => 'http://cdn.example.com', + 'base_path' => '/foo', + ), + ), + ), + )); + }); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php new file mode 100644 index 0000000..ac1ba24 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; + +class XmlFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/xml')); + $loader->load($file.'.xml'); + } + + public function testAssetsHelperIsRemovedWhenPhpTemplatingEngineIsEnabledAndAssetsAreDisabled() + { + $this->markTestSkipped('The assets key cannot be set to false using the XML configuration format.'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php b/vendor/symfony/framework-bundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.php new file mode 100644 index 0000000..457467a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/DependencyInjection/YamlFrameworkExtensionTest.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\Bundle\FrameworkBundle\Tests\DependencyInjection; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + +class YamlFrameworkExtensionTest extends FrameworkExtensionTest +{ + protected function loadFromFile(ContainerBuilder $container, $file) + { + $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/Fixtures/yml')); + $loader->load($file.'.yml'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/config/serializer/foo.yml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/config/serializer/foo.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/config/validator/foo.xml b/vendor/symfony/framework-bundle/Tests/DependencyInjection/config/validator/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/DependencyInjection/translations/test_default.en.xlf b/vendor/symfony/framework-bundle/Tests/DependencyInjection/translations/test_default.en.xlf new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php b/vendor/symfony/framework-bundle/Tests/EventListener/ResolveControllerNameSubscriberTest.php new file mode 100644 index 0000000..338c1ec --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/EventListener/ResolveControllerNameSubscriberTest.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\Bundle\FrameworkBundle\Tests\EventListener; + +use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; +use Symfony\Bundle\FrameworkBundle\EventListener\ResolveControllerNameSubscriber; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class ResolveControllerNameSubscriberTest extends TestCase +{ + public function testReplacesControllerAttribute() + { + $parser = $this->getMockBuilder(ControllerNameParser::class)->disableOriginalConstructor()->getMock(); + $parser->expects($this->any()) + ->method('parse') + ->with('AppBundle:Starting:format') + ->willReturn('App\\Final\\Format::methodName'); + $httpKernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock(); + + $request = new Request(); + $request->attributes->set('_controller', 'AppBundle:Starting:format'); + + $subscriber = new ResolveControllerNameSubscriber($parser); + $subscriber->onKernelRequest(new GetResponseEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST)); + $this->assertEquals('App\\Final\\Format::methodName', $request->attributes->get('_controller')); + } + + /** + * @dataProvider provideSkippedControllers + */ + public function testSkipsOtherControllerFormats($controller) + { + $parser = $this->getMockBuilder(ControllerNameParser::class)->disableOriginalConstructor()->getMock(); + $parser->expects($this->never()) + ->method('parse'); + $httpKernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock(); + + $request = new Request(); + $request->attributes->set('_controller', $controller); + + $subscriber = new ResolveControllerNameSubscriber($parser); + $subscriber->onKernelRequest(new GetResponseEvent($httpKernel, $request, HttpKernelInterface::MASTER_REQUEST)); + $this->assertEquals($controller, $request->attributes->get('_controller')); + } + + public function provideSkippedControllers() + { + yield array('Other:format'); + yield array(function () {}); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/BaseBundle.php b/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/BaseBundle.php new file mode 100644 index 0000000..494a18d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/BaseBundle.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\Bundle\FrameworkBundle\Tests\Fixtures\BaseBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class BaseBundle extends Bundle +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/base.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/base.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/controller/base.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/controller/base.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/this.is.a.template.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/BaseBundle/Resources/views/this.is.a.template.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/DeclaredClass.php b/vendor/symfony/framework-bundle/Tests/Fixtures/DeclaredClass.php new file mode 100644 index 0000000..3831d0d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/DeclaredClass.php @@ -0,0 +1,7 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.json new file mode 100644 index 0000000..6998dae --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.json @@ -0,0 +1,4 @@ +{ + "service": "service_2", + "public": false +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.md new file mode 100644 index 0000000..73a4101 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.md @@ -0,0 +1,2 @@ +- Service: `service_2` +- Public: no \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.txt new file mode 100644 index 0000000..0dbee6a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.txt @@ -0,0 +1,3 @@ + + // This service is an alias for the service service_2 + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.xml new file mode 100644 index 0000000..847050b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_2.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json new file mode 100644 index 0000000..efcf34d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.json @@ -0,0 +1,20 @@ +[ + { + "service": "service_1", + "public": true + }, + { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] + } +] diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md new file mode 100644 index 0000000..ac56c0d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.md @@ -0,0 +1,17 @@ +### alias_1 + +- Service: `service_1` +- Public: yes + +### service_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt new file mode 100644 index 0000000..011ae42 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.txt @@ -0,0 +1,21 @@ + // This service is an alias for the service service_1 + +Information for Service "service_1" +=================================== + + ---------------- ----------------------------- +  Option   Value  + ---------------- ----------------------------- + Service ID service_1 + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Autoconfigured no + Factory Class Full\Qualified\FactoryClass + Factory Method get + ---------------- ----------------------------- diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml new file mode 100644 index 0000000..19f5a04 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json new file mode 100644 index 0000000..03780e3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.json @@ -0,0 +1,41 @@ +[ + { + "service": "service_2", + "public": false + }, + { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] + } +] diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md new file mode 100644 index 0000000..406c5dc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.md @@ -0,0 +1,25 @@ +### alias_2 + +- Service: `service_2` +- Public: no + +### service_2 + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt new file mode 100644 index 0000000..735fe01 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.txt @@ -0,0 +1,25 @@ + // This service is an alias for the service service_2 + +Information for Service "service_2" +=================================== + + ----------------- --------------------------------- +  Option   Value  + ----------------- --------------------------------- + Service ID service_2 + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)  + tag1 (attr3: val3)  + tag2 + Calls setMailer + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service factory.service + Factory Method get + ----------------- --------------------------------- diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml new file mode 100644 index 0000000..3c15460 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/alias_with_definition_2.xml @@ -0,0 +1,18 @@ + + + + + + + + + + val1 + val2 + + + val3 + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.json new file mode 100644 index 0000000..cb68091 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.json @@ -0,0 +1,3 @@ +{ + "twig.form.resources": ["bootstrap_3_horizontal_layout.html.twig", "bootstrap_3_layout.html.twig", "form_div_layout.html.twig", "form_table_layout.html.twig"] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.md new file mode 100644 index 0000000..593be0c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.md @@ -0,0 +1,4 @@ +twig.form.resources +=================== + +["bootstrap_3_horizontal_layout.html.twig","bootstrap_3_layo... diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.txt new file mode 100644 index 0000000..903717b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.txt @@ -0,0 +1,5 @@ + --------------------- ----------------------------------------------------------------- +  Parameter   Value  + --------------------- ----------------------------------------------------------------- + twig.form.resources ["bootstrap_3_horizontal_layout.html.twig","bootstrap_3_layo... + --------------------- ----------------------------------------------------------------- diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.xml new file mode 100644 index 0000000..0e16f57 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/array_parameter.xml @@ -0,0 +1,2 @@ + +["bootstrap_3_horizontal_layout.html.twig","bootstrap_3_layo... diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.json new file mode 100644 index 0000000..df76274 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.json @@ -0,0 +1,80 @@ +{ + "definitions": { + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "arguments": [ + { + "type": "service", + "id": "definition2" + }, + "%parameter%", + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [] + }, + [ + "foo", + { + "type": "service", + "id": "definition2" + }, + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": null, + "tags": [] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": "definition_2" + } + ] + ], + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] + } + }, + "aliases": { + "alias_1": { + "service": "service_1", + "public": true + } + }, + "services": { + "service_container": "Symfony\\Component\\DependencyInjection\\ContainerBuilder" + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.md new file mode 100644 index 0000000..757da82 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.md @@ -0,0 +1,34 @@ +Public services +=============== + +Definitions +----------- + +### definition_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Arguments: yes +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` + + +Aliases +------- + +### alias_1 + +- Service: `service_1` +- Public: yes + + +Services +-------- + +- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt new file mode 100644 index 0000000..87f6b21 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.txt @@ -0,0 +1,12 @@ + +Symfony Container Public Services +================================= + + ------------------- -------------------------------------------------------- +  Service ID   Class name  + ------------------- -------------------------------------------------------- + alias_1 alias for "service_1" + definition_1 Full\Qualified\Class1 + service_container Symfony\Component\DependencyInjection\ContainerBuilder + ------------------- -------------------------------------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml new file mode 100644 index 0000000..59811b0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_arguments.xml @@ -0,0 +1,27 @@ + + + + + + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.json new file mode 100644 index 0000000..3419083 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.json @@ -0,0 +1,27 @@ +{ + "definitions": { + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] + } + }, + "aliases": { + "alias_1": { + "service": "service_1", + "public": true + } + }, + "services": { + "service_container": "Symfony\\Component\\DependencyInjection\\ContainerBuilder" + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.md new file mode 100644 index 0000000..b8c62be --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.md @@ -0,0 +1,33 @@ +Public services +=============== + +Definitions +----------- + +### definition_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` + + +Aliases +------- + +### alias_1 + +- Service: `service_1` +- Public: yes + + +Services +-------- + +- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.txt new file mode 100644 index 0000000..87f6b21 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.txt @@ -0,0 +1,12 @@ + +Symfony Container Public Services +================================= + + ------------------- -------------------------------------------------------- +  Service ID   Class name  + ------------------- -------------------------------------------------------- + alias_1 alias for "service_1" + definition_1 Full\Qualified\Class1 + service_container Symfony\Component\DependencyInjection\ContainerBuilder + ------------------- -------------------------------------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.xml new file mode 100644 index 0000000..ac92a28 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_public.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.json new file mode 100644 index 0000000..becd607 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.json @@ -0,0 +1,66 @@ +{ + "definitions": { + "definition_1": { + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] + }, + "definition_2": { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] + } + }, + "aliases": { + "alias_1": { + "service": "service_1", + "public": true + }, + "alias_2": { + "service": "service_2", + "public": false + } + }, + "services": { + "service_container": "Symfony\\Component\\DependencyInjection\\ContainerBuilder" + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.md new file mode 100644 index 0000000..5465566 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.md @@ -0,0 +1,59 @@ +Public and private services +=========================== + +Definitions +----------- + +### definition_1 + +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` + +### definition_2 + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` + + +Aliases +------- + +### alias_1 + +- Service: `service_1` +- Public: yes + +### alias_2 + +- Service: `service_2` +- Public: no + + +Services +-------- + +- `service_container`: `Symfony\Component\DependencyInjection\ContainerBuilder` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.txt new file mode 100644 index 0000000..e23ea6d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.txt @@ -0,0 +1,14 @@ + +Symfony Container Public and Private Services +============================================= + + ------------------- -------------------------------------------------------- +  Service ID   Class name  + ------------------- -------------------------------------------------------- + alias_1 alias for "service_1" + alias_2 alias for "service_2" + definition_1 Full\Qualified\Class1 + definition_2 Full\Qualified\Class2 + service_container Symfony\Component\DependencyInjection\ContainerBuilder + ------------------- -------------------------------------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.xml new file mode 100644 index 0000000..54da4f4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_services.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + val1 + val2 + + + val3 + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.json new file mode 100644 index 0000000..fb9634f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.json @@ -0,0 +1,41 @@ +{ + "definitions": { + "definition_2": { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] + } + }, + "aliases": [], + "services": [] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.md new file mode 100644 index 0000000..9895f1f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.md @@ -0,0 +1,26 @@ +Public and private services with tag `tag1` +=========================================== + +Definitions +----------- + +### definition_2 + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt new file mode 100644 index 0000000..b8b3932 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.txt @@ -0,0 +1,11 @@ + +Symfony Container Public and Private Services Tagged with "tag1" Tag +==================================================================== + + -------------- ------- ------- ------- ----------------------- +  Service ID   attr1   attr2   attr3   Class name  + -------------- ------- ------- ------- ----------------------- + definition_2 val1 val2 Full\Qualified\Class2 + " val3 + -------------- ------- ------- ------- ----------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml new file mode 100644 index 0000000..9c39b65 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tag1.xml @@ -0,0 +1,19 @@ + + + + + + + + + + val1 + val2 + + + val3 + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.json new file mode 100644 index 0000000..7f49ac2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.json @@ -0,0 +1,38 @@ +{ + "tag1": [ + { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ] + } + ], + "tag2": [ + { + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ] + } + ] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.md new file mode 100644 index 0000000..2055962 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.md @@ -0,0 +1,39 @@ +Container tags +============== + +tag1 +---- + +### definition_2 + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` + + +tag2 +---- + +### definition_2 + +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.txt new file mode 100644 index 0000000..45523dc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.txt @@ -0,0 +1,14 @@ + +Symfony Container Public and Private Tags +========================================= + +"tag1" tag +---------- + + * definition_2 + +"tag2" tag +---------- + + * definition_2 + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.xml new file mode 100644 index 0000000..4e98e77 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/builder_1_tags.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.json new file mode 100644 index 0000000..10eccf3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.json @@ -0,0 +1,4 @@ +{ + "type": "function", + "name": "array_key_exists" +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.md new file mode 100644 index 0000000..9cf3135 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.md @@ -0,0 +1,3 @@ + +- Type: `function` +- Name: `array_key_exists` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.txt new file mode 100644 index 0000000..09901c3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.txt @@ -0,0 +1 @@ +array_key_exists() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.xml new file mode 100644 index 0000000..28d100d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_1.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.json new file mode 100644 index 0000000..6397254 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.json @@ -0,0 +1,6 @@ +{ + "type": "function", + "name": "staticMethod", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "static": true +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.md new file mode 100644 index 0000000..c041ee8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.md @@ -0,0 +1,5 @@ + +- Type: `function` +- Name: `staticMethod` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Static: yes diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.txt new file mode 100644 index 0000000..5e3101e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::staticMethod() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.xml new file mode 100644 index 0000000..df49310 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_2.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.json new file mode 100644 index 0000000..f77f13a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.json @@ -0,0 +1,5 @@ +{ + "type": "function", + "name": "method", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass" +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.md new file mode 100644 index 0000000..3b61c0d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.md @@ -0,0 +1,4 @@ + +- Type: `function` +- Name: `method` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.txt new file mode 100644 index 0000000..dde0dbb --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::method() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.xml new file mode 100644 index 0000000..367e8b2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_3.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.json new file mode 100644 index 0000000..6397254 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.json @@ -0,0 +1,6 @@ +{ + "type": "function", + "name": "staticMethod", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "static": true +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.md new file mode 100644 index 0000000..c041ee8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.md @@ -0,0 +1,5 @@ + +- Type: `function` +- Name: `staticMethod` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Static: yes diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.txt new file mode 100644 index 0000000..5e3101e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::staticMethod() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.xml new file mode 100644 index 0000000..df49310 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_4.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.json new file mode 100644 index 0000000..249b59f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.json @@ -0,0 +1,7 @@ +{ + "type": "function", + "name": "staticMethod", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\ExtendedCallableClass", + "static": true, + "parent": true +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.md new file mode 100644 index 0000000..fc69e9b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.md @@ -0,0 +1,6 @@ + +- Type: `function` +- Name: `staticMethod` +- Class: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass` +- Static: yes +- Parent: yes diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.txt new file mode 100644 index 0000000..1c06a7e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\ExtendedCallableClass::parent::staticMethod() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.xml new file mode 100644 index 0000000..05fbb3d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_5.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.json new file mode 100644 index 0000000..1798fdd --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.json @@ -0,0 +1,3 @@ +{ + "type": "closure" +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.md new file mode 100644 index 0000000..474a9dd --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.md @@ -0,0 +1,2 @@ + +- Type: `closure` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.txt new file mode 100644 index 0000000..9b030ab --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.txt @@ -0,0 +1 @@ +\Closure() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.xml new file mode 100644 index 0000000..b0f2ab5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_6.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.json new file mode 100644 index 0000000..0d4d1e8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.json @@ -0,0 +1,4 @@ +{ + "type": "object", + "name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass" +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.md new file mode 100644 index 0000000..c9ad7af --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.md @@ -0,0 +1,3 @@ + +- Type: `object` +- Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.txt new file mode 100644 index 0000000..78ef6a6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.txt @@ -0,0 +1 @@ +Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.xml new file mode 100644 index 0000000..7a5d812 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/callable_7.xml @@ -0,0 +1,2 @@ + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.json new file mode 100644 index 0000000..940bab1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.json @@ -0,0 +1,14 @@ +{ + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.md new file mode 100644 index 0000000..e4d0429 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.md @@ -0,0 +1,10 @@ +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.txt new file mode 100644 index 0000000..88cf4cd --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.txt @@ -0,0 +1,17 @@ + ---------------- ----------------------------- +  Option   Value  + ---------------- ----------------------------- + Service ID - + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Autoconfigured no + Factory Class Full\Qualified\FactoryClass + Factory Method get + ---------------- ----------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.xml new file mode 100644 index 0000000..d5015fd --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_1.xml @@ -0,0 +1,4 @@ + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.json new file mode 100644 index 0000000..1c7b1ae --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.json @@ -0,0 +1,35 @@ +{ + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.md new file mode 100644 index 0000000..6ff6d60 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.md @@ -0,0 +1,18 @@ +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.txt new file mode 100644 index 0000000..2d5b037 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.txt @@ -0,0 +1,21 @@ + ----------------- --------------------------------- +  Option   Value  + ----------------- --------------------------------- + Service ID - + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)  + tag1 (attr3: val3)  + tag2 + Calls setMailer + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service factory.service + Factory Method get + ----------------- --------------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.xml new file mode 100644 index 0000000..72319ec --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_2.xml @@ -0,0 +1,17 @@ + + + + + + + + + val1 + val2 + + + val3 + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.json new file mode 100644 index 0000000..2568b87 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.json @@ -0,0 +1,67 @@ +{ + "class": "Full\\Qualified\\Class1", + "public": true, + "synthetic": false, + "lazy": true, + "shared": true, + "abstract": true, + "autowire": false, + "autoconfigure": false, + "arguments": [ + { + "type": "service", + "id": "definition2" + }, + "%parameter%", + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [ + "arg1", + "arg2" + ], + "file": null, + "tags": [] + }, + [ + "foo", + { + "type": "service", + "id": "definition2" + }, + { + "class": "inline_service", + "public": false, + "synthetic": false, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": null, + "tags": [] + } + ], + [ + { + "type": "service", + "id": "definition_1" + }, + { + "type": "service", + "id": "definition_2" + } + ] + ], + "file": null, + "factory_class": "Full\\Qualified\\FactoryClass", + "factory_method": "get", + "tags": [] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.md new file mode 100644 index 0000000..7cee17a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.md @@ -0,0 +1,11 @@ +- Class: `Full\Qualified\Class1` +- Public: yes +- Synthetic: no +- Lazy: yes +- Shared: yes +- Abstract: yes +- Autowired: no +- Autoconfigured: no +- Arguments: yes +- Factory Class: `Full\Qualified\FactoryClass` +- Factory Method: `get` \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt new file mode 100644 index 0000000..989f96e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.txt @@ -0,0 +1,22 @@ + ---------------- ----------------------------- +  Option   Value  + ---------------- ----------------------------- + Service ID - + Class Full\Qualified\Class1 + Tags - + Public yes + Synthetic no + Lazy yes + Shared yes + Abstract yes + Autowired no + Autoconfigured no + Factory Class Full\Qualified\FactoryClass + Factory Method get + Arguments Service(definition2)  + %parameter%  + Inlined Service  + Array (3 element(s))  + Iterator (2 element(s)) + ---------------- ----------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml new file mode 100644 index 0000000..e250060 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_1.xml @@ -0,0 +1,23 @@ + + + + + %parameter% + + + arg1 + arg2 + + + + foo + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.json new file mode 100644 index 0000000..8f65d27 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.json @@ -0,0 +1,36 @@ +{ + "class": "Full\\Qualified\\Class2", + "public": false, + "synthetic": true, + "lazy": false, + "shared": true, + "abstract": false, + "autowire": false, + "autoconfigure": false, + "arguments": [], + "file": "\/path\/to\/file", + "factory_service": "factory.service", + "factory_method": "get", + "calls": [ + "setMailer" + ], + "tags": [ + { + "name": "tag1", + "parameters": { + "attr1": "val1", + "attr2": "val2" + } + }, + { + "name": "tag1", + "parameters": { + "attr3": "val3" + } + }, + { + "name": "tag2", + "parameters": [] + } + ] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.md new file mode 100644 index 0000000..abe7dd4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.md @@ -0,0 +1,19 @@ +- Class: `Full\Qualified\Class2` +- Public: no +- Synthetic: yes +- Lazy: no +- Shared: yes +- Abstract: no +- Autowired: no +- Autoconfigured: no +- Arguments: no +- File: `/path/to/file` +- Factory Service: `factory.service` +- Factory Method: `get` +- Call: `setMailer` +- Tag: `tag1` + - Attr1: val1 + - Attr2: val2 +- Tag: `tag1` + - Attr3: val3 +- Tag: `tag2` \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt new file mode 100644 index 0000000..2d5b037 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.txt @@ -0,0 +1,21 @@ + ----------------- --------------------------------- +  Option   Value  + ----------------- --------------------------------- + Service ID - + Class Full\Qualified\Class2 + Tags tag1 (attr1: val1, attr2: val2)  + tag1 (attr3: val3)  + tag2 + Calls setMailer + Public no + Synthetic yes + Lazy no + Shared yes + Abstract no + Autowired no + Autoconfigured no + Required File /path/to/file + Factory Service factory.service + Factory Method get + ----------------- --------------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml new file mode 100644 index 0000000..72319ec --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/definition_arguments_2.xml @@ -0,0 +1,17 @@ + + + + + + + + + val1 + val2 + + + val3 + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json new file mode 100644 index 0000000..4b68f0c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.json @@ -0,0 +1,11 @@ +[ + { + "type": "function", + "name": "global_function", + "priority": 255 + }, + { + "type": "closure", + "priority": -1 + } +] diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md new file mode 100644 index 0000000..98b81ec --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.md @@ -0,0 +1,12 @@ +# Registered listeners for event `event1` ordered by descending priority + +## Listener 1 + +- Type: `function` +- Name: `global_function` +- Priority: `255` + +## Listener 2 + +- Type: `closure` +- Priority: `-1` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt new file mode 100644 index 0000000..99c7cba --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.txt @@ -0,0 +1,11 @@ + +Registered Listeners for "event1" Event +======================================= + + ------- ------------------- ---------- +  Order   Callable   Priority  + ------- ------------------- ---------- + #1 global_function() 255 + #2 \Closure() -1 + ------- ------------------- ---------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml new file mode 100644 index 0000000..bc03189 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_event1.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json new file mode 100644 index 0000000..30772d9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.json @@ -0,0 +1,20 @@ +{ + "event1": [ + { + "type": "function", + "name": "global_function", + "priority": 255 + }, + { + "type": "closure", + "priority": -1 + } + ], + "event2": [ + { + "type": "object", + "name": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\CallableClass", + "priority": 0 + } + ] +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md new file mode 100644 index 0000000..eb80978 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.md @@ -0,0 +1,22 @@ +# Registered listeners + +## event1 + +### Listener 1 + +- Type: `function` +- Name: `global_function` +- Priority: `255` + +### Listener 2 + +- Type: `closure` +- Priority: `-1` + +## event2 + +### Listener 1 + +- Type: `object` +- Name: `Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass` +- Priority: `0` diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt new file mode 100644 index 0000000..687323e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.txt @@ -0,0 +1,23 @@ + +Registered Listeners Grouped by Event +===================================== + +"event1" event +-------------- + + ------- ------------------- ---------- +  Order   Callable   Priority  + ------- ------------------- ---------- + #1 global_function() 255 + #2 \Closure() -1 + ------- ------------------- ---------- + +"event2" event +-------------- + + ------- ----------------------------------------------------------------------------------- ---------- +  Order   Callable   Priority  + ------- ----------------------------------------------------------------------------------- ---------- + #1 Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\CallableClass::__invoke() 0 + ------- ----------------------------------------------------------------------------------- ---------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml new file mode 100644 index 0000000..d7443f9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/event_dispatcher_1_events.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.json new file mode 100644 index 0000000..069fdbc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.json @@ -0,0 +1,3 @@ +{ + "database_name": "symfony" +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.md new file mode 100644 index 0000000..239e98d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.md @@ -0,0 +1,4 @@ +database_name +============= + +symfony \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.txt new file mode 100644 index 0000000..a3d4404 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.txt @@ -0,0 +1,6 @@ + --------------- --------- +  Parameter   Value  + --------------- --------- + database_name symfony + --------------- --------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.xml new file mode 100644 index 0000000..8465522 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameter.xml @@ -0,0 +1,2 @@ + +symfony diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.json new file mode 100644 index 0000000..686cbc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.json @@ -0,0 +1,10 @@ +{ + "array": [ + 12, + "Hello world!", + true + ], + "boolean": true, + "integer": 12, + "string": "Hello world!" +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.md new file mode 100644 index 0000000..c1eb4dc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.md @@ -0,0 +1,7 @@ +Container parameters +==================== + +- `array`: `[12,"Hello world!",true]` +- `boolean`: `true` +- `integer`: `12` +- `string`: `Hello world!` \ No newline at end of file diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.txt new file mode 100644 index 0000000..f5b8f1b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.txt @@ -0,0 +1,13 @@ + +Symfony Container Parameters +============================ + + ----------- -------------------------- +  Parameter   Value  + ----------- -------------------------- + array [12,"Hello world!",true] + boolean true + integer 12 + string Hello world! + ----------- -------------------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.xml new file mode 100644 index 0000000..e97f895 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/parameters_1.xml @@ -0,0 +1,7 @@ + + + [12,"Hello world!",true] + true + 12 + Hello world! + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.json new file mode 100644 index 0000000..1108109 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.json @@ -0,0 +1,20 @@ +{ + "path": "\/hello\/{name}", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "GET|HEAD", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": { + "name": "Joseph" + }, + "requirements": { + "name": "[a-z]+" + }, + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.md new file mode 100644 index 0000000..c36d35c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.md @@ -0,0 +1,15 @@ +- Path: /hello/{name} +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: GET|HEAD +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: + - `name`: Joseph +- Requirements: + - `name`: [a-z]+ +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.txt new file mode 100644 index 0000000..25074df --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.txt @@ -0,0 +1,17 @@ ++--------------+-------------------------------------------------------------------+ +| Property | Value | ++--------------+-------------------------------------------------------------------+ +| Route Name | | +| Path | /hello/{name} | +| Path Regex | #PATH_REGEX# | +| Host | localhost | +| Host Regex | #HOST_REGEX# | +| Scheme | http|https | +| Method | GET|HEAD | +| Requirements | name: [a-z]+ | +| Class | Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub | +| Defaults | name: Joseph | +| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | +| | opt1: val1 | +| | opt2: val2 | ++--------------+-------------------------------------------------------------------+ diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.xml new file mode 100644 index 0000000..9ff531c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_1.xml @@ -0,0 +1,20 @@ + + + /hello/{name} + localhost + http + https + GET + HEAD + + Joseph + + + [a-z]+ + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.json new file mode 100644 index 0000000..e190ef0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.json @@ -0,0 +1,16 @@ +{ + "path": "\/name\/add", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "PUT|POST", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": [], + "requirements": "NO CUSTOM", + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.md new file mode 100644 index 0000000..1d776c5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.md @@ -0,0 +1,13 @@ +- Path: /name/add +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: PUT|POST +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: NONE +- Requirements: NO CUSTOM +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.txt new file mode 100644 index 0000000..5593cc0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.txt @@ -0,0 +1,17 @@ ++--------------+-------------------------------------------------------------------+ +| Property | Value | ++--------------+-------------------------------------------------------------------+ +| Route Name | | +| Path | /name/add | +| Path Regex | #PATH_REGEX# | +| Host | localhost | +| Host Regex | #HOST_REGEX# | +| Scheme | http|https | +| Method | PUT|POST | +| Requirements | NO CUSTOM | +| Class | Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub | +| Defaults | NONE | +| Options | compiler_class: Symfony\Component\Routing\RouteCompiler | +| | opt1: val1 | +| | opt2: val2 | ++--------------+-------------------------------------------------------------------+ diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.xml new file mode 100644 index 0000000..584ab1b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_2.xml @@ -0,0 +1,14 @@ + + + /name/add + localhost + http + https + PUT + POST + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.json b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.json new file mode 100644 index 0000000..bd60070 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.json @@ -0,0 +1,38 @@ +{ + "route_1": { + "path": "\/hello\/{name}", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "GET|HEAD", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": { + "name": "Joseph" + }, + "requirements": { + "name": "[a-z]+" + }, + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } + }, + "route_2": { + "path": "\/name\/add", + "pathRegex": "#PATH_REGEX#", + "host": "localhost", + "hostRegex": "#HOST_REGEX#", + "scheme": "http|https", + "method": "PUT|POST", + "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub", + "defaults": [], + "requirements": "NO CUSTOM", + "options": { + "compiler_class": "Symfony\\Component\\Routing\\RouteCompiler", + "opt1": "val1", + "opt2": "val2" + } + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.md b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.md new file mode 100644 index 0000000..cbb70b4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.md @@ -0,0 +1,37 @@ +route_1 +------- + +- Path: /hello/{name} +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: GET|HEAD +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: + - `name`: Joseph +- Requirements: + - `name`: [a-z]+ +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 + + +route_2 +------- + +- Path: /name/add +- Path Regex: #PATH_REGEX# +- Host: localhost +- Host Regex: #HOST_REGEX# +- Scheme: http|https +- Method: PUT|POST +- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub +- Defaults: NONE +- Requirements: NO CUSTOM +- Options: + - `compiler_class`: Symfony\Component\Routing\RouteCompiler + - `opt1`: val1 + - `opt2`: val2 + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.txt b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.txt new file mode 100644 index 0000000..9d06562 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.txt @@ -0,0 +1,7 @@ + --------- ---------- ------------ ----------- --------------- +  Name   Method   Scheme   Host   Path  + --------- ---------- ------------ ----------- --------------- + route_1 GET|HEAD http|https localhost /hello/{name} + route_2 PUT|POST http|https localhost /name/add + --------- ---------- ------------ ----------- --------------- + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.xml b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.xml new file mode 100644 index 0000000..666a537 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Descriptor/route_collection_1.xml @@ -0,0 +1,35 @@ + + + + /hello/{name} + localhost + http + https + GET + HEAD + + Joseph + + + [a-z]+ + + + + + + + + + /name/add + localhost + http + https + PUT + POST + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/BaseBundle/views/base.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/BaseBundle/views/base.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/BaseBundle/views/controller/custom.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/BaseBundle/views/controller/custom.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/translations/messages.fr.yml b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/translations/messages.fr.yml new file mode 100644 index 0000000..767141d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/translations/messages.fr.yml @@ -0,0 +1 @@ +folder: répertoire diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/resource.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/resource.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/this.is.a.template.format.engine b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/this.is.a.template.format.engine new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/translation.html.php b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/translation.html.php new file mode 100644 index 0000000..1dcc553 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Resources/views/translation.html.php @@ -0,0 +1,49 @@ +This template is used for translation message extraction tests +trans('single-quoted key') ?> +trans('double-quoted key') ?> +trans(<<<'EOF' +heredoc key +EOF +) ?> +trans(<<<'EOF' +nowdoc key +EOF +) ?> +trans( + "double-quoted key with whitespace and escaped \$\n\" sequences" +) ?> +trans( + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' +) ?> +trans(<< +trans(<<<'EOF' +nowdoc key with whitespace and nonescaped \$\n sequences +EOF +) ?> + +trans('single-quoted key with "quote mark at the end"') ?> + +transChoice( + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + 10, + array('%count%' => 10) +) ?> + +trans('other-domain-test-no-params-short-array', array(), 'not_messages'); ?> + +trans('other-domain-test-no-params-long-array', array(), 'not_messages'); ?> + +trans('other-domain-test-params-short-array', array('foo' => 'bar'), 'not_messages'); ?> + +trans('other-domain-test-params-long-array', array('foo' => 'bar'), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-short-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +transChoice('other-domain-test-trans-choice-long-array-%count%', 10, array('%count%' => 10), 'not_messages'); ?> + +trans('typecast', array('a' => (int) '123'), 'not_messages'); ?> +transChoice('msg1', 10 + 1, array(), 'not_messages'); ?> +transChoice('msg2', ceil(4.5), array(), 'not_messages'); ?> diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Author.php b/vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Author.php new file mode 100644 index 0000000..efb0b43 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Serialization/Author.php @@ -0,0 +1,8 @@ + + + + + group1 + group2 + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TemplatePathsCache/templates-empty.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TemplatePathsCache/templates-empty.php new file mode 100644 index 0000000..9ba2230 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TemplatePathsCache/templates-empty.php @@ -0,0 +1,2 @@ + __DIR__.'/../path/to/template.html.twig', +); diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..c4bee6c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/Controller/DefaultController.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 TestBundle\Fabpot\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.php new file mode 100644 index 0000000..17894ba --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Fabpot/FooBundle/FabpotFooBundle.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 TestBundle\Fabpot\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FabpotFooBundle extends Bundle +{ + /** + * {@inheritdoc} + */ + public function getParent() + { + return 'SensioFooBundle'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..ddda38c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/DefaultController.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 TestBundle\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.php new file mode 100644 index 0000000..3c889e7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Sub/DefaultController.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 TestBundle\FooBundle\Controller\Sub; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.php new file mode 100644 index 0000000..1bffc7f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/Controller/Test/DefaultController.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 TestBundle\FooBundle\Controller\Test; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.php new file mode 100644 index 0000000..656f17c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/FooBundle/FooBundle.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 TestBundle\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class FooBundle extends Bundle +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..1bb8038 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/Controller/DefaultController.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 TestBundle\Sensio\Cms\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.php new file mode 100644 index 0000000..58967d8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/Cms/FooBundle/SensioCmsFooBundle.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 TestBundle\Sensio\Cms\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class SensioCmsFooBundle extends Bundle +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.php new file mode 100644 index 0000000..86486f0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/Controller/DefaultController.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 TestBundle\Sensio\FooBundle\Controller; + +/** + * DefaultController. + * + * @author Fabien Potencier + */ +class DefaultController +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.php new file mode 100644 index 0000000..d1bc5dc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/TestBundle/Sensio/FooBundle/SensioFooBundle.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 TestBundle\Sensio\FooBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Bundle. + * + * @author Fabien Potencier + */ +class SensioFooBundle extends Bundle +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Article.php b/vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Article.php new file mode 100644 index 0000000..a1ecee6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Validation/Article.php @@ -0,0 +1,8 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/framework-bundle/Tests/Fixtures/Validation/SubCategory.php b/vendor/symfony/framework-bundle/Tests/Fixtures/Validation/SubCategory.php new file mode 100644 index 0000000..7fc982b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Fixtures/Validation/SubCategory.php @@ -0,0 +1,13 @@ + __DIR__.'/../Fixtures/Resources/views/this.is.a.template.format.engine', +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/AnnotatedControllerTest.php b/vendor/symfony/framework-bundle/Tests/Functional/AnnotatedControllerTest.php new file mode 100644 index 0000000..2fdbef8 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/AnnotatedControllerTest.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\Bundle\FrameworkBundle\Tests\Functional; + +class AnnotatedControllerTest extends WebTestCase +{ + /** + * @dataProvider getRoutes + */ + public function testAnnotatedController($path, $expectedValue) + { + $client = $this->createClient(array('test_case' => 'AnnotatedController', 'root_config' => 'config.yml')); + $client->request('GET', '/annotated'.$path); + + $this->assertSame(200, $client->getResponse()->getStatusCode()); + $this->assertSame($expectedValue, $client->getResponse()->getContent()); + } + + public function getRoutes() + { + return array( + array('/null_request', 'Symfony\Component\HttpFoundation\Request'), + array('/null_argument', ''), + array('/null_argument_with_route_param', ''), + array('/null_argument_with_route_param/value', 'value'), + array('/argument_with_route_param_and_default', 'value'), + array('/argument_with_route_param_and_default/custom', 'custom'), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/AutowiringTypesTest.php b/vendor/symfony/framework-bundle/Tests/Functional/AutowiringTypesTest.php new file mode 100644 index 0000000..1fba6d7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/AutowiringTypesTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\CachedReader; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface as FrameworkBundleEngineInterface; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; +use Symfony\Component\Templating\EngineInterface as ComponentEngineInterface; + +class AutowiringTypesTest extends WebTestCase +{ + public function testAnnotationReaderAutowiring() + { + static::bootKernel(array('root_config' => 'no_annotations_cache.yml', 'environment' => 'no_annotations_cache')); + $container = static::$kernel->getContainer(); + + $annotationReader = $container->get('test.autowiring_types.autowired_services')->getAnnotationReader(); + $this->assertInstanceOf(AnnotationReader::class, $annotationReader); + } + + public function testCachedAnnotationReaderAutowiring() + { + static::bootKernel(); + $container = static::$kernel->getContainer(); + + $annotationReader = $container->get('test.autowiring_types.autowired_services')->getAnnotationReader(); + $this->assertInstanceOf(CachedReader::class, $annotationReader); + } + + public function testTemplatingAutowiring() + { + static::bootKernel(); + $container = static::$kernel->getContainer(); + + $autowiredServices = $container->get('test.autowiring_types.autowired_services'); + $this->assertInstanceOf(FrameworkBundleEngineInterface::class, $autowiredServices->getFrameworkBundleEngine()); + $this->assertInstanceOf(ComponentEngineInterface::class, $autowiredServices->getEngine()); + } + + public function testEventDispatcherAutowiring() + { + static::bootKernel(array('debug' => false)); + $container = static::$kernel->getContainer(); + + $autowiredServices = $container->get('test.autowiring_types.autowired_services'); + + if (class_exists(ContainerAwareEventDispatcher::class)) { + $this->assertInstanceOf(ContainerAwareEventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled'); + } else { + $this->assertInstanceOf(EventDispatcher::class, $autowiredServices->getDispatcher(), 'The event_dispatcher service should be injected if the debug is not enabled'); + } + + static::bootKernel(array('debug' => true)); + $container = static::$kernel->getContainer(); + + $autowiredServices = $container->get('test.autowiring_types.autowired_services'); + $this->assertInstanceOf(TraceableEventDispatcher::class, $autowiredServices->getDispatcher(), 'The debug.event_dispatcher service should be injected if the debug is enabled'); + } + + public function testCacheAutowiring() + { + static::bootKernel(); + $container = static::$kernel->getContainer(); + + $autowiredServices = $container->get('test.autowiring_types.autowired_services'); + $this->assertInstanceOf(FilesystemAdapter::class, $autowiredServices->getCachePool()); + } + + protected static function createKernel(array $options = array()) + { + return parent::createKernel(array('test_case' => 'AutowiringTypes') + $options); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.php new file mode 100644 index 0000000..a62cdf6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/AutowiringTypes/AutowiredServices.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\AutowiringTypes; + +use Doctrine\Common\Annotations\Reader; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface as FrameworkBundleEngineInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Templating\EngineInterface; + +class AutowiredServices +{ + private $annotationReader; + private $frameworkBundleEngine; + private $engine; + private $dispatcher; + private $cachePool; + + public function __construct(Reader $annotationReader = null, FrameworkBundleEngineInterface $frameworkBundleEngine, EngineInterface $engine, EventDispatcherInterface $dispatcher, CacheItemPoolInterface $cachePool) + { + $this->annotationReader = $annotationReader; + $this->frameworkBundleEngine = $frameworkBundleEngine; + $this->engine = $engine; + $this->dispatcher = $dispatcher; + $this->cachePool = $cachePool; + } + + public function getAnnotationReader() + { + return $this->annotationReader; + } + + public function getFrameworkBundleEngine() + { + return $this->frameworkBundleEngine; + } + + public function getEngine() + { + return $this->engine; + } + + public function getDispatcher() + { + return $this->dispatcher; + } + + public function getCachePool() + { + return $this->cachePool; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.php new file mode 100644 index 0000000..96543ce --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/AnnotatedController.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Annotation\Route; + +class AnnotatedController +{ + /** + * @Route("/null_request", name="null_request") + */ + public function requestDefaultNullAction(Request $request = null) + { + return new Response($request ? \get_class($request) : null); + } + + /** + * @Route("/null_argument", name="null_argument") + */ + public function argumentDefaultNullWithoutRouteParamAction($value = null) + { + return new Response($value); + } + + /** + * @Route("/null_argument_with_route_param/{value}", name="null_argument_with_route_param") + */ + public function argumentDefaultNullWithRouteParamAction($value = null) + { + return new Response($value); + } + + /** + * @Route("/argument_with_route_param_and_default/{value}", defaults={"value": "value"}, name="argument_with_route_param_and_default") + */ + public function argumentWithoutDefaultWithRouteParamAndDefaultAction($value) + { + return new Response($value); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.php new file mode 100644 index 0000000..88bd102 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/FragmentController.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class FragmentController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction(Request $request) + { + return $this->container->get('templating')->renderResponse('fragment.html.php', array('bar' => new Bar())); + } + + public function inlinedAction($options, $_format) + { + return new Response($options['bar']->getBar().' '.$_format); + } + + public function customFormatAction($_format) + { + return new Response($_format); + } + + public function customLocaleAction(Request $request) + { + return new Response($request->getLocale()); + } + + public function forwardLocaleAction(Request $request) + { + return new Response($request->getLocale()); + } +} + +class Bar +{ + private $bar = 'bar'; + + public function getBar() + { + return $this->bar; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.php new file mode 100644 index 0000000..dd518a1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/ProfilerController.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Response; + +class ProfilerController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction() + { + return new Response('Hello'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.php new file mode 100644 index 0000000..e4d8560 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SessionController.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +class SessionController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function welcomeAction(Request $request, $name = null) + { + $session = $request->getSession(); + + // new session case + if (!$session->has('name')) { + if (!$name) { + return new Response('You are new here and gave no name.'); + } + + // remember name + $session->set('name', $name); + + return new Response(sprintf('Hello %s, nice to meet you.', $name)); + } + + // existing session + $name = $session->get('name'); + + return new Response(sprintf('Welcome back %s, nice to meet you.', $name)); + } + + public function cacheableAction() + { + $response = new Response('all good'); + $response->setSharedMaxAge(100); + + return $response; + } + + public function logoutAction(Request $request) + { + $request->getSession()->invalidate(); + + return new Response('Session cleared.'); + } + + public function setFlashAction(Request $request, $message) + { + $session = $request->getSession(); + $session->getFlashBag()->set('notice', $message); + + return new RedirectResponse($this->container->get('router')->generate('session_showflash')); + } + + public function showFlashAction(Request $request) + { + $session = $request->getSession(); + + if ($session->getFlashBag()->has('notice')) { + list($output) = $session->getFlashBag()->get('notice'); + } else { + $output = 'No flash was set.'; + } + + return new Response($output); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.php new file mode 100644 index 0000000..1df4629 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestController.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +class SubRequestController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction($handler) + { + $errorUrl = $this->generateUrl('subrequest_fragment_error', array('_locale' => 'fr', '_format' => 'json')); + $altUrl = $this->generateUrl('subrequest_fragment', array('_locale' => 'fr', '_format' => 'json')); + + // simulates a failure during the rendering of a fragment... + // should render fr/json + $content = $handler->render($errorUrl, 'inline', array('alt' => $altUrl)); + + // ...to check that the FragmentListener still references the right Request + // when rendering another fragment after the error occurred + // should render en/html instead of fr/json + $content .= $handler->render(new ControllerReference('TestBundle:SubRequest:fragment')); + + // forces the LocaleListener to set fr for the locale... + // should render fr/json + $content .= $handler->render($altUrl); + + // ...and check that after the rendering, the original Request is back + // and en is used as a locale + // should use en/html instead of fr/json + $content .= '--'.$this->generateUrl('subrequest_fragment'); + + // The RouterListener is also tested as if it does not keep the right + // Request in the context, a 301 would be generated + return new Response($content); + } + + public function fragmentAction(Request $request) + { + return new Response('--'.$request->getLocale().'/'.$request->getRequestFormat()); + } + + public function fragmentErrorAction() + { + throw new \RuntimeException('error'); + } + + protected function generateUrl($name, $arguments = array()) + { + return $this->container->get('router')->generate($name, $arguments); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.php new file mode 100644 index 0000000..ae17f60 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Controller/SubRequestServiceResolutionController.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller; + +use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class SubRequestServiceResolutionController implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + public function indexAction() + { + $request = $this->container->get('request_stack')->getCurrentRequest(); + $path['_forwarded'] = $request->attributes; + $path['_controller'] = 'TestBundle:SubRequestServiceResolution:fragment'; + $subRequest = $request->duplicate(array(), null, $path); + + return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + } + + public function fragmentAction(LoggerInterface $logger) + { + return new Response('---'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.php new file mode 100644 index 0000000..53555fd --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/AnnotationReaderPass.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +class AnnotationReaderPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + // simulate using "annotation_reader" in a compiler pass + $container->get('test.annotation_reader'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.php new file mode 100644 index 0000000..d3a9b2a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Config/CustomConfig.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config; + +class CustomConfig +{ + public function addConfiguration($rootNode) + { + $rootNode + ->children() + ->scalarNode('custom')->end() + ->arrayNode('array') + ->children() + ->scalarNode('child1')->end() + ->scalarNode('child2')->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..2f45cd9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/Configuration.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +class Configuration implements ConfigurationInterface +{ + private $customConfig; + + public function __construct($customConfig = null) + { + $this->customConfig = $customConfig; + } + + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('test'); + + if ($this->customConfig) { + $this->customConfig->addConfiguration($rootNode); + } + + return $treeBuilder; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.php new file mode 100644 index 0000000..6648937 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/DependencyInjection/TestExtension.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; + +class TestExtension extends Extension implements PrependExtensionInterface +{ + private $customConfig; + + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + $container->setAlias('test.annotation_reader', new Alias('annotation_reader', true)); + } + + /** + * {@inheritdoc} + */ + public function prepend(ContainerBuilder $container) + { + $container->prependExtensionConfig('test', array('custom' => 'foo')); + } + + /** + * {@inheritdoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + return new Configuration($this->customConfig); + } + + public function setCustomConfig($customConfig) + { + $this->customConfig = $customConfig; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml new file mode 100644 index 0000000..923204a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/Resources/config/routing.yml @@ -0,0 +1,54 @@ +session_welcome: + path: /session + defaults: { _controller: TestBundle:Session:welcome } + +session_cacheable: + path: /cacheable + defaults: { _controller: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SessionController::cacheableAction } + +session_welcome_name: + path: /session/{name} + defaults: { _controller: TestBundle:Session:welcome } + +session_logout: + path: /session_logout + defaults: { _controller: TestBundle:Session:logout} + +session_setflash: + path: /session_setflash/{message} + defaults: { _controller: TestBundle:Session:setFlash} + +session_showflash: + path: /session_showflash + defaults: { _controller: TestBundle:Session:showFlash} + +profiler: + path: /profiler + defaults: { _controller: TestBundle:Profiler:index } + +subrequest_index: + path: /subrequest/{_locale}.{_format} + defaults: { _controller: TestBundle:SubRequest:index, _format: "html" } + schemes: [https] + +subrequest_fragment_error: + path: /subrequest/fragment/error/{_locale}.{_format} + defaults: { _controller: TestBundle:SubRequest:fragmentError, _format: "html" } + schemes: [http] + +subrequest_fragment: + path: /subrequest/fragment/{_locale}.{_format} + defaults: { _controller: TestBundle:SubRequest:fragment, _format: "html" } + schemes: [http] + +fragment_home: + path: /fragment_home + defaults: { _controller: TestBundle:Fragment:index, _format: txt } + +fragment_inlined: + path: /fragment_inlined + defaults: { _controller: TestBundle:Fragment:inlined } + +array_controller: + path: /array_controller + defaults: { _controller: [ArrayController, someAction] } diff --git a/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/TestBundle.php b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/TestBundle.php new file mode 100644 index 0000000..83dd4dc --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/Bundle/TestBundle/TestBundle.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\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle; + +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\AnnotationReaderPass; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\DependencyInjection\Config\CustomConfig; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class TestBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + + /** @var $extension DependencyInjection\TestExtension */ + $extension = $container->getExtension('test'); + + $extension->setCustomConfig(new CustomConfig()); + + $container->addCompilerPass(new AnnotationReaderPass(), PassConfig::TYPE_AFTER_REMOVING); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/CachePoolClearCommandTest.php b/vendor/symfony/framework-bundle/Tests/Functional/CachePoolClearCommandTest.php new file mode 100644 index 0000000..384beeb --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/CachePoolClearCommandTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Command\CachePoolClearCommand; +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * @group functional + */ +class CachePoolClearCommandTest extends WebTestCase +{ + protected function setUp() + { + static::bootKernel(array('test_case' => 'CachePoolClear', 'root_config' => 'config.yml')); + } + + public function testClearPrivatePool() + { + $tester = $this->createCommandTester(); + $tester->execute(array('pools' => array('cache.private_pool')), array('decorated' => false)); + + $this->assertSame(0, $tester->getStatusCode(), 'cache:pool:clear exits with 0 in case of success'); + $this->assertContains('Clearing cache pool: cache.private_pool', $tester->getDisplay()); + $this->assertContains('[OK] Cache was successfully cleared.', $tester->getDisplay()); + } + + public function testClearPublicPool() + { + $tester = $this->createCommandTester(); + $tester->execute(array('pools' => array('cache.public_pool')), array('decorated' => false)); + + $this->assertSame(0, $tester->getStatusCode(), 'cache:pool:clear exits with 0 in case of success'); + $this->assertContains('Clearing cache pool: cache.public_pool', $tester->getDisplay()); + $this->assertContains('[OK] Cache was successfully cleared.', $tester->getDisplay()); + } + + public function testClearPoolWithCustomClearer() + { + $tester = $this->createCommandTester(); + $tester->execute(array('pools' => array('cache.pool_with_clearer')), array('decorated' => false)); + + $this->assertSame(0, $tester->getStatusCode(), 'cache:pool:clear exits with 0 in case of success'); + $this->assertContains('Clearing cache pool: cache.pool_with_clearer', $tester->getDisplay()); + $this->assertContains('[OK] Cache was successfully cleared.', $tester->getDisplay()); + } + + public function testCallClearer() + { + $tester = $this->createCommandTester(); + $tester->execute(array('pools' => array('cache.app_clearer')), array('decorated' => false)); + + $this->assertSame(0, $tester->getStatusCode(), 'cache:pool:clear exits with 0 in case of success'); + $this->assertContains('Calling cache clearer: cache.app_clearer', $tester->getDisplay()); + $this->assertContains('[OK] Cache was successfully cleared.', $tester->getDisplay()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException + * @expectedExceptionMessage You have requested a non-existent service "unknown_pool" + */ + public function testClearUnexistingPool() + { + $this->createCommandTester() + ->execute(array('pools' => array('unknown_pool')), array('decorated' => false)); + } + + /** + * @group legacy + * @expectedDeprecation Symfony\Bundle\FrameworkBundle\Command\CachePoolClearCommand::__construct() expects an instance of "Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer" as first argument since Symfony 3.4. Not passing it is deprecated and will throw a TypeError in 4.0. + */ + public function testLegacyClearCommand() + { + $application = new Application(static::$kernel); + $application->add(new CachePoolClearCommand()); + + $tester = new CommandTester($application->find('cache:pool:clear')); + + $tester->execute(array('pools' => array())); + + $this->assertContains('Cache was successfully cleared', $tester->getDisplay()); + } + + private function createCommandTester() + { + $container = static::$kernel->getContainer(); + $application = new Application(static::$kernel); + $application->add(new CachePoolClearCommand($container->get('cache.global_clearer'))); + + return new CommandTester($application->find('cache:pool:clear')); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/CachePoolsTest.php b/vendor/symfony/framework-bundle/Tests/Functional/CachePoolsTest.php new file mode 100644 index 0000000..eafc798 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/CachePoolsTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +class CachePoolsTest extends WebTestCase +{ + public function testCachePools() + { + $this->doTestCachePools(array(), AdapterInterface::class); + } + + /** + * @requires extension redis + */ + public function testRedisCachePools() + { + try { + $this->doTestCachePools(array('root_config' => 'redis_config.yml', 'environment' => 'redis_cache'), RedisAdapter::class); + } catch (\PHPUnit\Framework\Error\Warning $e) { + if (0 !== strpos($e->getMessage(), 'unable to connect to')) { + throw $e; + } + $this->markTestSkipped($e->getMessage()); + } catch (\PHPUnit_Framework_Error_Warning $e) { + if (0 !== strpos($e->getMessage(), 'unable to connect to')) { + throw $e; + } + $this->markTestSkipped($e->getMessage()); + } catch (InvalidArgumentException $e) { + if (0 !== strpos($e->getMessage(), 'Redis connection failed')) { + throw $e; + } + $this->markTestSkipped($e->getMessage()); + } + } + + /** + * @requires extension redis + */ + public function testRedisCustomCachePools() + { + try { + $this->doTestCachePools(array('root_config' => 'redis_custom_config.yml', 'environment' => 'custom_redis_cache'), RedisAdapter::class); + } catch (\PHPUnit\Framework\Error\Warning $e) { + if (0 !== strpos($e->getMessage(), 'unable to connect to')) { + throw $e; + } + $this->markTestSkipped($e->getMessage()); + } catch (\PHPUnit_Framework_Error_Warning $e) { + if (0 !== strpos($e->getMessage(), 'unable to connect to')) { + throw $e; + } + $this->markTestSkipped($e->getMessage()); + } + } + + private function doTestCachePools($options, $adapterClass) + { + static::bootKernel($options); + $container = static::$kernel->getContainer(); + + $pool1 = $container->get('cache.pool1'); + $this->assertInstanceOf($adapterClass, $pool1); + + $key = 'foobar'; + $pool1->deleteItem($key); + $item = $pool1->getItem($key); + $this->assertFalse($item->isHit()); + + $item->set('baz'); + $pool1->save($item); + $item = $pool1->getItem($key); + $this->assertTrue($item->isHit()); + + $pool2 = $container->get('cache.pool2'); + $pool2->save($item); + + $container->get('cache_clearer')->clear($container->getParameter('kernel.cache_dir')); + $item = $pool1->getItem($key); + $this->assertFalse($item->isHit()); + + $item = $pool2->getItem($key); + $this->assertTrue($item->isHit()); + } + + protected static function createKernel(array $options = array()) + { + return parent::createKernel(array('test_case' => 'CachePools') + $options); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/ConfigDebugCommandTest.php b/vendor/symfony/framework-bundle/Tests/Functional/ConfigDebugCommandTest.php new file mode 100644 index 0000000..a988799 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/ConfigDebugCommandTest.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * @group functional + */ +class ConfigDebugCommandTest extends WebTestCase +{ + private $application; + + protected function setUp() + { + $kernel = static::createKernel(array('test_case' => 'ConfigDump', 'root_config' => 'config.yml')); + $this->application = new Application($kernel); + $this->application->doRun(new ArrayInput(array()), new NullOutput()); + } + + public function testDumpBundleName() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'TestBundle')); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('custom: foo', $tester->getDisplay()); + } + + public function testDumpBundleOption() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'TestBundle', 'path' => 'custom')); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('foo', $tester->getDisplay()); + } + + public function testParametersValuesAreResolved() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'framework')); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertContains("locale: '%env(LOCALE)%'", $tester->getDisplay()); + $this->assertContains('secret: test', $tester->getDisplay()); + } + + public function testDumpUndefinedBundleOption() + { + $tester = $this->createCommandTester(); + $tester->execute(array('name' => 'TestBundle', 'path' => 'foo')); + + $this->assertContains('Unable to find configuration for "test.foo"', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $command = $this->application->find('debug:config'); + + return new CommandTester($command); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/ConfigDumpReferenceCommandTest.php b/vendor/symfony/framework-bundle/Tests/Functional/ConfigDumpReferenceCommandTest.php new file mode 100644 index 0000000..3817afe --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/ConfigDumpReferenceCommandTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * @group functional + */ +class ConfigDumpReferenceCommandTest extends WebTestCase +{ + private $application; + + protected function setUp() + { + $kernel = static::createKernel(array('test_case' => 'ConfigDump', 'root_config' => 'config.yml')); + $this->application = new Application($kernel); + $this->application->doRun(new ArrayInput(array()), new NullOutput()); + } + + public function testDumpBundleName() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array('name' => 'TestBundle')); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertContains('test:', $tester->getDisplay()); + $this->assertContains(' custom:', $tester->getDisplay()); + } + + public function testDumpAtPath() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array( + 'name' => 'test', + 'path' => 'array', + )); + + $this->assertSame(0, $ret, 'Returns 0 in case of success'); + $this->assertSame(<<<'EOL' +# Default configuration for extension with alias: "test" at path "array" +array: + child1: ~ + child2: ~ + + +EOL + , $tester->getDisplay(true)); + } + + public function testDumpAtPathXml() + { + $tester = $this->createCommandTester(); + $ret = $tester->execute(array( + 'name' => 'test', + 'path' => 'array', + '--format' => 'xml', + )); + + $this->assertSame(1, $ret); + $this->assertContains('[ERROR] The "path" option is only available for the "yaml" format.', $tester->getDisplay()); + } + + /** + * @return CommandTester + */ + private function createCommandTester() + { + $command = $this->application->find('config:dump-reference'); + + return new CommandTester($command); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/ContainerDebugCommandTest.php b/vendor/symfony/framework-bundle/Tests/Functional/ContainerDebugCommandTest.php new file mode 100644 index 0000000..d19b08f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/ContainerDebugCommandTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\ApplicationTester; + +/** + * @group functional + */ +class ContainerDebugCommandTest extends WebTestCase +{ + public function testDumpContainerIfNotExists() + { + static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml')); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + @unlink(static::$kernel->getContainer()->getParameter('debug.container.dump')); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'debug:container')); + + $this->assertFileExists(static::$kernel->getContainer()->getParameter('debug.container.dump')); + } + + public function testNoDebug() + { + static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml', 'debug' => false)); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'debug:container')); + + $this->assertContains('public', $tester->getDisplay()); + } + + public function testPrivateAlias() + { + static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml')); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'debug:container', '--show-private' => true)); + $this->assertContains('public', $tester->getDisplay()); + $this->assertContains('private_alias', $tester->getDisplay()); + + $tester->run(array('command' => 'debug:container')); + $this->assertContains('public', $tester->getDisplay()); + $this->assertNotContains('private_alias', $tester->getDisplay()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/ContainerDumpTest.php b/vendor/symfony/framework-bundle/Tests/Functional/ContainerDumpTest.php new file mode 100644 index 0000000..423d673 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/ContainerDumpTest.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\Bundle\FrameworkBundle\Tests\Functional; + +/** + * Checks that the container compiles correctly when all the bundle features are enabled. + */ +class ContainerDumpTest extends WebTestCase +{ + public function testContainerCompilationInDebug() + { + $client = $this->createClient(array('test_case' => 'ContainerDump', 'root_config' => 'config.yml')); + + $this->assertTrue($client->getContainer()->has('serializer')); + } + + public function testContainerCompilation() + { + $client = $this->createClient(array('test_case' => 'ContainerDump', 'root_config' => 'config.yml', 'debug' => false)); + + $this->assertTrue($client->getContainer()->has('serializer')); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/DebugAutowiringCommandTest.php b/vendor/symfony/framework-bundle/Tests/Functional/DebugAutowiringCommandTest.php new file mode 100644 index 0000000..0b7f929 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/DebugAutowiringCommandTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Component\Console\Tester\ApplicationTester; + +/** + * @group functional + */ +class DebugAutowiringCommandTest extends WebTestCase +{ + public function testBasicFunctionality() + { + static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml')); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'debug:autowiring')); + + $this->assertContains('Symfony\Component\HttpKernel\HttpKernelInterface', $tester->getDisplay()); + $this->assertContains('alias to http_kernel', $tester->getDisplay()); + } + + public function testSearchArgument() + { + static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml')); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'debug:autowiring', 'search' => 'kern')); + + $this->assertContains('Symfony\Component\HttpKernel\HttpKernelInterface', $tester->getDisplay()); + $this->assertNotContains('Symfony\Component\Routing\RouterInterface', $tester->getDisplay()); + } + + public function testSearchNoResults() + { + static::bootKernel(array('test_case' => 'ContainerDebug', 'root_config' => 'config.yml')); + + $application = new Application(static::$kernel); + $application->setAutoExit(false); + + $tester = new ApplicationTester($application); + $tester->run(array('command' => 'debug:autowiring', 'search' => 'foo_fake'), array('capture_stderr_separately' => true)); + + $this->assertContains('No autowirable classes or interfaces found matching "foo_fake"', $tester->getErrorOutput()); + $this->assertEquals(1, $tester->getStatusCode()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/FragmentTest.php b/vendor/symfony/framework-bundle/Tests/Functional/FragmentTest.php new file mode 100644 index 0000000..dff65d6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/FragmentTest.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\Bundle\FrameworkBundle\Tests\Functional; + +class FragmentTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testFragment($insulate) + { + $client = $this->createClient(array('test_case' => 'Fragment', 'root_config' => 'config.yml')); + if ($insulate) { + $client->insulate(); + } + + $client->request('GET', '/fragment_home'); + + $this->assertEquals('bar txt--html--es--fr', $client->getResponse()->getContent()); + } + + public function getConfigs() + { + return array( + array(false), + array(true), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/ProfilerTest.php b/vendor/symfony/framework-bundle/Tests/Functional/ProfilerTest.php new file mode 100644 index 0000000..2d422b0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/ProfilerTest.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\Bundle\FrameworkBundle\Tests\Functional; + +class ProfilerTest extends WebTestCase +{ + /** + * @dataProvider getConfigs + */ + public function testProfilerIsDisabled($insulate) + { + $client = $this->createClient(array('test_case' => 'Profiler', 'root_config' => 'config.yml')); + if ($insulate) { + $client->insulate(); + } + + $client->request('GET', '/profiler'); + $this->assertFalse($client->getProfile()); + + // enable the profiler for the next request + $client->enableProfiler(); + $crawler = $client->request('GET', '/profiler'); + $profile = $client->getProfile(); + $this->assertInternalType('object', $profile); + + $client->request('GET', '/profiler'); + $this->assertFalse($client->getProfile()); + } + + public function getConfigs() + { + return array( + array(false), + array(true), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/PropertyInfoTest.php b/vendor/symfony/framework-bundle/Tests/Functional/PropertyInfoTest.php new file mode 100644 index 0000000..f98072c --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/PropertyInfoTest.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\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Component\PropertyInfo\Type; + +class PropertyInfoTest extends WebTestCase +{ + public function testPhpDocPriority() + { + static::bootKernel(array('test_case' => 'Serializer')); + $container = static::$kernel->getContainer(); + + $this->assertEquals(array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_INT))), $container->get('test.property_info')->getTypes('Symfony\Bundle\FrameworkBundle\Tests\Functional\Dummy', 'codes')); + } +} + +class Dummy +{ + /** + * @param int[] $codes + */ + public function setCodes(array $codes) + { + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/SerializerTest.php b/vendor/symfony/framework-bundle/Tests/Functional/SerializerTest.php new file mode 100644 index 0000000..bc7dc12 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/SerializerTest.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\Bundle\FrameworkBundle\Tests\Functional; + +/** + * @author Kévin Dunglas + */ +class SerializerTest extends WebTestCase +{ + public function testDeserializeArrayOfObject() + { + static::bootKernel(array('test_case' => 'Serializer')); + $container = static::$kernel->getContainer(); + + $result = $container->get('serializer')->deserialize('{"bars": [{"id": 1}, {"id": 2}]}', Foo::class, 'json'); + + $bar1 = new Bar(); + $bar1->id = 1; + $bar2 = new Bar(); + $bar2->id = 2; + + $expected = new Foo(); + $expected->bars = array($bar1, $bar2); + + $this->assertEquals($expected, $result); + } +} + +class Foo +{ + /** + * @var Bar[] + */ + public $bars; +} + +class Bar +{ + /** + * @var int + */ + public $id; +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/SessionTest.php b/vendor/symfony/framework-bundle/Tests/Functional/SessionTest.php new file mode 100644 index 0000000..2e16342 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/SessionTest.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\Bundle\FrameworkBundle\Tests\Functional; + +class SessionTest extends WebTestCase +{ + /** + * Tests session attributes persist. + * + * @dataProvider getConfigs + */ + public function testWelcome($config, $insulate) + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client->insulate(); + } + + // no session + $crawler = $client->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler->text()); + + // remember name + $crawler = $client->request('GET', '/session/drak'); + $this->assertContains('Hello drak, nice to meet you.', $crawler->text()); + + // prove remembered name + $crawler = $client->request('GET', '/session'); + $this->assertContains('Welcome back drak, nice to meet you.', $crawler->text()); + + // clear session + $crawler = $client->request('GET', '/session_logout'); + $this->assertContains('Session cleared.', $crawler->text()); + + // prove cleared session + $crawler = $client->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler->text()); + } + + /** + * Tests flash messages work in practice. + * + * @dataProvider getConfigs + */ + public function testFlash($config, $insulate) + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client->insulate(); + } + + // set flash + $crawler = $client->request('GET', '/session_setflash/Hello%20world.'); + + // check flash displays on redirect + $this->assertContains('Hello world.', $client->followRedirect()->text()); + + // check flash is gone + $crawler = $client->request('GET', '/session_showflash'); + $this->assertContains('No flash was set.', $crawler->text()); + } + + /** + * See if two separate insulated clients can run without + * polluting eachother's session data. + * + * @dataProvider getConfigs + */ + public function testTwoClients($config, $insulate) + { + // start first client + $client1 = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client1->insulate(); + } + + // start second client + $client2 = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client2->insulate(); + } + + // new session, so no name set. + $crawler1 = $client1->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler1->text()); + + // set name of client1 + $crawler1 = $client1->request('GET', '/session/client1'); + $this->assertContains('Hello client1, nice to meet you.', $crawler1->text()); + + // no session for client2 + $crawler2 = $client2->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler2->text()); + + // remember name client2 + $crawler2 = $client2->request('GET', '/session/client2'); + $this->assertContains('Hello client2, nice to meet you.', $crawler2->text()); + + // prove remembered name of client1 + $crawler1 = $client1->request('GET', '/session'); + $this->assertContains('Welcome back client1, nice to meet you.', $crawler1->text()); + + // prove remembered name of client2 + $crawler2 = $client2->request('GET', '/session'); + $this->assertContains('Welcome back client2, nice to meet you.', $crawler2->text()); + + // clear client1 + $crawler1 = $client1->request('GET', '/session_logout'); + $this->assertContains('Session cleared.', $crawler1->text()); + + // prove client1 data is cleared + $crawler1 = $client1->request('GET', '/session'); + $this->assertContains('You are new here and gave no name.', $crawler1->text()); + + // prove remembered name of client2 remains untouched. + $crawler2 = $client2->request('GET', '/session'); + $this->assertContains('Welcome back client2, nice to meet you.', $crawler2->text()); + } + + /** + * @dataProvider getConfigs + */ + public function testCorrectCacheControlHeadersForCacheableAction($config, $insulate) + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => $config)); + if ($insulate) { + $client->insulate(); + } + + $client->request('GET', '/cacheable'); + + $response = $client->getResponse(); + $this->assertSame('public, s-maxage=100', $response->headers->get('cache-control')); + } + + public function getConfigs() + { + return array( + // configfile, insulate + array('config.yml', true), + array('config.yml', false), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/SubRequestsTest.php b/vendor/symfony/framework-bundle/Tests/Functional/SubRequestsTest.php new file mode 100644 index 0000000..1a87ff9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/SubRequestsTest.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\Bundle\FrameworkBundle\Tests\Functional; + +class SubRequestsTest extends WebTestCase +{ + public function testStateAfterSubRequest() + { + $client = $this->createClient(array('test_case' => 'Session', 'root_config' => 'config.yml')); + $client->request('GET', 'https://localhost/subrequest/en'); + + $this->assertEquals('--fr/json--en/html--fr/json--http://localhost/subrequest/fragment/en', $client->getResponse()->getContent()); + } + + public function testSubRequestControllerServicesAreResolved() + { + $client = $this->createClient(array('test_case' => 'ControllerServiceResolution', 'root_config' => 'config.yml')); + $client->request('GET', 'https://localhost/subrequest'); + + $this->assertEquals('---', $client->getResponse()->getContent()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/WebTestCase.php b/vendor/symfony/framework-bundle/Tests/Functional/WebTestCase.php new file mode 100644 index 0000000..b25ab8e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/WebTestCase.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional; + +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; +use Symfony\Component\Filesystem\Filesystem; + +class WebTestCase extends BaseWebTestCase +{ + public static function assertRedirect($response, $location) + { + self::assertTrue($response->isRedirect(), 'Response is not a redirect, got status code: '.$response->getStatusCode()); + self::assertEquals('http://localhost'.$location, $response->headers->get('Location')); + } + + public static function setUpBeforeClass() + { + static::deleteTmpDir(); + } + + public static function tearDownAfterClass() + { + static::deleteTmpDir(); + } + + protected static function deleteTmpDir() + { + if (!file_exists($dir = sys_get_temp_dir().'/'.static::getVarDir())) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } + + protected static function getKernelClass() + { + require_once __DIR__.'/app/AppKernel.php'; + + return 'Symfony\Bundle\FrameworkBundle\Tests\Functional\app\AppKernel'; + } + + protected static function createKernel(array $options = array()) + { + $class = self::getKernelClass(); + + if (!isset($options['test_case'])) { + throw new \InvalidArgumentException('The option "test_case" must be set.'); + } + + return new $class( + static::getVarDir(), + $options['test_case'], + isset($options['root_config']) ? $options['root_config'] : 'config.yml', + isset($options['environment']) ? $options['environment'] : strtolower(static::getVarDir().$options['test_case']), + isset($options['debug']) ? $options['debug'] : true + ); + } + + protected static function getVarDir() + { + return 'FB'.substr(strrchr(\get_called_class(), '\\'), 1); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/config.yml new file mode 100644 index 0000000..377d3e7 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/config.yml @@ -0,0 +1,2 @@ +imports: + - { resource: ../config/default.yml } diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/routing.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/routing.yml new file mode 100644 index 0000000..ebd18a0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AnnotatedController/routing.yml @@ -0,0 +1,4 @@ +annotated_controller: + prefix: /annotated + resource: "@TestBundle/Controller/AnnotatedController.php" + type: annotation diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AppKernel.php b/vendor/symfony/framework-bundle/Tests/Functional/app/AppKernel.php new file mode 100644 index 0000000..e239bb5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AppKernel.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app; + +use Psr\Log\NullLogger; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Kernel; + +/** + * App Test Kernel for functional tests. + * + * @author Johannes M. Schmitt + */ +class AppKernel extends Kernel +{ + private $varDir; + private $testCase; + private $rootConfig; + + public function __construct($varDir, $testCase, $rootConfig, $environment, $debug) + { + if (!is_dir(__DIR__.'/'.$testCase)) { + throw new \InvalidArgumentException(sprintf('The test case "%s" does not exist.', $testCase)); + } + $this->varDir = $varDir; + $this->testCase = $testCase; + + $fs = new Filesystem(); + if (!$fs->isAbsolutePath($rootConfig) && !file_exists($rootConfig = __DIR__.'/'.$testCase.'/'.$rootConfig)) { + throw new \InvalidArgumentException(sprintf('The root config "%s" does not exist.', $rootConfig)); + } + $this->rootConfig = $rootConfig; + + parent::__construct($environment, $debug); + } + + public function registerBundles() + { + if (!file_exists($filename = $this->getRootDir().'/'.$this->testCase.'/bundles.php')) { + throw new \RuntimeException(sprintf('The bundles file "%s" does not exist.', $filename)); + } + + return include $filename; + } + + public function getRootDir() + { + return __DIR__; + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.$this->varDir.'/'.$this->testCase.'/logs'; + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + $loader->load($this->rootConfig); + } + + protected function build(ContainerBuilder $container) + { + $container->register('logger', NullLogger::class); + } + + public function serialize() + { + return serialize(array($this->varDir, $this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug())); + } + + public function unserialize($str) + { + $a = unserialize($str); + $this->__construct($a[0], $a[1], $a[2], $a[3], $a[4]); + } + + protected function getKernelParameters() + { + $parameters = parent::getKernelParameters(); + $parameters['kernel.test_case'] = $this->testCase; + + return $parameters; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/config.yml new file mode 100644 index 0000000..1b47c11 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/config.yml @@ -0,0 +1,11 @@ +imports: + - { resource: ../config/default.yml } + +services: + _defaults: { public: true } + test.autowiring_types.autowired_services: + class: Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\AutowiringTypes\AutowiredServices + autowire: true +framework: + templating: + engines: ['php'] diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml new file mode 100644 index 0000000..fec387d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/AutowiringTypes/no_annotations_cache.yml @@ -0,0 +1,6 @@ +imports: + - { resource: config.yml } + +framework: + annotations: + cache: none diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/config.yml new file mode 100644 index 0000000..b991dd5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePoolClear/config.yml @@ -0,0 +1,22 @@ +imports: + - { resource: ../config/default.yml } + +services: + dummy: + class: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\DeclaredClass + arguments: ['@cache.private_pool'] + public: true + custom_clearer: + parent: cache.default_clearer + tags: + - name: kernel.cache_clearer + +framework: + cache: + pools: + cache.private_pool: ~ + cache.public_pool: + public: true + cache.pool_with_clearer: + public: true + clearer: custom_clearer diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/config.yml new file mode 100644 index 0000000..de1e144 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/config.yml @@ -0,0 +1,14 @@ +imports: + - { resource: ../config/default.yml } + +framework: + cache: + pools: + cache.pool1: + public: true + adapter: cache.system + cache.pool2: + public: true + adapter: cache.pool3 + cache.pool3: + clearer: ~ diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_config.yml new file mode 100644 index 0000000..3bf10f4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_config.yml @@ -0,0 +1,17 @@ +imports: + - { resource: ../config/default.yml } + +parameters: + env(REDIS_HOST): 'localhost' + +framework: + cache: + app: cache.adapter.redis + default_redis_provider: "redis://%env(REDIS_HOST)%" + pools: + cache.pool1: + public: true + clearer: cache.system_clearer + cache.pool2: + public: true + clearer: ~ diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_custom_config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_custom_config.yml new file mode 100644 index 0000000..d0a2197 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/CachePools/redis_custom_config.yml @@ -0,0 +1,28 @@ +imports: + - { resource: ../config/default.yml } + +parameters: + env(REDIS_HOST): 'localhost' + +services: + cache.test_redis_connection: + public: false + class: Redis + calls: + - [connect, ['%env(REDIS_HOST)%']] + + cache.app: + parent: cache.adapter.redis + tags: + - name: cache.pool + provider: cache.test_redis_connection + +framework: + cache: + pools: + cache.pool1: + public: true + clearer: cache.system_clearer + cache.pool2: + public: true + clearer: ~ diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/config.yml new file mode 100644 index 0000000..c1d1288 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ConfigDump/config.yml @@ -0,0 +1,10 @@ +imports: + - { resource: ../config/default.yml } + +framework: + secret: '%secret%' + default_locale: '%env(LOCALE)%' + +parameters: + env(LOCALE): en + secret: test diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/config.yml new file mode 100644 index 0000000..d00d6f2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDebug/config.yml @@ -0,0 +1,10 @@ +imports: + - { resource: ../config/default.yml } + +services: + _defaults: { public: true } + public: + class: Symfony\Bundle\FrameworkBundle\Tests\Fixtures\DeclaredClass + private_alias: + alias: public + public: false diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/config.yml new file mode 100644 index 0000000..29f1e32 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ContainerDump/config.yml @@ -0,0 +1,21 @@ +imports: + - { resource: ../config/default.yml } + +framework: + esi: true + ssi: true + fragments: true + profiler: true + router: true + session: true + request: true + templating: + enabled: true + engines: ['php'] + assets: true + translator: true + validation: true + serializer: true + property_info: true + csrf_protection: true + form: true diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/config.yml new file mode 100644 index 0000000..d196ce9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/config.yml @@ -0,0 +1,10 @@ +imports: + - { resource: ../config/default.yml } + +services: + Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SubRequestServiceResolutionController: + public: true + tags: [controller.service_arguments] + + logger: { class: Psr\Log\NullLogger } + Psr\Log\LoggerInterface: '@logger' diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/routing.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/routing.yml new file mode 100644 index 0000000..ffd9471 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/ControllerServiceResolution/routing.yml @@ -0,0 +1,4 @@ +sub_request_page: + path: /subrequest + defaults: + _controller: 'TestBundle:SubRequestServiceResolution:index' diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/config.yml new file mode 100644 index 0000000..f7ea83d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/config.yml @@ -0,0 +1,7 @@ +imports: + - { resource: ../config/default.yml } + +framework: + fragments: ~ + templating: + engines: ['php'] diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/routing.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/routing.yml new file mode 100644 index 0000000..8a9bd84 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Fragment/routing.yml @@ -0,0 +1,2 @@ +_fragmenttest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/config.yml new file mode 100644 index 0000000..12ce67e --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/config.yml @@ -0,0 +1,7 @@ +imports: + - { resource: ../config/default.yml } + +framework: + profiler: + enabled: true + collect: false diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/routing.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/routing.yml new file mode 100644 index 0000000..d4b77c3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Profiler/routing.yml @@ -0,0 +1,2 @@ +_sessiontest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Resources/views/fragment.html.php b/vendor/symfony/framework-bundle/Tests/Functional/app/Resources/views/fragment.html.php new file mode 100644 index 0000000..8ea3c36 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Resources/views/fragment.html.php @@ -0,0 +1,14 @@ +get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:inlined', array( + 'options' => array( + 'bar' => $bar, + 'eleven' => 11, + ), + ))); +?>--get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:customformat', array('_format' => 'html'))); +?>--get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:customlocale', array('_locale' => 'es'))); +?>--getRequest()->setLocale('fr'); + echo $this->get('actions')->render($this->get('actions')->controller('TestBundle:Fragment:forwardlocale')); +?> diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/bundles.php new file mode 100644 index 0000000..144db90 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/bundles.php @@ -0,0 +1,16 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + +return array( + new FrameworkBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/config.yml new file mode 100644 index 0000000..e409004 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Serializer/config.yml @@ -0,0 +1,10 @@ +imports: + - { resource: ../config/default.yml } + +services: + _defaults: { public: true } + test.property_info: '@property_info' + +framework: + serializer: { enabled: true } + property_info: { enabled: true } diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Session/bundles.php b/vendor/symfony/framework-bundle/Tests/Functional/app/Session/bundles.php new file mode 100644 index 0000000..422ffc9 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Session/bundles.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\TestBundle; + +return array( + new FrameworkBundle(), + new TestBundle(), +); diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Session/config.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Session/config.yml new file mode 100644 index 0000000..ad6bdb6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Session/config.yml @@ -0,0 +1,7 @@ +imports: + - { resource: ./../config/default.yml } + +services: + Symfony\Bundle\FrameworkBundle\Tests\Functional\Bundle\TestBundle\Controller\SubRequestController: + tags: + - { name: controller.service_arguments, action: indexAction, argument: handler, id: fragment.handler } diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/Session/routing.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/Session/routing.yml new file mode 100644 index 0000000..d4b77c3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/Session/routing.yml @@ -0,0 +1,2 @@ +_sessiontest_bundle: + resource: '@TestBundle/Resources/config/routing.yml' diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/config/default.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/config/default.yml new file mode 100644 index 0000000..8ddd7ed --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/config/default.yml @@ -0,0 +1,2 @@ +imports: + - { resource: framework.yml } diff --git a/vendor/symfony/framework-bundle/Tests/Functional/app/config/framework.yml b/vendor/symfony/framework-bundle/Tests/Functional/app/config/framework.yml new file mode 100644 index 0000000..a313b33 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Functional/app/config/framework.yml @@ -0,0 +1,13 @@ +framework: + secret: test + router: { resource: "%kernel.root_dir%/%kernel.test_case%/routing.yml" } + validation: { enabled: true, enable_annotations: true } + csrf_protection: true + form: true + test: ~ + default_locale: en + session: + storage_id: session.storage.mock_file + +services: + logger: { class: Psr\Log\NullLogger } diff --git a/vendor/symfony/framework-bundle/Tests/Kernel/ConcreteMicroKernel.php b/vendor/symfony/framework-bundle/Tests/Kernel/ConcreteMicroKernel.php new file mode 100644 index 0000000..3c80e70 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Kernel/ConcreteMicroKernel.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\Bundle\FrameworkBundle\Tests\Kernel; + +use Psr\Log\NullLogger; +use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Routing\RouteCollectionBuilder; + +class ConcreteMicroKernel extends Kernel implements EventSubscriberInterface +{ + use MicroKernelTrait; + + private $cacheDir; + + public function onKernelException(GetResponseForExceptionEvent $event) + { + if ($event->getException() instanceof Danger) { + $event->setResponse(Response::create('It\'s dangerous to go alone. Take this ⚔')); + } + } + + public function halloweenAction() + { + return new Response('halloween'); + } + + public function dangerousAction() + { + throw new Danger(); + } + + public function registerBundles() + { + return array( + new FrameworkBundle(), + ); + } + + public function getCacheDir() + { + return $this->cacheDir = sys_get_temp_dir().'/sf_micro_kernel'; + } + + public function getLogDir() + { + return $this->cacheDir; + } + + public function __destruct() + { + $fs = new Filesystem(); + $fs->remove($this->cacheDir); + } + + protected function configureRoutes(RouteCollectionBuilder $routes) + { + $routes->add('/', 'kernel:halloweenAction'); + $routes->add('/danger', 'kernel:dangerousAction'); + } + + protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader) + { + $c->register('logger', NullLogger::class); + $c->loadFromExtension('framework', array( + 'secret' => '$ecret', + )); + + $c->setParameter('halloween', 'Have a great day!'); + $c->register('halloween', 'stdClass')->setPublic(true); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + KernelEvents::EXCEPTION => 'onKernelException', + ); + } +} + +class Danger extends \RuntimeException +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Kernel/MicroKernelTraitTest.php b/vendor/symfony/framework-bundle/Tests/Kernel/MicroKernelTraitTest.php new file mode 100644 index 0000000..539306f --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Kernel/MicroKernelTraitTest.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\Bundle\FrameworkBundle\Tests\Kernel; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; + +class MicroKernelTraitTest extends TestCase +{ + public function test() + { + $kernel = new ConcreteMicroKernel('test', false); + $kernel->boot(); + + $request = Request::create('/'); + $response = $kernel->handle($request); + + $this->assertEquals('halloween', $response->getContent()); + $this->assertEquals('Have a great day!', $kernel->getContainer()->getParameter('halloween')); + $this->assertInstanceOf('stdClass', $kernel->getContainer()->get('halloween')); + } + + public function testAsEventSubscriber() + { + $kernel = new ConcreteMicroKernel('test', false); + $kernel->boot(); + + $request = Request::create('/danger'); + $response = $kernel->handle($request); + + $this->assertSame('It\'s dangerous to go alone. Take this ⚔', $response->getContent()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Routing/DelegatingLoaderTest.php b/vendor/symfony/framework-bundle/Tests/Routing/DelegatingLoaderTest.php new file mode 100644 index 0000000..4071583 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Routing/DelegatingLoaderTest.php @@ -0,0 +1,20 @@ +getMockBuilder(ControllerNameParser::class) + ->disableOriginalConstructor() + ->getMock(); + new DelegatingLoader($controllerNameParser, new LoaderResolver()); + $this->assertTrue(true, '__construct() takes a ControllerNameParser and LoaderResolverInterface respectively as its first and second argument.'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Routing/RedirectableUrlMatcherTest.php b/vendor/symfony/framework-bundle/Tests/Routing/RedirectableUrlMatcherTest.php new file mode 100644 index 0000000..b5f7a3b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Routing/RedirectableUrlMatcherTest.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\Bundle\FrameworkBundle\Tests\Routing; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RedirectableUrlMatcherTest extends TestCase +{ + public function testRedirectWhenNoSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $matcher = new RedirectableUrlMatcher($coll, $context = new RequestContext()); + + $this->assertEquals(array( + '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', + 'path' => '/foo/', + 'permanent' => true, + 'scheme' => null, + 'httpPort' => $context->getHttpPort(), + 'httpsPort' => $context->getHttpsPort(), + '_route' => 'foo', + ), + $matcher->match('/foo') + ); + } + + public function testSchemeRedirect() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + + $matcher = new RedirectableUrlMatcher($coll, $context = new RequestContext()); + + $this->assertEquals(array( + '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', + 'path' => '/foo', + 'permanent' => true, + 'scheme' => 'https', + 'httpPort' => $context->getHttpPort(), + 'httpsPort' => $context->getHttpsPort(), + '_route' => 'foo', + ), + $matcher->match('/foo') + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Routing/RouterTest.php b/vendor/symfony/framework-bundle/Tests/Routing/RouterTest.php new file mode 100644 index 0000000..ba5db79 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Routing/RouterTest.php @@ -0,0 +1,264 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Routing; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Routing\Router; +use Symfony\Component\DependencyInjection\Config\ContainerParametersResource; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RouterTest extends TestCase +{ + public function testGenerateWithServiceParam() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + ' /{_locale}', + array( + '_locale' => '%locale%', + ), + array( + '_locale' => 'en|es', + ), array(), '', array(), array(), '"%foo%" == "bar"' + )); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('locale', 'es'); + $sc->setParameter('foo', 'bar'); + + $router = new Router($sc, 'foo'); + + $this->assertSame('/en', $router->generate('foo', array('_locale' => 'en'))); + $this->assertSame('/', $router->generate('foo', array('_locale' => 'es'))); + $this->assertSame('"bar" == "bar"', $router->getRouteCollection()->get('foo')->getCondition()); + } + + public function testDefaultsPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%escaped%%', + 'boo' => array('%parameter%', '%%escaped_parameter%%', array('%bee_parameter%', 'bee')), + 'bee' => array('bee', 'bee'), + ), + array( + ) + )); + + $sc = $this->getServiceContainer($routes); + + $sc->setParameter('parameter.foo', 'foo'); + $sc->setParameter('parameter.bar', 'bar'); + $sc->setParameter('parameter', 'boo'); + $sc->setParameter('bee_parameter', 'foo_bee'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%escaped%', + 'boo' => array('boo', '%escaped_parameter%', array('foo_bee', 'bee')), + 'bee' => array('bee', 'bee'), + ), + $route->getDefaults() + ); + } + + public function testRequirementsPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route( + '/foo', + array( + ), + array( + 'foo' => 'before_%parameter.foo%', + 'bar' => '%parameter.bar%_after', + 'baz' => '%%escaped%%', + ) + )); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('parameter.foo', 'foo'); + $sc->setParameter('parameter.bar', 'bar'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + array( + 'foo' => 'before_foo', + 'bar' => 'bar_after', + 'baz' => '%escaped%', + ), + $route->getRequirements() + ); + } + + public function testPatternPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/before/%parameter.foo%/after/%%escaped%%')); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('parameter.foo', 'foo'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%escaped%', + $route->getPath() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Using "%env(FOO)%" is not allowed in routing configuration. + */ + public function testEnvPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%env(FOO)%')); + + $router = new Router($this->getServiceContainer($routes), 'foo'); + $router->getRouteCollection(); + } + + public function testHostPlaceholders() + { + $routes = new RouteCollection(); + + $route = new Route('foo'); + $route->setHost('/before/%parameter.foo%/after/%%escaped%%'); + + $routes->add('foo', $route); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('parameter.foo', 'foo'); + + $router = new Router($sc, 'foo'); + $route = $router->getRouteCollection()->get('foo'); + + $this->assertEquals( + '/before/foo/after/%escaped%', + $route->getHost() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException + * @expectedExceptionMessage You have requested a non-existent parameter "nope". + */ + public function testExceptionOnNonExistentParameter() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%nope%')); + + $sc = $this->getServiceContainer($routes); + + $router = new Router($sc, 'foo'); + $router->getRouteCollection()->get('foo'); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage The container parameter "object", used in the route configuration value "/%object%", must be a string or numeric, but it is of type object. + */ + public function testExceptionOnNonStringParameter() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%object%')); + + $sc = $this->getServiceContainer($routes); + $sc->setParameter('object', new \stdClass()); + + $router = new Router($sc, 'foo'); + $router->getRouteCollection()->get('foo'); + } + + /** + * @dataProvider getNonStringValues + */ + public function testDefaultValuesAsNonStrings($value) + { + $routes = new RouteCollection(); + $routes->add('foo', new Route('foo', array('foo' => $value), array('foo' => '\d+'))); + + $sc = $this->getServiceContainer($routes); + + $router = new Router($sc, 'foo'); + + $route = $router->getRouteCollection()->get('foo'); + + $this->assertSame($value, $route->getDefault('foo')); + } + + public function testGetRouteCollectionAddsContainerParametersResource() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('foo', new Route('/%locale%')); + + $sc = $this->getServiceContainer($routeCollection); + $sc->setParameter('locale', 'en'); + + $router = new Router($sc, 'foo'); + + $routeCollection = $router->getRouteCollection(); + + $this->assertEquals(array(new ContainerParametersResource(array('locale' => 'en'))), $routeCollection->getResources()); + } + + public function getNonStringValues() + { + return array(array(null), array(false), array(true), array(new \stdClass()), array(array('foo', 'bar')), array(array(array()))); + } + + /** + * @return \Symfony\Component\DependencyInjection\Container + */ + private function getServiceContainer(RouteCollection $routes) + { + $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); + + $loader + ->expects($this->any()) + ->method('load') + ->will($this->returnValue($routes)) + ; + + $sc = $this->getMockBuilder('Symfony\\Component\\DependencyInjection\\Container')->setMethods(array('get'))->getMock(); + + $sc + ->expects($this->once()) + ->method('get') + ->will($this->returnValue($loader)) + ; + + return $sc; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/DelegatingEngineTest.php b/vendor/symfony/framework-bundle/Tests/Templating/DelegatingEngineTest.php new file mode 100644 index 0000000..e3ff92b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/DelegatingEngineTest.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine; +use Symfony\Component\HttpFoundation\Response; + +class DelegatingEngineTest extends TestCase +{ + public function testSupportsRetrievesEngineFromTheContainer() + { + $container = $this->getContainerMock(array( + 'engine.first' => $this->getEngineMock('template.php', false), + 'engine.second' => $this->getEngineMock('template.php', true), + )); + + $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); + + $this->assertTrue($delegatingEngine->supports('template.php')); + } + + public function testGetExistingEngine() + { + $firstEngine = $this->getEngineMock('template.php', false); + $secondEngine = $this->getEngineMock('template.php', true); + $container = $this->getContainerMock(array( + 'engine.first' => $firstEngine, + 'engine.second' => $secondEngine, + )); + + $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); + + $this->assertSame($secondEngine, $delegatingEngine->getEngine('template.php')); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage No engine is able to work with the template "template.php" + */ + public function testGetInvalidEngine() + { + $firstEngine = $this->getEngineMock('template.php', false); + $secondEngine = $this->getEngineMock('template.php', false); + $container = $this->getContainerMock(array( + 'engine.first' => $firstEngine, + 'engine.second' => $secondEngine, + )); + + $delegatingEngine = new DelegatingEngine($container, array('engine.first', 'engine.second')); + $delegatingEngine->getEngine('template.php'); + } + + public function testRenderResponseWithFrameworkEngine() + { + $response = new Response(); + $engine = $this->getFrameworkEngineMock('template.php', true); + $engine->expects($this->once()) + ->method('renderResponse') + ->with('template.php', array('foo' => 'bar')) + ->will($this->returnValue($response)); + $container = $this->getContainerMock(array('engine' => $engine)); + + $delegatingEngine = new DelegatingEngine($container, array('engine')); + + $this->assertSame($response, $delegatingEngine->renderResponse('template.php', array('foo' => 'bar'))); + } + + public function testRenderResponseWithTemplatingEngine() + { + $engine = $this->getEngineMock('template.php', true); + $container = $this->getContainerMock(array('engine' => $engine)); + $delegatingEngine = new DelegatingEngine($container, array('engine')); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $delegatingEngine->renderResponse('template.php', array('foo' => 'bar'))); + } + + private function getEngineMock($template, $supports) + { + $engine = $this->getMockBuilder('Symfony\Component\Templating\EngineInterface')->getMock(); + + $engine->expects($this->once()) + ->method('supports') + ->with($template) + ->will($this->returnValue($supports)); + + return $engine; + } + + private function getFrameworkEngineMock($template, $supports) + { + $engine = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + + $engine->expects($this->once()) + ->method('supports') + ->with($template) + ->will($this->returnValue($supports)); + + return $engine; + } + + private function getContainerMock($services) + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + + $i = 0; + foreach ($services as $id => $service) { + $container->expects($this->at($i++)) + ->method('get') + ->with($id) + ->will($this->returnValue($service)); + } + + return $container; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/GlobalVariablesTest.php b/vendor/symfony/framework-bundle/Tests/Templating/GlobalVariablesTest.php new file mode 100644 index 0000000..348820b --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/GlobalVariablesTest.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\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; + +class GlobalVariablesTest extends TestCase +{ + private $container; + private $globals; + + protected function setUp() + { + $this->container = new Container(); + $this->globals = new GlobalVariables($this->container); + } + + public function testGetTokenNoTokenStorage() + { + $this->assertNull($this->globals->getToken()); + } + + public function testGetTokenNoToken() + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); + $this->container->set('security.token_storage', $tokenStorage); + $this->assertNull($this->globals->getToken()); + } + + public function testGetToken() + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); + + $this->container->set('security.token_storage', $tokenStorage); + + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue('token')); + + $this->assertSame('token', $this->globals->getToken()); + } + + public function testGetUserNoTokenStorage() + { + $this->assertNull($this->globals->getUser()); + } + + public function testGetUserNoToken() + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); + $this->container->set('security.token_storage', $tokenStorage); + $this->assertNull($this->globals->getUser()); + } + + /** + * @dataProvider getUserProvider + */ + public function testGetUser($user, $expectedUser) + { + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface')->getMock(); + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + + $this->container->set('security.token_storage', $tokenStorage); + + $token + ->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue($token)); + + $this->assertSame($expectedUser, $this->globals->getUser()); + } + + public function getUserProvider() + { + $user = $this->getMockBuilder('Symfony\Component\Security\Core\User\UserInterface')->getMock(); + $std = new \stdClass(); + $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock(); + + return array( + array($user, $user), + array($std, $std), + array($token, $token), + array('Anon.', null), + array(null, null), + array(10, null), + array(true, null), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/AssetsHelperTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/AssetsHelperTest.php new file mode 100644 index 0000000..01fac95 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/AssetsHelperTest.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\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\AssetsHelper; +use Symfony\Component\Asset\Package; +use Symfony\Component\Asset\Packages; +use Symfony\Component\Asset\VersionStrategy\StaticVersionStrategy; + +class AssetsHelperTest extends TestCase +{ + private $helper; + + protected function setUp() + { + $fooPackage = new Package(new StaticVersionStrategy('42', '%s?v=%s')); + $barPackage = new Package(new StaticVersionStrategy('22', '%s?%s')); + + $packages = new Packages($fooPackage, array('bar' => $barPackage)); + + $this->helper = new AssetsHelper($packages); + } + + public function testGetUrl() + { + $this->assertEquals('me.png?v=42', $this->helper->getUrl('me.png')); + $this->assertEquals('me.png?22', $this->helper->getUrl('me.png', 'bar')); + } + + public function testGetVersion() + { + $this->assertEquals('42', $this->helper->getVersion('/')); + $this->assertEquals('22', $this->helper->getVersion('/', 'bar')); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.php new file mode 100644 index 0000000..9835bc2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTemplateNameParser.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\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures; + +use Symfony\Component\Templating\TemplateNameParserInterface; +use Symfony\Component\Templating\TemplateReference; + +class StubTemplateNameParser implements TemplateNameParserInterface +{ + private $root; + + private $rootTheme; + + public function __construct($root, $rootTheme) + { + $this->root = $root; + $this->rootTheme = $rootTheme; + } + + public function parse($name) + { + list($bundle, $controller, $template) = explode(':', $name, 3); + + if ('_' == $template[0]) { + $path = $this->rootTheme.'/Custom/'.$template; + } elseif ('TestBundle' === $bundle) { + $path = $this->rootTheme.'/'.$controller.'/'.$template; + } else { + $path = $this->root.'/'.$controller.'/'.$template; + } + + return new TemplateReference($path, 'php'); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTranslator.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTranslator.php new file mode 100644 index 0000000..17d1fd4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Fixtures/StubTranslator.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\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures; + +use Symfony\Component\Translation\TranslatorInterface; + +class StubTranslator implements TranslatorInterface +{ + public function trans($id, array $parameters = array(), $domain = null, $locale = null) + { + return '[trans]'.$id.'[/trans]'; + } + + public function transChoice($id, $number, array $parameters = array(), $domain = null, $locale = null) + { + return '[trans]'.$id.'[/trans]'; + } + + public function setLocale($locale) + { + } + + public function getLocale() + { + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php new file mode 100644 index 0000000..e655250 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator; +use Symfony\Component\Form\Extension\Templating\TemplatingExtension; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\AbstractDivLayoutTest; +use Symfony\Component\Templating\Loader\FilesystemLoader; +use Symfony\Component\Templating\PhpEngine; + +class FormHelperDivLayoutTest extends AbstractDivLayoutTest +{ + /** + * @var PhpEngine + */ + protected $engine; + + protected function getExtensions() + { + // should be moved to the Form component once absolute file paths are supported + // by the default name parser in the Templating component + $reflClass = new \ReflectionClass('Symfony\Bundle\FrameworkBundle\FrameworkBundle'); + $root = realpath(\dirname($reflClass->getFileName()).'/Resources/views'); + $rootTheme = realpath(__DIR__.'/Resources'); + $templateNameParser = new StubTemplateNameParser($root, $rootTheme); + $loader = new FilesystemLoader(array()); + + $this->engine = new PhpEngine($templateNameParser, $loader); + $this->engine->addGlobal('global', ''); + $this->engine->setHelpers(array( + new TranslatorHelper(new StubTranslator()), + )); + + return array_merge(parent::getExtensions(), array( + new TemplatingExtension($this->engine, $this->csrfTokenManager, array( + 'FrameworkBundle:Form', + )), + )); + } + + protected function tearDown() + { + $this->engine = null; + + parent::tearDown(); + } + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testMoneyWidgetInIso() + { + $this->engine->setCharset('ISO-8859-1'); + + $view = $this->factory + ->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\MoneyType') + ->createView() + ; + + $this->assertSame('€ ', $this->renderWidget($view)); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->form($view, $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + return (string) $this->engine->get('form')->label($view, $label, $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->engine->get('form')->errors($view); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->widget($view, $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->row($view, $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->rest($view, $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->start($view, $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->end($view, $vars); + } + + protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true) + { + $this->engine->get('form')->setTheme($view, $themes, $useDefaultThemes); + } + + public static function themeBlockInheritanceProvider() + { + return array( + array(array('TestBundle:Parent')), + ); + } + + public static function themeInheritanceProvider() + { + return array( + array(array('TestBundle:Parent'), array('TestBundle:Child')), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php new file mode 100644 index 0000000..46c2011 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/FormHelperTableLayoutTest.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\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use Symfony\Bundle\FrameworkBundle\Templating\Helper\TranslatorHelper; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Tests\Templating\Helper\Fixtures\StubTranslator; +use Symfony\Component\Form\Extension\Templating\TemplatingExtension; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\Tests\AbstractTableLayoutTest; +use Symfony\Component\Templating\Loader\FilesystemLoader; +use Symfony\Component\Templating\PhpEngine; + +class FormHelperTableLayoutTest extends AbstractTableLayoutTest +{ + /** + * @var PhpEngine + */ + protected $engine; + + public function testStartTagHasNoActionAttributeWhenActionIsEmpty() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + public function testStartTagHasActionAttributeWhenActionIsZero() + { + $form = $this->factory->create('Symfony\Component\Form\Extension\Core\Type\FormType', null, array( + 'method' => 'get', + 'action' => '0', + )); + + $html = $this->renderStart($form->createView()); + + $this->assertSame('', $html); + } + + protected function getExtensions() + { + // should be moved to the Form component once absolute file paths are supported + // by the default name parser in the Templating component + $reflClass = new \ReflectionClass('Symfony\Bundle\FrameworkBundle\FrameworkBundle'); + $root = realpath(\dirname($reflClass->getFileName()).'/Resources/views'); + $rootTheme = realpath(__DIR__.'/Resources'); + $templateNameParser = new StubTemplateNameParser($root, $rootTheme); + $loader = new FilesystemLoader(array()); + + $this->engine = new PhpEngine($templateNameParser, $loader); + $this->engine->addGlobal('global', ''); + $this->engine->setHelpers(array( + new TranslatorHelper(new StubTranslator()), + )); + + return array_merge(parent::getExtensions(), array( + new TemplatingExtension($this->engine, $this->csrfTokenManager, array( + 'FrameworkBundle:Form', + 'FrameworkBundle:FormTable', + )), + )); + } + + protected function tearDown() + { + $this->engine = null; + + parent::tearDown(); + } + + protected function renderForm(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->form($view, $vars); + } + + protected function renderLabel(FormView $view, $label = null, array $vars = array()) + { + return (string) $this->engine->get('form')->label($view, $label, $vars); + } + + protected function renderErrors(FormView $view) + { + return (string) $this->engine->get('form')->errors($view); + } + + protected function renderWidget(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->widget($view, $vars); + } + + protected function renderRow(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->row($view, $vars); + } + + protected function renderRest(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->rest($view, $vars); + } + + protected function renderStart(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->start($view, $vars); + } + + protected function renderEnd(FormView $view, array $vars = array()) + { + return (string) $this->engine->get('form')->end($view, $vars); + } + + protected function setTheme(FormView $view, array $themes, $useDefaultThemes = true) + { + $this->engine->get('form')->setTheme($view, $themes, $useDefaultThemes); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/RequestHelperTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/RequestHelperTest.php new file mode 100644 index 0000000..6795bc1 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/RequestHelperTest.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\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\RequestHelper; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +class RequestHelperTest extends TestCase +{ + protected $requestStack; + + protected function setUp() + { + $this->requestStack = new RequestStack(); + $request = new Request(); + $request->initialize(array('foobar' => 'bar')); + $this->requestStack->push($request); + } + + public function testGetParameter() + { + $helper = new RequestHelper($this->requestStack); + + $this->assertEquals('bar', $helper->getParameter('foobar')); + $this->assertEquals('foo', $helper->getParameter('bar', 'foo')); + + $this->assertNull($helper->getParameter('foo')); + } + + public function testGetLocale() + { + $helper = new RequestHelper($this->requestStack); + + $this->assertEquals('en', $helper->getLocale()); + } + + public function testGetName() + { + $helper = new RequestHelper($this->requestStack); + + $this->assertEquals('request', $helper->getName()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Child/form_label.html.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Child/form_label.html.php new file mode 100644 index 0000000..aebb53d --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Child/form_label.html.php @@ -0,0 +1 @@ + diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php new file mode 100644 index 0000000..0524003 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_name_c_entry_label.html.php @@ -0,0 +1,2 @@ +humanize($name); } ?> + diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php new file mode 100644 index 0000000..e165a42 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_names_entry_label.html.php @@ -0,0 +1,4 @@ +humanize($name); +} ?> + diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php new file mode 100644 index 0000000..078fe57 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Custom/_text_id_widget.html.php @@ -0,0 +1,3 @@ +
    + widget($form) ?> +
    diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php new file mode 100644 index 0000000..068c5de --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_label.html.php @@ -0,0 +1 @@ + diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php new file mode 100644 index 0000000..1b53a72 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/Resources/Parent/form_widget_simple.html.php @@ -0,0 +1,2 @@ + +block($form, 'widget_attributes'); ?> value="" rel="theme" /> diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/SessionHelperTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/SessionHelperTest.php new file mode 100644 index 0000000..c0fc0ba --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/SessionHelperTest.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\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\SessionHelper; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; + +class SessionHelperTest extends TestCase +{ + protected $requestStack; + + protected function setUp() + { + $request = new Request(); + + $session = new Session(new MockArraySessionStorage()); + $session->set('foobar', 'bar'); + $session->getFlashBag()->set('notice', 'bar'); + + $request->setSession($session); + + $this->requestStack = new RequestStack(); + $this->requestStack->push($request); + } + + protected function tearDown() + { + $this->requestStack = null; + } + + public function testFlash() + { + $helper = new SessionHelper($this->requestStack); + + $this->assertTrue($helper->hasFlash('notice')); + + $this->assertEquals(array('bar'), $helper->getFlash('notice')); + } + + public function testGetFlashes() + { + $helper = new SessionHelper($this->requestStack); + $this->assertEquals(array('notice' => array('bar')), $helper->getFlashes()); + } + + public function testGet() + { + $helper = new SessionHelper($this->requestStack); + + $this->assertEquals('bar', $helper->get('foobar')); + $this->assertEquals('foo', $helper->get('bar', 'foo')); + + $this->assertNull($helper->get('foo')); + } + + public function testGetName() + { + $helper = new SessionHelper($this->requestStack); + + $this->assertEquals('session', $helper->getName()); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Helper/StopwatchHelperTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Helper/StopwatchHelperTest.php new file mode 100644 index 0000000..686c655 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Helper/StopwatchHelperTest.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\Bundle\FrameworkBundle\Tests\Templating\Helper; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Templating\Helper\StopwatchHelper; + +class StopwatchHelperTest extends TestCase +{ + public function testDevEnvironment() + { + $stopwatch = $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch')->getMock(); + $stopwatch->expects($this->once()) + ->method('start') + ->with('foo'); + + $helper = new StopwatchHelper($stopwatch); + $helper->start('foo'); + } + + public function testProdEnvironment() + { + $helper = new StopwatchHelper(null); + $helper->start('foo'); + + // 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); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/Loader/TemplateLocatorTest.php b/vendor/symfony/framework-bundle/Tests/Templating/Loader/TemplateLocatorTest.php new file mode 100644 index 0000000..96d75e3 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/Loader/TemplateLocatorTest.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\Bundle\FrameworkBundle\Tests\Templating\Loader; + +use Symfony\Bundle\FrameworkBundle\Templating\Loader\TemplateLocator; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TemplateLocatorTest extends TestCase +{ + public function testLocateATemplate() + { + $template = new TemplateReference('bundle', 'controller', 'name', 'format', 'engine'); + + $fileLocator = $this->getFileLocator(); + + $fileLocator + ->expects($this->once()) + ->method('locate') + ->with($template->getPath()) + ->will($this->returnValue('/path/to/template')) + ; + + $locator = new TemplateLocator($fileLocator); + + $this->assertEquals('/path/to/template', $locator->locate($template)); + + // Assert cache is used as $fileLocator->locate should be called only once + $this->assertEquals('/path/to/template', $locator->locate($template)); + } + + public function testLocateATemplateFromCacheDir() + { + $template = new TemplateReference('bundle', 'controller', 'name', 'format', 'engine'); + + $fileLocator = $this->getFileLocator(); + + $locator = new TemplateLocator($fileLocator, __DIR__.'/../../Fixtures'); + + $this->assertEquals(realpath(__DIR__.'/../../Fixtures/Resources/views/this.is.a.template.format.engine'), $locator->locate($template)); + } + + public function testThrowsExceptionWhenTemplateNotFound() + { + $template = new TemplateReference('bundle', 'controller', 'name', 'format', 'engine'); + + $fileLocator = $this->getFileLocator(); + + $errorMessage = 'FileLocator exception message'; + + $fileLocator + ->expects($this->once()) + ->method('locate') + ->will($this->throwException(new \InvalidArgumentException($errorMessage))) + ; + + $locator = new TemplateLocator($fileLocator); + + try { + $locator->locate($template); + $this->fail('->locate() should throw an exception when the file is not found.'); + } catch (\InvalidArgumentException $e) { + $this->assertContains( + $errorMessage, + $e->getMessage(), + 'TemplateLocator exception should propagate the FileLocator exception message' + ); + } + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testThrowsAnExceptionWhenTemplateIsNotATemplateReferenceInterface() + { + $locator = new TemplateLocator($this->getFileLocator()); + $locator->locate('template'); + } + + protected function getFileLocator() + { + return $this + ->getMockBuilder('Symfony\Component\Config\FileLocator') + ->setMethods(array('locate')) + ->setConstructorArgs(array('/path/to/fallback')) + ->getMock() + ; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/PhpEngineTest.php b/vendor/symfony/framework-bundle/Tests/Templating/PhpEngineTest.php new file mode 100644 index 0000000..9628828 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/PhpEngineTest.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\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Bundle\FrameworkBundle\Templating\PhpEngine; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\Templating\TemplateNameParser; + +class PhpEngineTest extends TestCase +{ + public function testEvaluateAddsAppGlobal() + { + $container = $this->getContainer(); + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $engine = new PhpEngine(new TemplateNameParser(), $container, $loader, $app = new GlobalVariables($container)); + $globals = $engine->getGlobals(); + $this->assertSame($app, $globals['app']); + } + + public function testEvaluateWithoutAvailableRequest() + { + $container = new Container(); + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $engine = new PhpEngine(new TemplateNameParser(), $container, $loader, new GlobalVariables($container)); + + $this->assertFalse($container->has('request_stack')); + $globals = $engine->getGlobals(); + $this->assertEmpty($globals['app']->getRequest()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetInvalidHelper() + { + $container = $this->getContainer(); + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $engine = new PhpEngine(new TemplateNameParser(), $container, $loader); + + $engine->get('non-existing-helper'); + } + + /** + * Creates a Container with a Session-containing Request service. + * + * @return Container + */ + protected function getContainer() + { + $container = new Container(); + $session = new Session(new MockArraySessionStorage()); + $request = new Request(); + $stack = new RequestStack(); + $stack->push($request); + + $request->setSession($session); + $container->set('request_stack', $stack); + + return $container; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/TemplateFilenameParserTest.php b/vendor/symfony/framework-bundle/Tests/Templating/TemplateFilenameParserTest.php new file mode 100644 index 0000000..eedbcd2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/TemplateFilenameParserTest.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\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateFilenameParser; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TemplateFilenameParserTest extends TestCase +{ + protected $parser; + + protected function setUp() + { + $this->parser = new TemplateFilenameParser(); + } + + protected function tearDown() + { + $this->parser = null; + } + + /** + * @dataProvider getFilenameToTemplateProvider + */ + public function testParseFromFilename($file, $ref) + { + $template = $this->parser->parse($file); + + if (false === $ref) { + $this->assertFalse($template); + } else { + $this->assertEquals($template->getLogicalName(), $ref->getLogicalName()); + } + } + + public function getFilenameToTemplateProvider() + { + return array( + array('/path/to/section/name.format.engine', new TemplateReference('', '/path/to/section', 'name', 'format', 'engine')), + array('\\path\\to\\section\\name.format.engine', new TemplateReference('', '/path/to/section', 'name', 'format', 'engine')), + array('name.format.engine', new TemplateReference('', '', 'name', 'format', 'engine')), + array('name.format', false), + array('name', false), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/TemplateNameParserTest.php b/vendor/symfony/framework-bundle/Tests/Templating/TemplateNameParserTest.php new file mode 100644 index 0000000..e5958e2 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/TemplateNameParserTest.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\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser; +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\Templating\TemplateReference as BaseTemplateReference; + +class TemplateNameParserTest extends TestCase +{ + protected $parser; + + protected function setUp() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\KernelInterface')->getMock(); + $kernel + ->expects($this->any()) + ->method('getBundle') + ->will($this->returnCallback(function ($bundle) { + if (\in_array($bundle, array('SensioFooBundle', 'SensioCmsFooBundle', 'FooBundle'))) { + return true; + } + + throw new \InvalidArgumentException(); + })) + ; + $this->parser = new TemplateNameParser($kernel); + } + + protected function tearDown() + { + $this->parser = null; + } + + /** + * @dataProvider parseProvider + */ + public function testParse($name, $logicalName, $path, $ref) + { + $template = $this->parser->parse($name); + + $this->assertSame($ref->getLogicalName(), $template->getLogicalName()); + $this->assertSame($logicalName, $template->getLogicalName()); + $this->assertSame($path, $template->getPath()); + } + + public function parseProvider() + { + return array( + array('FooBundle:Post:index.html.php', 'FooBundle:Post:index.html.php', '@FooBundle/Resources/views/Post/index.html.php', new TemplateReference('FooBundle', 'Post', 'index', 'html', 'php')), + array('FooBundle:Post:index.html.twig', 'FooBundle:Post:index.html.twig', '@FooBundle/Resources/views/Post/index.html.twig', new TemplateReference('FooBundle', 'Post', 'index', 'html', 'twig')), + array('FooBundle:Post:index.xml.php', 'FooBundle:Post:index.xml.php', '@FooBundle/Resources/views/Post/index.xml.php', new TemplateReference('FooBundle', 'Post', 'index', 'xml', 'php')), + array('SensioFooBundle:Post:index.html.php', 'SensioFooBundle:Post:index.html.php', '@SensioFooBundle/Resources/views/Post/index.html.php', new TemplateReference('SensioFooBundle', 'Post', 'index', 'html', 'php')), + array('SensioCmsFooBundle:Post:index.html.php', 'SensioCmsFooBundle:Post:index.html.php', '@SensioCmsFooBundle/Resources/views/Post/index.html.php', new TemplateReference('SensioCmsFooBundle', 'Post', 'index', 'html', 'php')), + array(':Post:index.html.php', ':Post:index.html.php', 'views/Post/index.html.php', new TemplateReference('', 'Post', 'index', 'html', 'php')), + array('::index.html.php', '::index.html.php', 'views/index.html.php', new TemplateReference('', '', 'index', 'html', 'php')), + array('index.html.php', '::index.html.php', 'views/index.html.php', new TemplateReference('', '', 'index', 'html', 'php')), + array('FooBundle:Post:foo.bar.index.html.php', 'FooBundle:Post:foo.bar.index.html.php', '@FooBundle/Resources/views/Post/foo.bar.index.html.php', new TemplateReference('FooBundle', 'Post', 'foo.bar.index', 'html', 'php')), + array('@FooBundle/Resources/views/layout.html.twig', '@FooBundle/Resources/views/layout.html.twig', '@FooBundle/Resources/views/layout.html.twig', new BaseTemplateReference('@FooBundle/Resources/views/layout.html.twig', 'twig')), + array('@FooBundle/Foo/layout.html.twig', '@FooBundle/Foo/layout.html.twig', '@FooBundle/Foo/layout.html.twig', new BaseTemplateReference('@FooBundle/Foo/layout.html.twig', 'twig')), + array('name.twig', 'name.twig', 'name.twig', new BaseTemplateReference('name.twig', 'twig')), + array('name', 'name', 'name', new BaseTemplateReference('name')), + array('default/index.html.php', '::default/index.html.php', 'views/default/index.html.php', new TemplateReference(null, null, 'default/index', 'html', 'php')), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testParseValidNameWithNotFoundBundle() + { + $this->parser->parse('BarBundle:Post:index.html.php'); + } + + /** + * @group legacy + * @dataProvider provideAbsolutePaths + * @expectedDeprecation Absolute template path support is deprecated since Symfony 3.1 and will be removed in 4.0. + */ + public function testAbsolutePathsAreDeprecated($name, $logicalName, $path, $ref) + { + $template = $this->parser->parse($name); + + $this->assertSame($ref->getLogicalName(), $template->getLogicalName()); + $this->assertSame($logicalName, $template->getLogicalName()); + $this->assertSame($path, $template->getPath()); + } + + public function provideAbsolutePaths() + { + return array( + array('/path/to/section/index.html.php', '/path/to/section/index.html.php', '/path/to/section/index.html.php', new BaseTemplateReference('/path/to/section/index.html.php', 'php')), + array('C:\\path\\to\\section\\name.html.php', 'C:path/to/section/name.html.php', 'C:path/to/section/name.html.php', new BaseTemplateReference('C:path/to/section/name.html.php', 'php')), + array('C:\\path\\to\\section\\name:foo.html.php', 'C:path/to/section/name:foo.html.php', 'C:path/to/section/name:foo.html.php', new BaseTemplateReference('C:path/to/section/name:foo.html.php', 'php')), + array('\\path\\to\\section\\name.html.php', '/path/to/section/name.html.php', '/path/to/section/name.html.php', new BaseTemplateReference('/path/to/section/name.html.php', 'php')), + array('/path/to/section/name.php', '/path/to/section/name.php', '/path/to/section/name.php', new BaseTemplateReference('/path/to/section/name.php', 'php')), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/TemplateReferenceTest.php b/vendor/symfony/framework-bundle/Tests/Templating/TemplateReferenceTest.php new file mode 100644 index 0000000..35561bf --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/TemplateReferenceTest.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\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TemplateReferenceTest extends TestCase +{ + public function testGetPathWorksWithNamespacedControllers() + { + $reference = new TemplateReference('AcmeBlogBundle', 'Admin\Post', 'index', 'html', 'twig'); + + $this->assertSame( + '@AcmeBlogBundle/Resources/views/Admin/Post/index.html.twig', + $reference->getPath() + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/TemplateTest.php b/vendor/symfony/framework-bundle/Tests/Templating/TemplateTest.php new file mode 100644 index 0000000..0321d05 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/TemplateTest.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\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; + +class TemplateTest extends TestCase +{ + /** + * @dataProvider getTemplateToPathProvider + */ + public function testGetPathForTemplate($template, $path) + { + $this->assertSame($template->getPath(), $path); + } + + public function getTemplateToPathProvider() + { + return array( + array(new TemplateReference('FooBundle', 'Post', 'index', 'html', 'php'), '@FooBundle/Resources/views/Post/index.html.php'), + array(new TemplateReference('FooBundle', '', 'index', 'html', 'twig'), '@FooBundle/Resources/views/index.html.twig'), + array(new TemplateReference('', 'Post', 'index', 'html', 'php'), 'views/Post/index.html.php'), + array(new TemplateReference('', '', 'index', 'html', 'php'), 'views/index.html.php'), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Templating/TimedPhpEngineTest.php b/vendor/symfony/framework-bundle/Tests/Templating/TimedPhpEngineTest.php new file mode 100644 index 0000000..bd02809 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Templating/TimedPhpEngineTest.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Templating; + +use Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables; +use Symfony\Bundle\FrameworkBundle\Templating\TimedPhpEngine; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; + +class TimedPhpEngineTest extends TestCase +{ + public function testThatRenderLogsTime() + { + $container = $this->getContainer(); + $templateNameParser = $this->getTemplateNameParser(); + $globalVariables = $this->getGlobalVariables(); + $loader = $this->getLoader($this->getStorage()); + + $stopwatch = $this->getStopwatch(); + $stopwatchEvent = $this->getStopwatchEvent(); + + $stopwatch->expects($this->once()) + ->method('start') + ->with('template.php (index.php)', 'template') + ->will($this->returnValue($stopwatchEvent)); + + $stopwatchEvent->expects($this->once())->method('stop'); + + $engine = new TimedPhpEngine($templateNameParser, $container, $loader, $stopwatch, $globalVariables); + $engine->render('index.php'); + } + + /** + * @return Container + */ + private function getContainer() + { + return $this->getMockBuilder('Symfony\Component\DependencyInjection\Container')->getMock(); + } + + /** + * @return \Symfony\Component\Templating\TemplateNameParserInterface + */ + private function getTemplateNameParser() + { + $templateReference = $this->getMockBuilder('Symfony\Component\Templating\TemplateReferenceInterface')->getMock(); + $templateNameParser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock(); + $templateNameParser->expects($this->any()) + ->method('parse') + ->will($this->returnValue($templateReference)); + + return $templateNameParser; + } + + /** + * @return GlobalVariables + */ + private function getGlobalVariables() + { + return $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\GlobalVariables') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return \Symfony\Component\Templating\Storage\StringStorage + */ + private function getStorage() + { + return $this->getMockBuilder('Symfony\Component\Templating\Storage\StringStorage') + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + } + + /** + * @param \Symfony\Component\Templating\Storage\StringStorage $storage + * + * @return \Symfony\Component\Templating\Loader\Loader + */ + private function getLoader($storage) + { + $loader = $this->getMockForAbstractClass('Symfony\Component\Templating\Loader\Loader'); + $loader->expects($this->once()) + ->method('load') + ->will($this->returnValue($storage)); + + return $loader; + } + + /** + * @return \Symfony\Component\Stopwatch\StopwatchEvent + */ + private function getStopwatchEvent() + { + return $this->getMockBuilder('Symfony\Component\Stopwatch\StopwatchEvent') + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * @return \Symfony\Component\Stopwatch\Stopwatch + */ + private function getStopwatch() + { + return $this->getMockBuilder('Symfony\Component\Stopwatch\Stopwatch')->getMock(); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/TestCase.php b/vendor/symfony/framework-bundle/Tests/TestCase.php new file mode 100644 index 0000000..115ca1a --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/TestCase.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\Bundle\FrameworkBundle\Tests; + +use PHPUnit\Framework\TestCase as PHPUnitTestCase; + +class TestCase extends PHPUnitTestCase +{ +} diff --git a/vendor/symfony/framework-bundle/Tests/Translation/PhpExtractorTest.php b/vendor/symfony/framework-bundle/Tests/Translation/PhpExtractorTest.php new file mode 100644 index 0000000..ca45f43 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Translation/PhpExtractorTest.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\Bundle\FrameworkBundle\Tests\Translation; + +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Bundle\FrameworkBundle\Translation\PhpExtractor; +use Symfony\Component\Translation\MessageCatalogue; + +/** + * @group legacy + */ +class PhpExtractorTest extends TestCase +{ + /** + * @dataProvider resourcesProvider + * + * @param array|string $resource + */ + public function testExtraction($resource) + { + // Arrange + $extractor = new PhpExtractor(); + $extractor->setPrefix('prefix'); + $catalogue = new MessageCatalogue('en'); + + // Act + $extractor->extract($resource, $catalogue); + + $expectedHeredoc = << array( + 'single-quoted key' => 'prefixsingle-quoted key', + 'double-quoted key' => 'prefixdouble-quoted key', + 'heredoc key' => 'prefixheredoc key', + 'nowdoc key' => 'prefixnowdoc key', + "double-quoted key with whitespace and escaped \$\n\" sequences" => "prefixdouble-quoted key with whitespace and escaped \$\n\" sequences", + 'single-quoted key with whitespace and nonescaped \$\n\' sequences' => 'prefixsingle-quoted key with whitespace and nonescaped \$\n\' sequences', + 'single-quoted key with "quote mark at the end"' => 'prefixsingle-quoted key with "quote mark at the end"', + $expectedHeredoc => 'prefix'.$expectedHeredoc, + $expectedNowdoc => 'prefix'.$expectedNowdoc, + '{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples' => 'prefix{0} There is no apples|{1} There is one apple|]1,Inf[ There are %count% apples', + ), + 'not_messages' => array( + 'other-domain-test-no-params-short-array' => 'prefixother-domain-test-no-params-short-array', + 'other-domain-test-no-params-long-array' => 'prefixother-domain-test-no-params-long-array', + 'other-domain-test-params-short-array' => 'prefixother-domain-test-params-short-array', + 'other-domain-test-params-long-array' => 'prefixother-domain-test-params-long-array', + 'other-domain-test-trans-choice-short-array-%count%' => 'prefixother-domain-test-trans-choice-short-array-%count%', + 'other-domain-test-trans-choice-long-array-%count%' => 'prefixother-domain-test-trans-choice-long-array-%count%', + 'typecast' => 'prefixtypecast', + 'msg1' => 'prefixmsg1', + 'msg2' => 'prefixmsg2', + ), + ); + $actualCatalogue = $catalogue->all(); + + $this->assertEquals($expectedCatalogue, $actualCatalogue); + } + + public function resourcesProvider() + { + $directory = __DIR__.'/../Fixtures/Resources/views/'; + $splFiles = array(); + foreach (new \DirectoryIterator($directory) as $fileInfo) { + if ($fileInfo->isDot()) { + continue; + } + if ('translation.html.php' === $fileInfo->getBasename()) { + $phpFile = $fileInfo->getPathname(); + } + $splFiles[] = $fileInfo->getFileInfo(); + } + + return array( + array($directory), + array($phpFile), + array(glob($directory.'*')), + array($splFiles), + array(new \ArrayObject(glob($directory.'*'))), + array(new \ArrayObject($splFiles)), + ); + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Translation/TranslatorTest.php b/vendor/symfony/framework-bundle/Tests/Translation/TranslatorTest.php new file mode 100644 index 0000000..08b4407 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Translation/TranslatorTest.php @@ -0,0 +1,496 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Translation; + +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\Translation\Translator; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Translation\Formatter\MessageFormatter; +use Symfony\Component\Translation\MessageCatalogue; + +class TranslatorTest extends TestCase +{ + protected $tmpDir; + + protected function setUp() + { + $this->tmpDir = sys_get_temp_dir().'/sf2_translation'; + $this->deleteTmpDir(); + } + + protected function tearDown() + { + $this->deleteTmpDir(); + } + + protected function deleteTmpDir() + { + if (!file_exists($dir = $this->tmpDir)) { + return; + } + + $fs = new Filesystem(); + $fs->remove($dir); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. + */ + public function testTransWithoutCachingOmittingLocale() + { + $translator = $this->getTranslator($this->getLoader(), array(), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. + */ + public function testTransWithCachingOmittingLocale() + { + // prime the cache + $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + + // do it another time as the cache is primed now + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader->expects($this->never())->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. + * @expectedException \InvalidArgumentException + */ + public function testTransWithCachingWithInvalidLocaleOmittingLocale() + { + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Tests\Translation\TranslatorWithInvalidLocale', null); + + $translator->trans('foo'); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. + */ + public function testLoadResourcesWithoutCachingOmittingLocale() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = array( + 'fr' => array( + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ), + ); + + $translator = $this->getTranslator($loader, array('resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); + $translator->setLocale('fr'); + + $this->assertEquals('répertoire', $translator->trans('folder')); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. + */ + public function testGetDefaultLocaleOmittingLocale() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container + ->expects($this->once()) + ->method('getParameter') + ->with('kernel.default_locale') + ->will($this->returnValue('en')) + ; + $translator = new Translator($container, new MessageFormatter()); + + $this->assertSame('en', $translator->getLocale()); + } + + /** + * @group legacy + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Missing third $defaultLocale argument. + */ + public function testGetDefaultLocaleOmittingLocaleWithPsrContainer() + { + $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); + $translator = new Translator($container, new MessageFormatter()); + } + + /** + * @group legacy + * @expectedDeprecation The "Symfony\Bundle\FrameworkBundle\Translation\Translator::__construct()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0. + */ + public function testWarmupOmittingLocale() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = array( + 'fr' => array( + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ), + ); + + // prime the cache + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); + $translator->setFallbackLocales(array('fr')); + $translator->warmup($this->tmpDir); + + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader + ->expects($this->never()) + ->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml', '\Symfony\Bundle\FrameworkBundle\Translation\Translator', null); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('fr')); + $this->assertEquals('répertoire', $translator->trans('folder')); + } + + public function testTransWithoutCaching() + { + $translator = $this->getTranslator($this->getLoader()); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + } + + public function testTransWithCaching() + { + // prime the cache + $translator = $this->getTranslator($this->getLoader(), array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + + // do it another time as the cache is primed now + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader->expects($this->never())->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir)); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('en', 'es', 'pt-PT', 'pt_BR', 'fr.UTF-8', 'sr@latin')); + + $this->assertEquals('foo (FR)', $translator->trans('foo')); + $this->assertEquals('bar (EN)', $translator->trans('bar')); + $this->assertEquals('foobar (ES)', $translator->trans('foobar')); + $this->assertEquals('choice 0 (EN)', $translator->transChoice('choice', 0)); + $this->assertEquals('no translation', $translator->trans('no translation')); + $this->assertEquals('foobarfoo (PT-PT)', $translator->trans('foobarfoo')); + $this->assertEquals('other choice 1 (PT-BR)', $translator->transChoice('other choice', 1)); + $this->assertEquals('foobarbaz (fr.UTF-8)', $translator->trans('foobarbaz')); + $this->assertEquals('foobarbax (sr@latin)', $translator->trans('foobarbax')); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Invalid "invalid locale" locale. + */ + public function testTransWithCachingWithInvalidLocale() + { + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir), 'loader', '\Symfony\Bundle\FrameworkBundle\Tests\Translation\TranslatorWithInvalidLocale'); + + $translator->trans('foo'); + } + + public function testLoadResourcesWithoutCaching() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = array( + 'fr' => array( + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ), + ); + + $translator = $this->getTranslator($loader, array('resource_files' => $resourceFiles), 'yml'); + $translator->setLocale('fr'); + + $this->assertEquals('répertoire', $translator->trans('folder')); + } + + public function testGetDefaultLocale() + { + $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); + $translator = new Translator($container, new MessageFormatter(), 'en'); + + $this->assertSame('en', $translator->getLocale()); + } + + /** + * @expectedException \Symfony\Component\Translation\Exception\InvalidArgumentException + * @expectedExceptionMessage The Translator does not support the following options: 'foo' + */ + public function testInvalidOptions() + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + + (new Translator($container, new MessageFormatter(), 'en', array(), array('foo' => 'bar'))); + } + + /** @dataProvider getDebugModeAndCacheDirCombinations */ + public function testResourceFilesOptionLoadsBeforeOtherAddedResources($debug, $enableCache) + { + $someCatalogue = $this->getCatalogue('some_locale', array()); + + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + + $loader->expects($this->at(0)) + ->method('load') + /* The "messages.some_locale.loader" is passed via the resource_file option and shall be loaded first */ + ->with('messages.some_locale.loader', 'some_locale', 'messages') + ->willReturn($someCatalogue); + + $loader->expects($this->at(1)) + ->method('load') + /* This resource is added by an addResource() call and shall be loaded after the resource_files */ + ->with('second_resource.some_locale.loader', 'some_locale', 'messages') + ->willReturn($someCatalogue); + + $options = array( + 'resource_files' => array('some_locale' => array('messages.some_locale.loader')), + 'debug' => $debug, + ); + + if ($enableCache) { + $options['cache_dir'] = $this->tmpDir; + } + + /** @var Translator $translator */ + $translator = $this->createTranslator($loader, $options); + $translator->addResource('loader', 'second_resource.some_locale.loader', 'some_locale', 'messages'); + + $translator->trans('some_message', array(), null, 'some_locale'); + } + + public function getDebugModeAndCacheDirCombinations() + { + return array( + array(false, false), + array(true, false), + array(false, true), + array(true, true), + ); + } + + protected function getCatalogue($locale, $messages, $resources = array()) + { + $catalogue = new MessageCatalogue($locale); + foreach ($messages as $key => $translation) { + $catalogue->set($key, $translation); + } + foreach ($resources as $resource) { + $catalogue->addResource($resource); + } + + return $catalogue; + } + + protected function getLoader() + { + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader + ->expects($this->at(0)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('fr', array( + 'foo' => 'foo (FR)', + )))) + ; + $loader + ->expects($this->at(1)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('en', array( + 'foo' => 'foo (EN)', + 'bar' => 'bar (EN)', + 'choice' => '{0} choice 0 (EN)|{1} choice 1 (EN)|]1,Inf] choice inf (EN)', + )))) + ; + $loader + ->expects($this->at(2)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('es', array( + 'foobar' => 'foobar (ES)', + )))) + ; + $loader + ->expects($this->at(3)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('pt-PT', array( + 'foobarfoo' => 'foobarfoo (PT-PT)', + )))) + ; + $loader + ->expects($this->at(4)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('pt_BR', array( + 'other choice' => '{0} other choice 0 (PT-BR)|{1} other choice 1 (PT-BR)|]1,Inf] other choice inf (PT-BR)', + )))) + ; + $loader + ->expects($this->at(5)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('fr.UTF-8', array( + 'foobarbaz' => 'foobarbaz (fr.UTF-8)', + )))) + ; + $loader + ->expects($this->at(6)) + ->method('load') + ->will($this->returnValue($this->getCatalogue('sr@latin', array( + 'foobarbax' => 'foobarbax (sr@latin)', + )))) + ; + + return $loader; + } + + protected function getContainer($loader) + { + $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); + $container + ->expects($this->any()) + ->method('get') + ->will($this->returnValue($loader)) + ; + + return $container; + } + + public function getTranslator($loader, $options = array(), $loaderFomat = 'loader', $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $defaultLocale = 'en') + { + $translator = $this->createTranslator($loader, $options, $translatorClass, $loaderFomat, $defaultLocale); + + if ('loader' === $loaderFomat) { + $translator->addResource('loader', 'foo', 'fr'); + $translator->addResource('loader', 'foo', 'en'); + $translator->addResource('loader', 'foo', 'es'); + $translator->addResource('loader', 'foo', 'pt-PT'); // European Portuguese + $translator->addResource('loader', 'foo', 'pt_BR'); // Brazilian Portuguese + $translator->addResource('loader', 'foo', 'fr.UTF-8'); + $translator->addResource('loader', 'foo', 'sr@latin'); // Latin Serbian + } + + return $translator; + } + + public function testWarmup() + { + $loader = new \Symfony\Component\Translation\Loader\YamlFileLoader(); + $resourceFiles = array( + 'fr' => array( + __DIR__.'/../Fixtures/Resources/translations/messages.fr.yml', + ), + ); + + // prime the cache + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml'); + $translator->setFallbackLocales(array('fr')); + $translator->warmup($this->tmpDir); + + $loader = $this->getMockBuilder('Symfony\Component\Translation\Loader\LoaderInterface')->getMock(); + $loader + ->expects($this->never()) + ->method('load'); + + $translator = $this->getTranslator($loader, array('cache_dir' => $this->tmpDir, 'resource_files' => $resourceFiles), 'yml'); + $translator->setLocale('fr'); + $translator->setFallbackLocales(array('fr')); + $this->assertEquals('répertoire', $translator->trans('folder')); + } + + private function createTranslator($loader, $options, $translatorClass = '\Symfony\Bundle\FrameworkBundle\Translation\Translator', $loaderFomat = 'loader', $defaultLocale = 'en') + { + if (null === $defaultLocale) { + return new $translatorClass( + $this->getContainer($loader), + new MessageFormatter(), + array($loaderFomat => array($loaderFomat)), + $options + ); + } + + return new $translatorClass( + $this->getContainer($loader), + new MessageFormatter(), + $defaultLocale, + array($loaderFomat => array($loaderFomat)), + $options + ); + } +} + +class TranslatorWithInvalidLocale extends Translator +{ + /** + * {@inheritdoc} + */ + public function getLocale() + { + return 'invalid locale'; + } +} diff --git a/vendor/symfony/framework-bundle/Tests/Validator/ConstraintValidatorFactoryTest.php b/vendor/symfony/framework-bundle/Tests/Validator/ConstraintValidatorFactoryTest.php new file mode 100644 index 0000000..f82c3f4 --- /dev/null +++ b/vendor/symfony/framework-bundle/Tests/Validator/ConstraintValidatorFactoryTest.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\Bundle\FrameworkBundle\Tests\Validator; + +use PHPUnit\Framework\TestCase; +use Symfony\Bundle\FrameworkBundle\Validator\ConstraintValidatorFactory; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Blank as BlankConstraint; +use Symfony\Component\Validator\ConstraintValidator; + +/** + * @group legacy + */ +class ConstraintValidatorFactoryTest extends TestCase +{ + public function testGetInstanceCreatesValidator() + { + $factory = new ConstraintValidatorFactory(new Container()); + $this->assertInstanceOf(DummyConstraintValidator::class, $factory->getInstance(new DummyConstraint())); + } + + public function testGetInstanceReturnsExistingValidator() + { + $factory = new ConstraintValidatorFactory(new Container()); + $v1 = $factory->getInstance(new BlankConstraint()); + $v2 = $factory->getInstance(new BlankConstraint()); + $this->assertSame($v1, $v2); + } + + public function testGetInstanceReturnsService() + { + $validator = new DummyConstraintValidator(); + $container = new Container(); + $container->set(DummyConstraintValidator::class, $validator); + + $factory = new ConstraintValidatorFactory($container); + + $this->assertSame($validator, $factory->getInstance(new DummyConstraint())); + } + + public function testGetInstanceReturnsServiceWithAlias() + { + $validator = new DummyConstraintValidator(); + + $container = new Container(); + $container->set('validator_constraint_service', $validator); + + $factory = new ConstraintValidatorFactory($container, array('validator_constraint_alias' => 'validator_constraint_service')); + $this->assertSame($validator, $factory->getInstance(new ConstraintAliasStub())); + } + + /** + * @expectedException \Symfony\Component\Validator\Exception\ValidatorException + */ + public function testGetInstanceInvalidValidatorClass() + { + $constraint = $this->getMockBuilder('Symfony\\Component\\Validator\\Constraint')->getMock(); + $constraint + ->expects($this->exactly(2)) + ->method('validatedBy') + ->will($this->returnValue('Fully\\Qualified\\ConstraintValidator\\Class\\Name')); + + $factory = new ConstraintValidatorFactory(new Container()); + $factory->getInstance($constraint); + } +} + +class ConstraintAliasStub extends Constraint +{ + public function validatedBy() + { + return 'validator_constraint_alias'; + } +} + +class DummyConstraint extends Constraint +{ + public function validatedBy() + { + return DummyConstraintValidator::class; + } +} + +class DummyConstraintValidator extends ConstraintValidator +{ + public function validate($value, Constraint $constraint) + { + } +} diff --git a/vendor/symfony/framework-bundle/Translation/PhpExtractor.php b/vendor/symfony/framework-bundle/Translation/PhpExtractor.php new file mode 100644 index 0000000..35bda9d --- /dev/null +++ b/vendor/symfony/framework-bundle/Translation/PhpExtractor.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\Bundle\FrameworkBundle\Translation; + +use Symfony\Component\Translation\Extractor\PhpExtractor as NewPhpExtractor; + +@trigger_error(sprintf('The class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use "%s" instead. ', PhpExtractor::class, NewPhpExtractor::class), E_USER_DEPRECATED); + +/** + * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Extractor\PhpExtractor instead + */ +class PhpExtractor extends NewPhpExtractor +{ +} diff --git a/vendor/symfony/framework-bundle/Translation/PhpStringTokenParser.php b/vendor/symfony/framework-bundle/Translation/PhpStringTokenParser.php new file mode 100644 index 0000000..02fd4b5 --- /dev/null +++ b/vendor/symfony/framework-bundle/Translation/PhpStringTokenParser.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\Bundle\FrameworkBundle\Translation; + +@trigger_error(sprintf('The class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use "%s" instead. ', PhpStringTokenParser::class, \Symfony\Component\Translation\Extractor\PhpStringTokenParser::class), E_USER_DEPRECATED); + +/** + * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Extractor\PhpStringTokenParser instead + */ +class PhpStringTokenParser extends \Symfony\Component\Translation\Extractor\PhpStringTokenParser +{ +} diff --git a/vendor/symfony/framework-bundle/Translation/TranslationLoader.php b/vendor/symfony/framework-bundle/Translation/TranslationLoader.php new file mode 100644 index 0000000..1fe0197 --- /dev/null +++ b/vendor/symfony/framework-bundle/Translation/TranslationLoader.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\Bundle\FrameworkBundle\Translation; + +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\Reader\TranslationReader; + +/** + * @deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\Translation\Reader\TranslationReader instead + */ +class TranslationLoader extends TranslationReader +{ + public function __construct() + { + @trigger_error(sprintf('The class "%s" is deprecated since Symfony 3.4 and will be removed in 4.0. Use "%s" instead. ', self::class, TranslationReader::class), E_USER_DEPRECATED); + } + + /** + * Loads translation messages from a directory to the catalogue. + * + * @param string $directory The directory to look into + * @param MessageCatalogue $catalogue The catalogue + */ + public function loadMessages($directory, MessageCatalogue $catalogue) + { + $this->read($directory, $catalogue); + } +} diff --git a/vendor/symfony/framework-bundle/Translation/Translator.php b/vendor/symfony/framework-bundle/Translation/Translator.php new file mode 100644 index 0000000..a1438f6 --- /dev/null +++ b/vendor/symfony/framework-bundle/Translation/Translator.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\Bundle\FrameworkBundle\Translation; + +use Psr\Container\ContainerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface as SymfonyContainerInterface; +use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; +use Symfony\Component\Translation\Exception\InvalidArgumentException; +use Symfony\Component\Translation\Formatter\MessageFormatterInterface; +use Symfony\Component\Translation\Translator as BaseTranslator; + +/** + * Translator. + * + * @author Fabien Potencier + */ +class Translator extends BaseTranslator implements WarmableInterface +{ + protected $container; + protected $loaderIds; + + protected $options = array( + 'cache_dir' => null, + 'debug' => false, + 'resource_files' => array(), + ); + + /** + * @var array + */ + private $resourceLocales; + + /** + * Holds parameters from addResource() calls so we can defer the actual + * parent::addResource() calls until initialize() is executed. + * + * @var array + */ + private $resources = array(); + + /** + * Constructor. + * + * Available options: + * + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * * resource_files: List of translation resources available grouped by locale. + * + * @param ContainerInterface $container A ContainerInterface instance + * @param MessageFormatterInterface $formatter The message formatter + * @param string $defaultLocale + * @param array $loaderIds An array of loader Ids + * @param array $options An array of options + * + * @throws InvalidArgumentException + */ + public function __construct(ContainerInterface $container, $formatter, $defaultLocale = null, array $loaderIds = array(), array $options = array()) + { + // BC 3.x, to be removed in 4.0 along with the $defaultLocale default value + if (\is_array($defaultLocale) || 3 > \func_num_args()) { + if (!$container instanceof SymfonyContainerInterface) { + throw new \InvalidArgumentException('Missing third $defaultLocale argument.'); + } + + $options = $loaderIds; + $loaderIds = $defaultLocale; + $defaultLocale = $container->getParameter('kernel.default_locale'); + @trigger_error(sprintf('The "%s()" method takes the default locale as the 3rd argument since Symfony 3.3. Not passing it is deprecated and will trigger an error in 4.0.', __METHOD__), E_USER_DEPRECATED); + } + + $this->container = $container; + $this->loaderIds = $loaderIds; + + // check option names + if ($diff = array_diff(array_keys($options), array_keys($this->options))) { + throw new InvalidArgumentException(sprintf('The Translator does not support the following options: \'%s\'.', implode('\', \'', $diff))); + } + + $this->options = array_merge($this->options, $options); + $this->resourceLocales = array_keys($this->options['resource_files']); + $this->addResourceFiles($this->options['resource_files']); + + parent::__construct($defaultLocale, $formatter, $this->options['cache_dir'], $this->options['debug']); + } + + /** + * {@inheritdoc} + */ + public function warmUp($cacheDir) + { + // skip warmUp when translator doesn't use cache + if (null === $this->options['cache_dir']) { + return; + } + + $locales = array_merge($this->getFallbackLocales(), array($this->getLocale()), $this->resourceLocales); + foreach (array_unique($locales) as $locale) { + // reset catalogue in case it's already loaded during the dump of the other locales. + if (isset($this->catalogues[$locale])) { + unset($this->catalogues[$locale]); + } + + $this->loadCatalogue($locale); + } + } + + public function addResource($format, $resource, $locale, $domain = null) + { + $this->resources[] = array($format, $resource, $locale, $domain); + } + + /** + * {@inheritdoc} + */ + protected function initializeCatalogue($locale) + { + $this->initialize(); + parent::initializeCatalogue($locale); + } + + protected function initialize() + { + foreach ($this->resources as $key => $params) { + list($format, $resource, $locale, $domain) = $params; + parent::addResource($format, $resource, $locale, $domain); + } + $this->resources = array(); + + foreach ($this->loaderIds as $id => $aliases) { + foreach ($aliases as $alias) { + $this->addLoader($alias, $this->container->get($id)); + } + } + } + + private function addResourceFiles($filesByLocale) + { + foreach ($filesByLocale as $locale => $files) { + foreach ($files as $key => $file) { + // filename is domain.locale.format + list($domain, $locale, $format) = explode('.', basename($file), 3); + $this->addResource($format, $file, $locale, $domain); + } + } + } +} diff --git a/vendor/symfony/framework-bundle/Validator/ConstraintValidatorFactory.php b/vendor/symfony/framework-bundle/Validator/ConstraintValidatorFactory.php new file mode 100644 index 0000000..78eb6b0 --- /dev/null +++ b/vendor/symfony/framework-bundle/Validator/ConstraintValidatorFactory.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\Bundle\FrameworkBundle\Validator; + +use Psr\Container\ContainerInterface; +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidatorInterface; +use Symfony\Component\Validator\ContainerConstraintValidatorFactory; +use Symfony\Component\Validator\Exception\UnexpectedTypeException; +use Symfony\Component\Validator\Exception\ValidatorException; + +@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0. Use %s instead.', ConstraintValidatorFactory::class, ContainerConstraintValidatorFactory::class), E_USER_DEPRECATED); + +/** + * Uses a service container to create constraint validators. + * + * A constraint validator should be tagged as "validator.constraint_validator" + * in the service container and include an "alias" attribute: + * + * + * + * + * + * + * A constraint may then return this alias in its validatedBy() method: + * + * public function validatedBy() + * { + * return 'some_alias'; + * } + * + * @author Kris Wallsmith + * + * @deprecated since version 3.3 + */ +class ConstraintValidatorFactory extends ContainerConstraintValidatorFactory +{ + protected $container; + protected $validators; + + public function __construct(ContainerInterface $container, array $validators = array()) + { + parent::__construct($container); + + $this->validators = $validators; + $this->container = $container; + } + + /** + * Returns the validator for the supplied constraint. + * + * @return ConstraintValidatorInterface A validator for the supplied constraint + * + * @throws ValidatorException When the validator class does not exist + * @throws UnexpectedTypeException When the validator is not an instance of ConstraintValidatorInterface + */ + public function getInstance(Constraint $constraint) + { + $name = $constraint->validatedBy(); + + if (!isset($this->validators[$name])) { + return parent::getInstance($constraint); + } + + if (\is_string($this->validators[$name])) { + $this->validators[$name] = $this->container->get($this->validators[$name]); + } + + if (!$this->validators[$name] instanceof ConstraintValidatorInterface) { + throw new UnexpectedTypeException($this->validators[$name], 'Symfony\Component\Validator\ConstraintValidatorInterface'); + } + + return $this->validators[$name]; + } +} diff --git a/vendor/symfony/framework-bundle/composer.json b/vendor/symfony/framework-bundle/composer.json new file mode 100644 index 0000000..c1aad68 --- /dev/null +++ b/vendor/symfony/framework-bundle/composer.json @@ -0,0 +1,98 @@ +{ + "name": "symfony/framework-bundle", + "type": "symfony-bundle", + "description": "Symfony FrameworkBundle", + "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", + "ext-xml": "*", + "symfony/cache": "~3.4|~4.0", + "symfony/class-loader": "~3.2", + "symfony/dependency-injection": "^3.4.3|^4.0.3", + "symfony/config": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "^3.3.11|~4.0", + "symfony/http-kernel": "~3.4|~4.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/finder": "~2.8|~3.0|~4.0", + "symfony/routing": "^3.4.5|^4.0.5" + }, + "require-dev": { + "doctrine/cache": "~1.0", + "fig/link-util": "^1.0", + "symfony/asset": "~3.3|~4.0", + "symfony/browser-kit": "~2.8|~3.0|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/dom-crawler": "~2.8|~3.0|~4.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/form": "~3.4|~4.0", + "symfony/expression-language": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0", + "symfony/security-core": "~3.2|~4.0", + "symfony/security-csrf": "^2.8.31|^3.3.13|~4.0", + "symfony/serializer": "~3.3|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/templating": "~2.8|~3.0|~4.0", + "symfony/validator": "~3.4|~4.0", + "symfony/var-dumper": "~3.3|~4.0", + "symfony/workflow": "~3.3|~4.0", + "symfony/yaml": "~3.2|~4.0", + "symfony/property-info": "~3.3|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/web-link": "~3.3|~4.0", + "doctrine/annotations": "~1.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0", + "twig/twig": "~1.34|~2.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/type-resolver": "<0.2.1", + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0", + "symfony/asset": "<3.3", + "symfony/console": "<3.4", + "symfony/form": "<3.4", + "symfony/property-info": "<3.3", + "symfony/serializer": "<3.3", + "symfony/stopwatch": "<3.4", + "symfony/translation": "<3.4", + "symfony/validator": "<3.4", + "symfony/workflow": "<3.3" + }, + "suggest": { + "ext-apcu": "For best performance of the system caches", + "symfony/console": "For using the console commands", + "symfony/form": "For using forms", + "symfony/serializer": "For using the serializer service", + "symfony/validator": "For using validation", + "symfony/yaml": "For using the debug:config and lint:yaml commands", + "symfony/property-info": "For using the property_info service", + "symfony/web-link": "For using web links, features such as preloading, prefetching or prerendering" + }, + "autoload": { + "psr-4": { "Symfony\\Bundle\\FrameworkBundle\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/vendor/symfony/framework-bundle/phpunit.xml.dist b/vendor/symfony/framework-bundle/phpunit.xml.dist new file mode 100644 index 0000000..b447e1a --- /dev/null +++ b/vendor/symfony/framework-bundle/phpunit.xml.dist @@ -0,0 +1,33 @@ + + + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/http-foundation/.gitignore b/vendor/symfony/http-foundation/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/http-foundation/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/http-foundation/AcceptHeader.php b/vendor/symfony/http-foundation/AcceptHeader.php new file mode 100644 index 0000000..c702371 --- /dev/null +++ b/vendor/symfony/http-foundation/AcceptHeader.php @@ -0,0 +1,173 @@ + + * + * 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; + + $parts = HeaderUtils::split((string) $headerValue, ',;='); + + return new self(array_map(function ($subParts) use (&$index) { + $part = array_shift($subParts); + $attributes = HeaderUtils::combine($subParts); + + $item = new AcceptHeaderItem($part[0], $attributes); + $item->setIndex($index++); + + return $item; + }, $parts)); + } + + /** + * 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 $this->items[$value] ?? $this->items[explode('/', $value)[0].'/*'] ?? $this->items['*/*'] ?? $this->items['*'] ?? 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/vendor/symfony/http-foundation/AcceptHeaderItem.php b/vendor/symfony/http-foundation/AcceptHeaderItem.php new file mode 100644 index 0000000..97f60c8 --- /dev/null +++ b/vendor/symfony/http-foundation/AcceptHeaderItem.php @@ -0,0 +1,191 @@ + + * + * 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(); + + public function __construct(string $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) + { + $parts = HeaderUtils::split($itemValue, ';='); + + $part = array_shift($parts); + $attributes = HeaderUtils::combine($parts); + + return new self($part[0], $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 .= '; '.HeaderUtils::toString($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/vendor/symfony/http-foundation/ApacheRequest.php b/vendor/symfony/http-foundation/ApacheRequest.php new file mode 100644 index 0000000..4e99186 --- /dev/null +++ b/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/vendor/symfony/http-foundation/BinaryFileResponse.php b/vendor/symfony/http-foundation/BinaryFileResponse.php new file mode 100644 index 0000000..07ef800 --- /dev/null +++ b/vendor/symfony/http-foundation/BinaryFileResponse.php @@ -0,0 +1,354 @@ + + * + * 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\Exception\FileException; +use Symfony\Component\HttpFoundation\File\File; + +/** + * 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 = 0; + protected $maxlen = -1; + 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 string|null $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, int $status = 200, array $headers = array(), bool $public = true, string $contentDisposition = null, bool $autoEtag = false, bool $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 string|null $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 + $parts = HeaderUtils::split($request->headers->get('X-Accel-Mapping', ''), ',='); + foreach ($parts as $part) { + list($pathPrefix, $location) = $part; + if (substr($path, 0, \strlen($pathPrefix)) === $pathPrefix) { + $path = $location.substr($path, \strlen($pathPrefix)); + 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 = true) + { + $this->deleteFileAfterSend = $shouldDelete; + + return $this; + } +} diff --git a/vendor/symfony/http-foundation/CHANGELOG.md b/vendor/symfony/http-foundation/CHANGELOG.md new file mode 100644 index 0000000..8d851c6 --- /dev/null +++ b/vendor/symfony/http-foundation/CHANGELOG.md @@ -0,0 +1,203 @@ +CHANGELOG +========= + +4.1.3 +----- + + * [BC BREAK] Support for the IIS-only `X_ORIGINAL_URL` and `X_REWRITE_URL` + HTTP headers has been dropped for security reasons. + +4.1.0 +----- + + * Query string normalization uses `parse_str()` instead of custom parsing logic. + * Passing the file size to the constructor of the `UploadedFile` class is deprecated. + * The `getClientSize()` method of the `UploadedFile` class is deprecated. Use `getSize()` instead. + * added `RedisSessionHandler` to use Redis as a session storage + * The `get()` method of the `AcceptHeader` class now takes into account the + `*` and `*/*` default values (if they are present in the Accept HTTP header) + when looking for items. + * deprecated `Request::getSession()` when no session has been set. Use `Request::hasSession()` instead. + * added `CannotWriteFileException`, `ExtensionFileException`, `FormSizeFileException`, + `IniSizeFileException`, `NoFileException`, `NoTmpDirFileException`, `PartialFileException` to + handle failed `UploadedFile`. + * added `MigratingSessionHandler` for migrating between two session handlers without losing sessions + * added `HeaderUtils`. + +4.0.0 +----- + + * the `Request::setTrustedHeaderName()` and `Request::getTrustedHeaderName()` + methods have been removed + * the `Request::HEADER_CLIENT_IP` constant has been removed, use + `Request::HEADER_X_FORWARDED_FOR` instead + * the `Request::HEADER_CLIENT_HOST` constant has been removed, use + `Request::HEADER_X_FORWARDED_HOST` instead + * the `Request::HEADER_CLIENT_PROTO` constant has been removed, use + `Request::HEADER_X_FORWARDED_PROTO` instead + * the `Request::HEADER_CLIENT_PORT` constant has been removed, use + `Request::HEADER_X_FORWARDED_PORT` instead + * checking for cacheable HTTP methods using the `Request::isMethodSafe()` + method (by not passing `false` as its argument) is not supported anymore and + throws a `\BadMethodCallException` + * the `WriteCheckSessionHandler`, `NativeSessionHandler` and `NativeProxy` classes have been removed + * setting session save handlers that do not implement `\SessionHandlerInterface` in + `NativeSessionStorage::setSaveHandler()` is not supported anymore and throws a + `\TypeError` + +3.4.0 +----- + + * 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/vendor/symfony/http-foundation/Cookie.php b/vendor/symfony/http-foundation/Cookie.php new file mode 100644 index 0000000..2332bb4 --- /dev/null +++ b/vendor/symfony/http-foundation/Cookie.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; + +/** + * 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, + ); + + $parts = HeaderUtils::split($cookie, ';='); + $part = array_shift($parts); + + $name = $decode ? urldecode($part[0]) : $part[0]; + $value = isset($part[1]) ? ($decode ? urldecode($part[1]) : $part[1]) : null; + + $data = HeaderUtils::combine($parts) + $data; + + if (isset($data['max-age'])) { + $data['expires'] = time() + (int) $data['max-age']; + } + + return new static($name, $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(string $name, string $value = null, $expire = 0, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true, bool $raw = false, string $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 = $secure; + $this->httpOnly = $httpOnly; + $this->raw = $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=0'; + } 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() + { + $maxAge = $this->expire - time(); + + return 0 >= $maxAge ? 0 : $maxAge; + } + + /** + * 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 0 !== $this->expire && $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/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php b/vendor/symfony/http-foundation/Exception/ConflictingHeadersException.php new file mode 100644 index 0000000..5fcf5b4 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php b/vendor/symfony/http-foundation/Exception/RequestExceptionInterface.php new file mode 100644 index 0000000..478d0dc --- /dev/null +++ b/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/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php b/vendor/symfony/http-foundation/Exception/SuspiciousOperationException.php new file mode 100644 index 0000000..ae7a5f1 --- /dev/null +++ b/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/vendor/symfony/http-foundation/ExpressionRequestMatcher.php b/vendor/symfony/http-foundation/ExpressionRequestMatcher.php new file mode 100644 index 0000000..e9c8441 --- /dev/null +++ b/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/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php b/vendor/symfony/http-foundation/File/Exception/AccessDeniedException.php new file mode 100644 index 0000000..c25c362 --- /dev/null +++ b/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(string $path) + { + parent::__construct(sprintf('The file %s could not be accessed', $path)); + } +} diff --git a/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php b/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php new file mode 100644 index 0000000..c49f53a --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/CannotWriteFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_CANT_WRITE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class CannotWriteFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php b/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php new file mode 100644 index 0000000..ed83499 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/ExtensionFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_EXTENSION error occurred with UploadedFile. + * + * @author Florent Mata + */ +class ExtensionFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/FileException.php b/vendor/symfony/http-foundation/File/Exception/FileException.php new file mode 100644 index 0000000..fad5133 --- /dev/null +++ b/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/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php b/vendor/symfony/http-foundation/File/Exception/FileNotFoundException.php new file mode 100644 index 0000000..0f1f3f9 --- /dev/null +++ b/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(string $path) + { + parent::__construct(sprintf('The file "%s" does not exist', $path)); + } +} diff --git a/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php b/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php new file mode 100644 index 0000000..8741be0 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/FormSizeFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_FORM_SIZE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class FormSizeFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php b/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php new file mode 100644 index 0000000..c8fde61 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/IniSizeFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_INI_SIZE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class IniSizeFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/NoFileException.php b/vendor/symfony/http-foundation/File/Exception/NoFileException.php new file mode 100644 index 0000000..4b48cc7 --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/NoFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_NO_FILE error occurred with UploadedFile. + * + * @author Florent Mata + */ +class NoFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php b/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php new file mode 100644 index 0000000..bdead2d --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/NoTmpDirFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_NO_TMP_DIR error occurred with UploadedFile. + * + * @author Florent Mata + */ +class NoTmpDirFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/PartialFileException.php b/vendor/symfony/http-foundation/File/Exception/PartialFileException.php new file mode 100644 index 0000000..4641efb --- /dev/null +++ b/vendor/symfony/http-foundation/File/Exception/PartialFileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an UPLOAD_ERR_PARTIAL error occurred with UploadedFile. + * + * @author Florent Mata + */ +class PartialFileException extends FileException +{ +} diff --git a/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php b/vendor/symfony/http-foundation/File/Exception/UnexpectedTypeException.php new file mode 100644 index 0000000..82b982b --- /dev/null +++ b/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, string $expectedType) + { + parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, \is_object($value) ? \get_class($value) : \gettype($value))); + } +} diff --git a/vendor/symfony/http-foundation/File/Exception/UploadException.php b/vendor/symfony/http-foundation/File/Exception/UploadException.php new file mode 100644 index 0000000..7074e76 --- /dev/null +++ b/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/vendor/symfony/http-foundation/File/File.php b/vendor/symfony/http-foundation/File/File.php new file mode 100644 index 0000000..de7b080 --- /dev/null +++ b/vendor/symfony/http-foundation/File/File.php @@ -0,0 +1,138 @@ + + * + * 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; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; + +/** + * 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(string $path, bool $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); + + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $renamed = rename($this->getPathname(), $target); + restore_error_handler(); + if (!$renamed) { + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error))); + } + + @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/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesser.php new file mode 100644 index 0000000..263fb32 --- /dev/null +++ b/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/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php b/vendor/symfony/http-foundation/File/MimeType/ExtensionGuesserInterface.php new file mode 100644 index 0000000..d19a0e5 --- /dev/null +++ b/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/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.php new file mode 100644 index 0000000..3130001 --- /dev/null +++ b/vendor/symfony/http-foundation/File/MimeType/FileBinaryMimeTypeGuesser.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\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + +/** + * Guesses the mime type with the binary "file" (only available on *nix). + * + * @author Bernhard Schussek + */ +class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $cmd; + + /** + * The $cmd pattern must contain a "%s" string that will be replaced + * with the file name to guess. + * + * The command output must start with the mime type of the file. + * + * @param string $cmd The command to run to get the mime type of a file + */ + public function __construct(string $cmd = 'file -b --mime %s 2>/dev/null') + { + $this->cmd = $cmd; + } + + /** + * Returns whether this guesser is supported on the current OS. + * + * @return bool + */ + public static function isSupported() + { + static $supported = null; + + if (null !== $supported) { + return $supported; + } + + if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) { + return $supported = false; + } + + ob_start(); + passthru('command -v file', $exitStatus); + $binPath = trim(ob_get_clean()); + + return $supported = 0 === $exitStatus && '' !== $binPath; + } + + /** + * {@inheritdoc} + */ + public function 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/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/FileinfoMimeTypeGuesser.php new file mode 100644 index 0000000..cee9ef3 --- /dev/null +++ b/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\AccessDeniedException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + +/** + * Guesses the mime type using the PECL extension FileInfo. + * + * @author Bernhard Schussek + */ +class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $magicFile; + + /** + * @param string $magicFile A magic file to use with the finfo instance + * + * @see http://www.php.net/manual/en/function.finfo-open.php + */ + public function __construct(string $magicFile = null) + { + $this->magicFile = $magicFile; + } + + /** + * 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/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php new file mode 100644 index 0000000..77bf51b --- /dev/null +++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -0,0 +1,808 @@ + + * + * 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-zip-compressed' => 'zip', + '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/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.php new file mode 100644 index 0000000..dae47a7 --- /dev/null +++ b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesser.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\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + +/** + * 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() + { + $this->register(new FileBinaryMimeTypeGuesser()); + $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); + } + + foreach ($this->guessers as $guesser) { + if (null !== $mimeType = $guesser->guess($path)) { + return $mimeType; + } + } + + if (2 === \count($this->guessers) && !FileBinaryMimeTypeGuesser::isSupported() && !FileinfoMimeTypeGuesser::isSupported()) { + throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)'); + } + } +} diff --git a/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php b/vendor/symfony/http-foundation/File/MimeType/MimeTypeGuesserInterface.php new file mode 100644 index 0000000..5ac1acb --- /dev/null +++ b/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\AccessDeniedException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + +/** + * 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/vendor/symfony/http-foundation/File/Stream.php b/vendor/symfony/http-foundation/File/Stream.php new file mode 100644 index 0000000..69ae74c --- /dev/null +++ b/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/vendor/symfony/http-foundation/File/UploadedFile.php b/vendor/symfony/http-foundation/File/UploadedFile.php new file mode 100644 index 0000000..63fa6c7 --- /dev/null +++ b/vendor/symfony/http-foundation/File/UploadedFile.php @@ -0,0 +1,300 @@ + + * + * 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\CannotWriteFileException; +use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException; +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException; +use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException; +use Symfony\Component\HttpFoundation\File\Exception\NoFileException; +use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException; +use Symfony\Component\HttpFoundation\File\Exception\PartialFileException; +use Symfony\Component\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 $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 $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(string $path, string $originalName, string $mimeType = null, int $error = null, $test = false) + { + $this->originalName = $this->getName($originalName); + $this->mimeType = $mimeType ?: 'application/octet-stream'; + + if (4 < \func_num_args() ? !\is_bool($test) : null !== $error && @filesize($path) === $error) { + @trigger_error(sprintf('Passing a size as 4th argument to the constructor of "%s" is deprecated since Symfony 4.1.', __CLASS__), E_USER_DEPRECATED); + $error = $test; + $test = 5 < \func_num_args() ? func_get_arg(5) : false; + } + + $this->error = $error ?: UPLOAD_ERR_OK; + $this->test = $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. + * + * @deprecated since Symfony 4.1, use getSize() instead. + * + * @return int|null The file sizes + */ + public function getClientSize() + { + @trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1. Use getSize() instead.', __METHOD__), E_USER_DEPRECATED); + + return $this->getSize(); + } + + /** + * 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); + + set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; }); + $moved = move_uploaded_file($this->getPathname(), $target); + restore_error_handler(); + if (!$moved) { + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error))); + } + + @chmod($target, 0666 & ~umask()); + + return $target; + } + + switch ($this->error) { + case UPLOAD_ERR_INI_SIZE: + throw new IniSizeFileException($this->getErrorMessage()); + case UPLOAD_ERR_FORM_SIZE: + throw new FormSizeFileException($this->getErrorMessage()); + case UPLOAD_ERR_PARTIAL: + throw new PartialFileException($this->getErrorMessage()); + case UPLOAD_ERR_NO_FILE: + throw new NoFileException($this->getErrorMessage()); + case UPLOAD_ERR_CANT_WRITE: + throw new CannotWriteFileException($this->getErrorMessage()); + case UPLOAD_ERR_NO_TMP_DIR: + throw new NoTmpDirFileException($this->getErrorMessage()); + case UPLOAD_ERR_EXTENSION: + throw new ExtensionFileException($this->getErrorMessage()); + } + + throw new FileException($this->getErrorMessage()); + } + + /** + * 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/vendor/symfony/http-foundation/FileBag.php b/vendor/symfony/http-foundation/FileBag.php new file mode 100644 index 0000000..f37e10f --- /dev/null +++ b/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['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/vendor/symfony/http-foundation/HeaderBag.php b/vendor/symfony/http-foundation/HeaderBag.php new file mode 100644 index 0000000..e1a6ae2 --- /dev/null +++ b/vendor/symfony/http-foundation/HeaderBag.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\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 = ucwords($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[]|null $default The default value + * @param bool $first Whether to return the first value or all header values + * + * @return string|string[]|null The first header value or default value if $first is true, an array of values otherwise + */ + 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 \DateTime|null 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() + { + ksort($this->cacheControl); + + return HeaderUtils::toString($this->cacheControl, ','); + } + + /** + * 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) + { + $parts = HeaderUtils::split($header, ',='); + + return HeaderUtils::combine($parts); + } +} diff --git a/vendor/symfony/http-foundation/HeaderUtils.php b/vendor/symfony/http-foundation/HeaderUtils.php new file mode 100644 index 0000000..3ee50b8 --- /dev/null +++ b/vendor/symfony/http-foundation/HeaderUtils.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\HttpFoundation; + +/** + * HTTP header utility functions. + * + * @author Christian Schmidt + */ +class HeaderUtils +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Splits an HTTP header by one or more separators. + * + * Example: + * + * HeaderUtils::split("da, en-gb;q=0.8", ",;") + * // => array(array('da'), array('en-gb', 'q=0.8')) + * + * @param string $header HTTP header value + * @param string $separators List of characters to split on, ordered by + * precedence, e.g. ",", ";=", or ",;=" + * + * @return array Nested array with as many levels as there are characters in + * $separators + */ + public static function split(string $header, string $separators): array + { + $quotedSeparators = preg_quote($separators, '/'); + + preg_match_all(' + / + (?!\s) + (?: + # quoted-string + "(?:[^"\\\\]|\\\\.)*(?:"|\\\\|$) + | + # token + [^"'.$quotedSeparators.']+ + )+ + (?['.$quotedSeparators.']) + \s* + /x', trim($header), $matches, PREG_SET_ORDER); + + return self::groupParts($matches, $separators); + } + + /** + * Combines an array of arrays into one associative array. + * + * Each of the nested arrays should have one or two elements. The first + * value will be used as the keys in the associative array, and the second + * will be used as the values, or true if the nested array only contains one + * element. Array keys are lowercased. + * + * Example: + * + * HeaderUtils::combine(array(array("foo", "abc"), array("bar"))) + * // => array("foo" => "abc", "bar" => true) + */ + public static function combine(array $parts): array + { + $assoc = array(); + foreach ($parts as $part) { + $name = strtolower($part[0]); + $value = $part[1] ?? true; + $assoc[$name] = $value; + } + + return $assoc; + } + + /** + * Joins an associative array into a string for use in an HTTP header. + * + * The key and value of each entry are joined with "=", and all entries + * are joined with the specified separator and an additional space (for + * readability). Values are quoted if necessary. + * + * Example: + * + * HeaderUtils::toString(array("foo" => "abc", "bar" => true, "baz" => "a b c"), ",") + * // => 'foo=abc, bar, baz="a b c"' + */ + public static function toString(array $assoc, string $separator): string + { + $parts = array(); + foreach ($assoc as $name => $value) { + if (true === $value) { + $parts[] = $name; + } else { + $parts[] = $name.'='.self::quote($value); + } + } + + return implode($separator.' ', $parts); + } + + /** + * Encodes a string as a quoted string, if necessary. + * + * If a string contains characters not allowed by the "token" construct in + * the HTTP specification, it is backslash-escaped and enclosed in quotes + * to match the "quoted-string" construct. + */ + public static function quote(string $s): string + { + if (preg_match('/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s)) { + return $s; + } + + return '"'.addcslashes($s, '"\\"').'"'; + } + + /** + * Decodes a quoted string. + * + * If passed an unquoted string that matches the "token" construct (as + * defined in the HTTP specification), it is passed through verbatimly. + */ + public static function unquote(string $s): string + { + return preg_replace('/\\\\(.)|"/', '$1', $s); + } + + private static function groupParts(array $matches, string $separators): array + { + $separator = $separators[0]; + $partSeparators = substr($separators, 1); + + $i = 0; + $partMatches = array(); + foreach ($matches as $match) { + if (isset($match['separator']) && $match['separator'] === $separator) { + ++$i; + } else { + $partMatches[$i][] = $match; + } + } + + $parts = array(); + if ($partSeparators) { + foreach ($partMatches as $matches) { + $parts[] = self::groupParts($matches, $partSeparators); + } + } else { + foreach ($partMatches as $matches) { + $parts[] = self::unquote($matches[0][0]); + } + } + + return $parts; + } +} diff --git a/vendor/symfony/http-foundation/IpUtils.php b/vendor/symfony/http-foundation/IpUtils.php new file mode 100644 index 0000000..a1bfa90 --- /dev/null +++ b/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/vendor/symfony/http-foundation/JsonResponse.php b/vendor/symfony/http-foundation/JsonResponse.php new file mode 100644 index 0000000..f10262e --- /dev/null +++ b/vendor/symfony/http-foundation/JsonResponse.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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, int $status = 200, array $headers = array(), bool $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()) + { + 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/vendor/symfony/http-foundation/LICENSE b/vendor/symfony/http-foundation/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/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/vendor/symfony/http-foundation/ParameterBag.php b/vendor/symfony/http-foundation/ParameterBag.php new file mode 100644 index 0000000..e3be0a9 --- /dev/null +++ b/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/vendor/symfony/http-foundation/README.md b/vendor/symfony/http-foundation/README.md new file mode 100644 index 0000000..8907f0b --- /dev/null +++ b/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/vendor/symfony/http-foundation/RedirectResponse.php b/vendor/symfony/http-foundation/RedirectResponse.php new file mode 100644 index 0000000..11f71a0 --- /dev/null +++ b/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(?string $url, int $status = 302, array $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/vendor/symfony/http-foundation/Request.php b/vendor/symfony/http-foundation/Request.php new file mode 100644 index 0000000..f095732 --- /dev/null +++ b/vendor/symfony/http-foundation/Request.php @@ -0,0 +1,2019 @@ + + * + * 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 + + 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(); + + 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|false|null + */ + 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; + + 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', + ); + + /** + * Names for headers that can be trusted when + * using trusted proxies. + * + * The FORWARDED header is the standard as of rfc7239. + * + * The other headers are non-standard, but widely used + * by popular reverse proxies (like Apache mod_proxy or Amazon EC2). + */ + private static $trustedHeaders = array( + self::HEADER_FORWARDED => 'FORWARDED', + self::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR', + self::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST', + self::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO', + self::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT', + ); + + /** + * @param array $query The GET parameters + * @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|null $content The raw body data + */ + public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $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|null $content The raw body data + */ + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $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() + { + $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|null $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', + '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'] .= ':'.$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(), '', '&'))); + + $_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; + 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; + } + + /** + * 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 ''; + } + + parse_str($qs, $qs); + ksort($qs); + + return http_build_query($qs, '', '&', PHP_QUERY_RFC3986); + } + + /** + * 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() + { + $session = $this->session; + if (!$session instanceof SessionInterface && null !== $session) { + $this->setSession($session = $session()); + } + + if (null === $session) { + @trigger_error(sprintf('Calling "%s()" when no session has been set is deprecated since Symfony 4.1 and will throw an exception in 5.0. Use "hasSession()" instead.', __METHOD__), E_USER_DEPRECATED); + // throw new \BadMethodCallException('Session has not been set'); + } + + return $session; + } + + /** + * 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->getSession()->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; + } + + /** + * @internal + */ + public function setSessionFactory(callable $factory) + { + $this->session = $factory; + } + + /** + * 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_X_FORWARDED_FOR, $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. + * + * @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. + * + * @return int|string can be a string if fetched from the server bag + */ + public function getPort() + { + if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_PORT)) { + $host = $host[0]; + } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) { + $host = $host[0]; + } elseif (!$host = $this->headers->get('HOST')) { + return $this->server->get('SERVER_PORT'); + } + + 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('/', substr($path, 1)); + 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". + * + * @return bool + */ + public function isSecure() + { + if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_X_FORWARDED_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. + * + * @return string + * + * @throws SuspiciousOperationException when the host name is invalid or not trusted + */ + public function getHost() + { + if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) { + $host = $host[0]; + } elseif (!$host = $this->headers->get('HOST')) { + if (!$host = $this->server->get('SERVER_NAME')) { + $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) { + $method = $this->request->get('_method', $this->query->get('_method', 'POST')); + if (\is_string($method)) { + $this->method = strtoupper($method); + } + } + } + } + + 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|null 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|null $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)) { + // setting $andCacheable to false should be deprecated in 4.1 + throw new \BadMethodCallException('Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is not supported.'); + } + + 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 (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 ('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'), + ); + } + + private function setPhpDefaultLocale(string $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. + * + * @return string|false The prefix as it is encoded in $string, or false + */ + private function getUrlencodedPrefix(string $string, 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::$trustedHeaderSet & $type) && $this->headers->has(self::$trustedHeaders[$type])) { + foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) { + $clientValues[] = (self::HEADER_X_FORWARDED_PORT === $type ? '0.0.0.0:' : '').trim($v); + } + } + + if ((self::$trustedHeaderSet & self::HEADER_FORWARDED) && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { + $forwarded = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); + $parts = HeaderUtils::split($forwarded, ',;='); + $forwardedValues = array(); + $param = self::$forwardedParams[$type]; + foreach ($parts as $subParts) { + if (null === $v = HeaderUtils::combine($subParts)[$param] ?? null) { + continue; + } + if (self::HEADER_X_FORWARDED_PORT === $type) { + if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) { + $v = $this->isSecure() ? ':443' : ':80'; + } + $v = '0.0.0.0'.$v; + } + $forwardedValues[] = $v; + } + } + + 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) { + if (strpos($clientIp, '.')) { + // Strip :port from IPv4 addresses. This is allowed in Forwarded + // and may occur in X-Forwarded-For. + $i = strpos($clientIp, ':'); + if ($i) { + $clientIps[$key] = $clientIp = substr($clientIp, 0, $i); + } + } elseif (0 === strpos($clientIp, '[')) { + // Strip brackets and :port from IPv6 addresses. + $i = strpos($clientIp, ']', 1); + $clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 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/vendor/symfony/http-foundation/RequestMatcher.php b/vendor/symfony/http-foundation/RequestMatcher.php new file mode 100644 index 0000000..57fa48e --- /dev/null +++ b/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(string $path = null, string $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/vendor/symfony/http-foundation/RequestMatcherInterface.php b/vendor/symfony/http-foundation/RequestMatcherInterface.php new file mode 100644 index 0000000..c26db3e --- /dev/null +++ b/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/vendor/symfony/http-foundation/RequestStack.php b/vendor/symfony/http-foundation/RequestStack.php new file mode 100644 index 0000000..40123f6 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Response.php b/vendor/symfony/http-foundation/Response.php new file mode 100644 index 0000000..7f6ae7c --- /dev/null +++ b/vendor/symfony/http-foundation/Response.php @@ -0,0 +1,1234 @@ + + * + * 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_EARLY_HINTS = 103; // RFC8297 + 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 + + /** + * @deprecated + */ + const HTTP_RESERVED_FOR_WEBDAV_ADVANCED_COLLECTIONS_EXPIRED_PROPOSAL = 425; // RFC2817 + const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04 + 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 => 'Too Early', // RFC-ietf-httpbis-replay-04 + 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 + ); + + /** + * @throws \InvalidArgumentException When the HTTP status code is not valid + */ + public function __construct($content = '', int $status = 200, array $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); + } + } + + // cookies + foreach ($this->headers->getCookies() as $cookie) { + header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode); + } + + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode); + + 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 (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) { + 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). + * + * @return $this + * + * @final + */ + public function setProtocolVersion(string $version) + { + $this->version = $version; + + return $this; + } + + /** + * Gets the HTTP protocol version. + * + * @final + */ + public function getProtocolVersion(): string + { + 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. + * + * @return $this + * + * @throws \InvalidArgumentException When the HTTP status code is not valid + * + * @final + */ + public function setStatusCode(int $code, $text = null) + { + $this->statusCode = $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. + * + * @final + */ + public function getStatusCode(): int + { + return $this->statusCode; + } + + /** + * Sets the response charset. + * + * @return $this + * + * @final + */ + public function setCharset(string $charset) + { + $this->charset = $charset; + + return $this; + } + + /** + * Retrieves the response charset. + * + * @final + */ + public function getCharset(): ?string + { + return $this->charset; + } + + /** + * Returns true if the response may safely be kept in a shared (surrogate) cache. + * + * 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 because there is + * no way to tell when or how to remove them from the cache. + * + * Note that RFC 7231 and RFC 7234 possibly allow for a more permissive implementation, + * for example "status codes that are defined as cacheable by default [...] + * can be reused by a cache with heuristic expiration unless otherwise indicated" + * (https://tools.ietf.org/html/rfc7231#section-6.1) + * + * @final + */ + public function isCacheable(): bool + { + 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. + * + * @final + */ + public function isFresh(): bool + { + 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. + * + * @final + */ + public function isValidateable(): bool + { + 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 + */ + 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 + */ + public function setPublic() + { + $this->headers->addCacheControlDirective('public'); + $this->headers->removeCacheControlDirective('private'); + + return $this; + } + + /** + * Marks the response as "immutable". + * + * @return $this + * + * @final + */ + public function setImmutable(bool $immutable = true) + { + if ($immutable) { + $this->headers->addCacheControlDirective('immutable'); + } else { + $this->headers->removeCacheControlDirective('immutable'); + } + + return $this; + } + + /** + * Returns true if the response is marked as "immutable". + * + * @final + */ + public function isImmutable(): bool + { + 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. + * + * @final + */ + public function mustRevalidate(): bool + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate'); + } + + /** + * Returns the Date header as a DateTime instance. + * + * @throws \RuntimeException When the header is not parseable + * + * @final + */ + public function getDate(): ?\DateTimeInterface + { + return $this->headers->getDate('Date'); + } + + /** + * Sets the Date header. + * + * @return $this + * + * @final + */ + public function setDate(\DateTimeInterface $date) + { + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + /** + * Returns the age of the response in seconds. + * + * @final + */ + public function getAge(): int + { + 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()); + $this->headers->remove('Expires'); + } + + return $this; + } + + /** + * Returns the value of the Expires header as a DateTime instance. + * + * @final + */ + public function getExpires(): ?\DateTimeInterface + { + try { + return $this->headers->getDate('Expires'); + } catch (\RuntimeException $e) { + // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past + return \DateTime::createFromFormat('U', time() - 172800); + } + } + + /** + * Sets the Expires HTTP header with a DateTime instance. + * + * Passing null as value will remove the header. + * + * @return $this + * + * @final + */ + public function setExpires(\DateTimeInterface $date = null) + { + if (null === $date) { + $this->headers->remove('Expires'); + + return $this; + } + + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + /** + * 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. + * + * @final + */ + public function getMaxAge(): ?int + { + 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 (int) ($this->getExpires()->format('U') - $this->getDate()->format('U')); + } + + return null; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh. + * + * This methods sets the Cache-Control max-age directive. + * + * @return $this + * + * @final + */ + public function setMaxAge(int $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. + * + * @return $this + * + * @final + */ + public function setSharedMaxAge(int $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. + * + * @final + */ + public function getTtl(): ?int + { + $maxAge = $this->getMaxAge(); + + return null !== $maxAge ? $maxAge - $this->getAge() : null; + } + + /** + * Sets the response's time-to-live for shared caches in seconds. + * + * This method adjusts the Cache-Control/s-maxage directive. + * + * @return $this + * + * @final + */ + public function setTtl(int $seconds) + { + $this->setSharedMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Sets the response's time-to-live for private/client caches in seconds. + * + * This method adjusts the Cache-Control/max-age directive. + * + * @return $this + * + * @final + */ + public function setClientTtl(int $seconds) + { + $this->setMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Returns the Last-Modified HTTP header as a DateTime instance. + * + * @throws \RuntimeException When the HTTP header is not parseable + * + * @final + */ + public function getLastModified(): ?\DateTimeInterface + { + return $this->headers->getDate('Last-Modified'); + } + + /** + * Sets the Last-Modified HTTP header with a DateTime instance. + * + * Passing null as value will remove the header. + * + * @return $this + * + * @final + */ + public function setLastModified(\DateTimeInterface $date = null) + { + if (null === $date) { + $this->headers->remove('Last-Modified'); + + return $this; + } + + if ($date instanceof \DateTime) { + $date = \DateTimeImmutable::createFromMutable($date); + } + + $date = $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + /** + * Returns the literal value of the ETag HTTP header. + * + * @final + */ + public function getEtag(): ?string + { + 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 + */ + public function setEtag(string $etag = null, bool $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. + * + * @return $this + * + * @throws \InvalidArgumentException + * + * @final + */ + 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 + */ + 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. + * + * @final + */ + public function hasVary(): bool + { + return null !== $this->headers->get('Vary'); + } + + /** + * Returns an array of header names given in the Vary header. + * + * @final + */ + public function getVary(): array + { + 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 + */ + public function setVary($headers, bool $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 + */ + public function isNotModified(Request $request): bool + { + 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? + * + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + * + * @final + */ + public function isInvalid(): bool + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * Is response informative? + * + * @final + */ + public function isInformational(): bool + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * Is response successful? + * + * @final + */ + public function isSuccessful(): bool + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * Is the response a redirect? + * + * @final + */ + public function isRedirection(): bool + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Is there a client error? + * + * @final + */ + public function isClientError(): bool + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Was there a server side error? + * + * @final + */ + public function isServerError(): bool + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Is the response OK? + * + * @final + */ + public function isOk(): bool + { + return 200 === $this->statusCode; + } + + /** + * Is the response forbidden? + * + * @final + */ + public function isForbidden(): bool + { + return 403 === $this->statusCode; + } + + /** + * Is the response a not found error? + * + * @final + */ + public function isNotFound(): bool + { + return 404 === $this->statusCode; + } + + /** + * Is the response a redirect of some form? + * + * @final + */ + public function isRedirect(string $location = null): bool + { + return \in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location')); + } + + /** + * Is the response empty? + * + * @final + */ + public function isEmpty(): bool + { + 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. + * + * @final + */ + public static function closeOutputBuffers(int $targetLevel, bool $flush) + { + $status = ob_get_status(true); + $level = \count($status); + $flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE); + + while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) { + if ($flush) { + 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 + */ + 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/vendor/symfony/http-foundation/ResponseHeaderBag.php b/vendor/symfony/http-foundation/ResponseHeaderBag.php new file mode 100644 index 0000000..ed2e0cf --- /dev/null +++ b/vendor/symfony/http-foundation/ResponseHeaderBag.php @@ -0,0 +1,339 @@ + + * + * 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 Cookie[] + * + * @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.'); + } + + $params = array('filename' => $filenameFallback); + if ($filename !== $filenameFallback) { + $params['filename*'] = "utf-8''".rawurlencode($filename); + } + + return $disposition.'; '.HeaderUtils::toString($params, ';'); + } + + /** + * 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/vendor/symfony/http-foundation/ServerBag.php b/vendor/symfony/http-foundation/ServerBag.php new file mode 100644 index 0000000..d8ab561 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php b/vendor/symfony/http-foundation/Session/Attribute/AttributeBag.php new file mode 100644 index 0000000..82577b8 --- /dev/null +++ b/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(string $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/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/vendor/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php new file mode 100644 index 0000000..0d8d179 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php new file mode 100644 index 0000000..50cd740 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.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\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(string $storageKey = '_sf2_attributes', string $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)) { + if (!$writeContext) { + $null = null; + + return $null; + } + + $array[$part] = array(); + } + + $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/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/vendor/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php new file mode 100644 index 0000000..ef23457 --- /dev/null +++ b/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(string $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/vendor/symfony/http-foundation/Session/Flash/FlashBag.php b/vendor/symfony/http-foundation/Session/Flash/FlashBag.php new file mode 100644 index 0000000..44ddb96 --- /dev/null +++ b/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(string $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/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/vendor/symfony/http-foundation/Session/Flash/FlashBagInterface.php new file mode 100644 index 0000000..f53c9da --- /dev/null +++ b/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 mixed $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/vendor/symfony/http-foundation/Session/Session.php b/vendor/symfony/http-foundation/Session/Session.php new file mode 100644 index 0000000..3349906 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Session.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\HttpFoundation\Session; + +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; +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; + +/** + * @author Fabien Potencier + * @author Drak + */ +class Session implements SessionInterface, \IteratorAggregate, \Countable +{ + protected $storage; + + private $flashName; + private $attributeName; + private $data = array(); + private $usageIndex = 0; + + /** + * @param SessionStorageInterface $storage A SessionStorageInterface instance + * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) + * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) + */ + public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + $this->storage = $storage ?: new NativeSessionStorage(); + + $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 int + * + * @internal + */ + public function getUsageIndex() + { + return $this->usageIndex; + } + + /** + * @return bool + * + * @internal + */ + public function isEmpty() + { + if ($this->isStarted()) { + ++$this->usageIndex; + } + 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) + { + if ($this->storage->getId() !== $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() + { + ++$this->usageIndex; + + return $this->storage->getMetadataBag(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex)); + } + + /** + * {@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/vendor/symfony/http-foundation/Session/SessionBagInterface.php b/vendor/symfony/http-foundation/Session/SessionBagInterface.php new file mode 100644 index 0000000..8e37d06 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/SessionBagProxy.php b/vendor/symfony/http-foundation/Session/SessionBagProxy.php new file mode 100644 index 0000000..3504bdf --- /dev/null +++ b/vendor/symfony/http-foundation/Session/SessionBagProxy.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\Session; + +/** + * @author Nicolas Grekas + * + * @internal + */ +final class SessionBagProxy implements SessionBagInterface +{ + private $bag; + private $data; + private $usageIndex; + + public function __construct(SessionBagInterface $bag, array &$data, &$usageIndex) + { + $this->bag = $bag; + $this->data = &$data; + $this->usageIndex = &$usageIndex; + } + + /** + * @return SessionBagInterface + */ + public function getBag() + { + ++$this->usageIndex; + + return $this->bag; + } + + /** + * @return bool + */ + public function isEmpty() + { + if (!isset($this->data[$this->bag->getStorageKey()])) { + return true; + } + ++$this->usageIndex; + + return empty($this->data[$this->bag->getStorageKey()]); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->bag->getName(); + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$array) + { + ++$this->usageIndex; + $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/vendor/symfony/http-foundation/Session/SessionInterface.php b/vendor/symfony/http-foundation/Session/SessionInterface.php new file mode 100644 index 0000000..95fca85 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php new file mode 100644 index 0000000..b146571 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php @@ -0,0 +1,154 @@ + + * + * 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; + + return $data; + } + + /** + * {@inheritdoc} + */ + public function write($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 (!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/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php new file mode 100644 index 0000000..61a7afd --- /dev/null +++ b/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) + { + $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl); + + return true; + } + + /** + * {@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/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php new file mode 100644 index 0000000..5293d24 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php @@ -0,0 +1,124 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Migrating session handler for migrating from one handler to another. It reads + * from the current handler and writes both the current and new ones. + * + * It ignores errors from the new handler. + * + * @author Ross Motley + * @author Oliver Radwell + */ +class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface +{ + private $currentHandler; + private $writeOnlyHandler; + + public function __construct(\SessionHandlerInterface $currentHandler, \SessionHandlerInterface $writeOnlyHandler) + { + if (!$currentHandler instanceof \SessionUpdateTimestampHandlerInterface) { + $currentHandler = new StrictSessionHandler($currentHandler); + } + if (!$writeOnlyHandler instanceof \SessionUpdateTimestampHandlerInterface) { + $writeOnlyHandler = new StrictSessionHandler($writeOnlyHandler); + } + + $this->currentHandler = $currentHandler; + $this->writeOnlyHandler = $writeOnlyHandler; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $result = $this->currentHandler->close(); + $this->writeOnlyHandler->close(); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + $result = $this->currentHandler->destroy($sessionId); + $this->writeOnlyHandler->destroy($sessionId); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + $result = $this->currentHandler->gc($maxlifetime); + $this->writeOnlyHandler->gc($maxlifetime); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + $result = $this->currentHandler->open($savePath, $sessionName); + $this->writeOnlyHandler->open($savePath, $sessionName); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + // No reading from new handler until switch-over + return $this->currentHandler->read($sessionId); + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $sessionData) + { + $result = $this->currentHandler->write($sessionId, $sessionData); + $this->writeOnlyHandler->write($sessionId, $sessionData); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function validateId($sessionId) + { + // No reading from new handler until switch-over + return $this->currentHandler->validateId($sessionId); + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $sessionData) + { + $result = $this->currentHandler->updateTimestamp($sessionId, $sessionData); + $this->writeOnlyHandler->updateTimestamp($sessionId, $sessionData); + + return $result; + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php new file mode 100644 index 0000000..853b0bc --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php @@ -0,0 +1,193 @@ + + * + * 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 \MongoDB\Collection + */ + 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 "database" or "collection" not provided + */ + public function __construct(\MongoDB\Client $mongo, array $options) + { + 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) + { + $this->getCollection()->deleteOne(array( + $this->options['id_field'] => $sessionId, + )); + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + $this->getCollection()->deleteMany(array( + $this->options['expiry_field'] => array('$lt' => new \MongoDB\BSON\UTCDateTime()), + )); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data) + { + $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); + + $fields = array( + $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), + $this->options['expiry_field'] => $expiry, + $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY), + ); + + $this->getCollection()->updateOne( + array($this->options['id_field'] => $sessionId), + array('$set' => $fields), + array('upsert' => true) + ); + + return true; + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000); + + $this->getCollection()->updateOne( + array($this->options['id_field'] => $sessionId), + array('$set' => array( + $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(), + $this->options['expiry_field'] => $expiry, + )) + ); + + return true; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId) + { + $dbData = $this->getCollection()->findOne(array( + $this->options['id_field'] => $sessionId, + $this->options['expiry_field'] => array('$gte' => new \MongoDB\BSON\UTCDateTime()), + )); + + if (null === $dbData) { + return ''; + } + + return $dbData[$this->options['data_field']]->getData(); + } + + /** + * @return \MongoDB\Collection + */ + private function getCollection() + { + if (null === $this->collection) { + $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']); + } + + return $this->collection; + } + + /** + * @return \MongoDB\Client + */ + protected function getMongo() + { + return $this->mongo; + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php new file mode 100644 index 0000000..f962965 --- /dev/null +++ b/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 \SessionHandler +{ + /** + * @param string $savePath Path of directory to save session files + * Default null will leave setting as defined by PHP. + * '/path', 'N;/path', or 'N;octal-mode;/path + * + * @see http://php.net/session.configuration.php#ini.session.save-path for further details. + * + * @throws \InvalidArgumentException On invalid $savePath + * @throws \RuntimeException When failing to create the save directory + */ + public function __construct(string $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/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php new file mode 100644 index 0000000..8d19315 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php new file mode 100644 index 0000000..dd1a263 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php @@ -0,0 +1,904 @@ + + * + * 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|false|null 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 URL string or null + * @param array $options An associative array of options + * + * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION + */ + public function __construct($pdoOrDsn = null, array $options = array()) + { + 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); + } elseif (\is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) { + $this->dsn = $this->buildDsnFromUrl($pdoOrDsn); + } 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); + } + + /** + * Builds a PDO DSN from a URL-like connection string. + * + * @param string $dsnOrUrl + * + * @return string + * + * @todo implement missing support for oci DSN (which look totally different from other PDO ones) + */ + private function buildDsnFromUrl($dsnOrUrl) + { + // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid + $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl); + + $params = parse_url($url); + + if (false === $params) { + return $dsnOrUrl; // If the URL is not valid, let's assume it might be a DSN already. + } + + $params = array_map('rawurldecode', $params); + + // Override the default username and password. Values passed through options will still win over these in the constructor. + if (isset($params['user'])) { + $this->username = $params['user']; + } + + if (isset($params['pass'])) { + $this->password = $params['pass']; + } + + if (!isset($params['scheme'])) { + throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler'); + } + + $driverAliasMap = array( + 'mssql' => 'sqlsrv', + 'mysql2' => 'mysql', // Amazon RDS, for some weird reason + 'postgres' => 'pgsql', + 'postgresql' => 'pgsql', + 'sqlite3' => 'sqlite', + ); + + $driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme']; + + // Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here. + if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) { + $driver = substr($driver, 4); + } + + switch ($driver) { + case 'mysql': + case 'pgsql': + $dsn = $driver.':'; + + if (isset($params['host']) && '' !== $params['host']) { + $dsn .= 'host='.$params['host'].';'; + } + + if (isset($params['port']) && '' !== $params['port']) { + $dsn .= 'port='.$params['port'].';'; + } + + if (isset($params['path'])) { + $dbName = substr($params['path'], 1); // Remove the leading slash + $dsn .= 'dbname='.$dbName.';'; + } + + return $dsn; + + case 'sqlite': + return 'sqlite:'.substr($params['path'], 1); + + case 'sqlsrv': + $dsn = 'sqlsrv:server='; + + if (isset($params['host'])) { + $dsn .= $params['host']; + } + + if (isset($params['port']) && '' !== $params['port']) { + $dsn .= ','.$params['port']; + } + + if (isset($params['path'])) { + $dbName = substr($params['path'], 1); // Remove the leading slash + $dsn .= ';Database='.$dbName; + } + + return $dsn; + + default: + throw new \InvalidArgumentException(sprintf('The scheme "%s" is not supported by the PdoSessionHandler URL configuration. Pass a PDO DSN directly.', $params['scheme'])); + } + } + + /** + * 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); + $insertStmt = null; + + 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 (null !== $insertStmt) { + $this->rollback(); + throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.'); + } + + 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. + * + * @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(string $sessionId) + { + switch ($this->driver) { + case 'mysql': + // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced. + $lockId = \substr($sessionId, 0, 64); + // 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', $lockId, \PDO::PARAM_STR); + $stmt->execute(); + + $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)'); + $releaseStmt->bindValue(':key', $lockId, \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. + */ + private function convertStringToInt(string $string): int + { + if (4 === \PHP_INT_SIZE) { + return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]); + } + + $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. + * + * @throws \DomainException When an unsupported PDO driver is used + */ + private function getSelectSql(): string + { + 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. + */ + private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement + { + switch (true) { + case 'mysql' === $this->driver: + $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :lifetime, :time) ". + "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/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php new file mode 100644 index 0000000..36adf24 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.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\HttpFoundation\Session\Storage\Handler; + +use Predis\Response\ErrorInterface; +use Symfony\Component\Cache\Traits\RedisProxy; + +/** + * Redis based session storage handler based on the Redis class + * provided by the PHP redis extension. + * + * @author Dalibor Karlović + */ +class RedisSessionHandler extends AbstractSessionHandler +{ + private $redis; + + /** + * @var string Key prefix for shared environments + */ + private $prefix; + + /** + * List of available options: + * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server. + * + * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client|RedisProxy $redis + * @param array $options An associative array of options + * + * @throws \InvalidArgumentException When unsupported client or options are passed + */ + public function __construct($redis, array $options = array()) + { + if ( + !$redis instanceof \Redis && + !$redis instanceof \RedisArray && + !$redis instanceof \RedisCluster && + !$redis instanceof \Predis\Client && + !$redis instanceof RedisProxy + ) { + throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis))); + } + + if ($diff = array_diff(array_keys($options), array('prefix'))) { + throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff))); + } + + $this->redis = $redis; + $this->prefix = $options['prefix'] ?? 'sf_s'; + } + + /** + * {@inheritdoc} + */ + protected function doRead($sessionId): string + { + return $this->redis->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + protected function doWrite($sessionId, $data): bool + { + $result = $this->redis->setEx($this->prefix.$sessionId, (int) ini_get('session.gc_maxlifetime'), $data); + + return $result && !$result instanceof ErrorInterface; + } + + /** + * {@inheritdoc} + */ + protected function doDestroy($sessionId): bool + { + $this->redis->del($this->prefix.$sessionId); + + return true; + } + + /** + * {@inheritdoc} + */ + public function close(): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime): bool + { + return true; + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return (bool) $this->redis->expire($this->prefix.$sessionId, (int) ini_get('session.gc_maxlifetime')); + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php b/vendor/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php new file mode 100644 index 0000000..83a1f2c --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php b/vendor/symfony/http-foundation/Session/Storage/MetadataBag.php new file mode 100644 index 0000000..ea0d5ec --- /dev/null +++ b/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(string $storageKey = '_sf2_meta', int $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/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php new file mode 100644 index 0000000..47cac39 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php @@ -0,0 +1,252 @@ + + * + * 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(); + + public function __construct(string $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/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php new file mode 100644 index 0000000..732f92a --- /dev/null +++ b/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(string $savePath = null, string $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/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php new file mode 100644 index 0000000..dc4ce44 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php @@ -0,0 +1,432 @@ + + * + * 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, "" + * gc_divisor, "100" + * gc_maxlifetime, "1440" + * gc_probability, "1" + * 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, + 'use_strict_mode' => 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 error handler to add information about the current save handler + $previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) { + if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) { + $handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler; + $msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler)); + } + + return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false; + }); + + try { + session_write_close(); + } finally { + restore_error_handler(); + $_SESSION = $session; + } + + $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_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly', + 'cookie_lifetime', 'cookie_path', 'cookie_secure', + 'gc_divisor', 'gc_maxlifetime', 'gc_probability', + 'lazy_write', 'name', 'referer_check', + 'serialize_handler', 'use_strict_mode', 'use_cookies', + 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', + 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', + 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags', + 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags', + )); + + foreach ($options as $key => $value) { + if (isset($validOptions[$key])) { + ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $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, 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/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/vendor/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php new file mode 100644 index 0000000..662ed50 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php new file mode 100644 index 0000000..09c9248 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php b/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php new file mode 100644 index 0000000..b11cc39 --- /dev/null +++ b/vendor/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php @@ -0,0 +1,101 @@ + + * + * 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, \SessionUpdateTimestampHandlerInterface +{ + 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); + } + + /** + * {@inheritdoc} + */ + public function validateId($sessionId) + { + return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId); + } + + /** + * {@inheritdoc} + */ + public function updateTimestamp($sessionId, $data) + { + return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data); + } +} diff --git a/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/vendor/symfony/http-foundation/Session/Storage/SessionStorageInterface.php new file mode 100644 index 0000000..66e8b33 --- /dev/null +++ b/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/vendor/symfony/http-foundation/StreamedResponse.php b/vendor/symfony/http-foundation/StreamedResponse.php new file mode 100644 index 0000000..40f785c --- /dev/null +++ b/vendor/symfony/http-foundation/StreamedResponse.php @@ -0,0 +1,146 @@ + + * + * 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, int $status = 200, array $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.'); + } + + $this->streamed = true; + + return $this; + } + + /** + * {@inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } +} diff --git a/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php b/vendor/symfony/http-foundation/Tests/AcceptHeaderItemTest.php new file mode 100644 index 0000000..1a66024 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php b/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php new file mode 100644 index 0000000..1ac6103 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/AcceptHeaderTest.php @@ -0,0 +1,130 @@ + + * + * 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', '*')), + ); + } + + /** + * @dataProvider provideDefaultValueData + */ + public function testDefaultValue($acceptHeader, $value, $expectedQuality) + { + $header = AcceptHeader::fromString($acceptHeader); + $this->assertSame($expectedQuality, $header->get($value)->getQuality()); + } + + public function provideDefaultValueData() + { + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, *;q=0.3', 'text/xml', 0.3); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/xml', 0.3); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/html', 1.0); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', 'text/plain', 0.5); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*;q=0.3', '*', 0.3); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', '*', 1.0); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/xml', 1.0); + yield array('text/plain;q=0.5, text/html, text/x-dvi;q=0.8, */*', 'text/*', 1.0); + yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/*', 0.8); + yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/html', 1.0); + yield array('text/plain;q=0.5, text/html, text/*;q=0.8, */*', 'text/x-dvi', 0.8); + yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', '*', 0.3); + yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'utf-8', 0.7); + yield array('*;q=0.3, ISO-8859-1;q=0.7, utf-8;q=0.7', 'SHIFT_JIS', 0.3); + } +} diff --git a/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php b/vendor/symfony/http-foundation/Tests/ApacheRequestTest.php new file mode 100644 index 0000000..157ab90 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php b/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php new file mode 100644 index 0000000..6bf04e1 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/BinaryFileResponseTest.php @@ -0,0 +1,366 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\File\Stream; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; +use Symfony\Component\HttpFoundation\Tests\File\FakeFile; + +class BinaryFileResponseTest extends ResponseTestCase +{ + public function testConstruction() + { + $file = __DIR__.'/../README.md'; + $response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true); + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEquals('Foo', $response->headers->get('X-Header')); + $this->assertTrue($response->headers->has('ETag')); + $this->assertTrue($response->headers->has('Last-Modified')); + $this->assertFalse($response->headers->has('Content-Disposition')); + + $response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE); + $this->assertEquals(404, $response->getStatusCode()); + $this->assertFalse($response->headers->has('ETag')); + $this->assertEquals('inline; filename=README.md', $response->headers->get('Content-Disposition')); + } + + public function testConstructWithNonAsciiFilename() + { + touch(sys_get_temp_dir().'/fööö.html'); + + $response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment'); + + @unlink(sys_get_temp_dir().'/fööö.html'); + + $this->assertSame('fööö.html', $response->getFile()->getFilename()); + } + + /** + * @expectedException \LogicException + */ + public function testSetContent() + { + $response = new BinaryFileResponse(__FILE__); + $response->setContent('foo'); + } + + public function testGetContent() + { + $response = new BinaryFileResponse(__FILE__); + $this->assertFalse($response->getContent()); + } + + public function testSetContentDispositionGeneratesSafeFallbackFilename() + { + $response = new BinaryFileResponse(__FILE__); + $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html'); + + $this->assertSame('attachment; filename=f__.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition')); + } + + public function testSetContentDispositionGeneratesSafeFallbackFilenameForWronglyEncodedFilename() + { + $response = new BinaryFileResponse(__FILE__); + + $iso88591EncodedFilename = utf8_decode('föö.html'); + $response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $iso88591EncodedFilename); + + // the parameter filename* is invalid in this case (rawurldecode('f%F6%F6') does not provide a UTF-8 string but an ISO-8859-1 encoded one) + $this->assertSame('attachment; filename=f__.html; filename*=utf-8\'\'f%F6%F6.html', $response->headers->get('Content-Disposition')); + } + + /** + * @dataProvider provideRanges + */ + public function testRequests($requestRange, $offset, $length, $responseRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); + + // do a request to get the ETag + $request = Request::create('/'); + $response->prepare($request); + $etag = $response->headers->get('ETag'); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('If-Range', $etag); + $request->headers->set('Range', $requestRange); + + $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); + fseek($file, $offset); + $data = fread($file, $length); + fclose($file); + + $this->expectOutputString($data); + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(206, $response->getStatusCode()); + $this->assertEquals($responseRange, $response->headers->get('Content-Range')); + $this->assertSame($length, $response->headers->get('Content-Length')); + } + + /** + * @dataProvider provideRanges + */ + public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); + + // do a request to get the LastModified + $request = Request::create('/'); + $response->prepare($request); + $lastModified = $response->headers->get('Last-Modified'); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('If-Range', $lastModified); + $request->headers->set('Range', $requestRange); + + $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); + fseek($file, $offset); + $data = fread($file, $length); + fclose($file); + + $this->expectOutputString($data); + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(206, $response->getStatusCode()); + $this->assertEquals($responseRange, $response->headers->get('Content-Range')); + } + + public function provideRanges() + { + return array( + array('bytes=1-4', 1, 4, 'bytes 1-4/35'), + array('bytes=-5', 30, 5, 'bytes 30-34/35'), + array('bytes=30-', 30, 5, 'bytes 30-34/35'), + array('bytes=30-30', 30, 1, 'bytes 30-30/35'), + array('bytes=30-34', 30, 5, 'bytes 30-34/35'), + ); + } + + public function testRangeRequestsWithoutLastModifiedDate() + { + // prevent auto last modified + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT'); + $request->headers->set('Range', 'bytes=1-4'); + + $this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif')); + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(200, $response->getStatusCode()); + $this->assertNull($response->headers->get('Content-Range')); + } + + /** + * @dataProvider provideFullFileRanges + */ + public function testFullFileRequests($requestRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('Range', $requestRange); + + $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r'); + $data = fread($file, 35); + fclose($file); + + $this->expectOutputString($data); + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + public function provideFullFileRanges() + { + return array( + array('bytes=0-'), + array('bytes=0-34'), + array('bytes=-35'), + // Syntactical invalid range-request should also return the full resource + array('bytes=20-10'), + array('bytes=50-40'), + ); + } + + public function testUnpreparedResponseSendsFullFile() + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200); + + $data = file_get_contents(__DIR__.'/File/Fixtures/test.gif'); + + $this->expectOutputString($data); + $response = clone $response; + $response->sendContent(); + + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @dataProvider provideInvalidRanges + */ + public function testInvalidRequests($requestRange) + { + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag(); + + // prepare a request for a range of the testing file + $request = Request::create('/'); + $request->headers->set('Range', $requestRange); + + $response = clone $response; + $response->prepare($request); + $response->sendContent(); + + $this->assertEquals(416, $response->getStatusCode()); + $this->assertEquals('bytes */35', $response->headers->get('Content-Range')); + } + + public function provideInvalidRanges() + { + return array( + array('bytes=-40'), + array('bytes=30-40'), + ); + } + + /** + * @dataProvider provideXSendfileFiles + */ + public function testXSendfile($file) + { + $request = Request::create('/'); + $request->headers->set('X-Sendfile-Type', 'X-Sendfile'); + + BinaryFileResponse::trustXSendfileTypeHeader(); + $response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream')); + $response->prepare($request); + + $this->expectOutputString(''); + $response->sendContent(); + + $this->assertContains('README.md', $response->headers->get('X-Sendfile')); + } + + public function provideXSendfileFiles() + { + return array( + array(__DIR__.'/../README.md'), + array('file://'.__DIR__.'/../README.md'), + ); + } + + /** + * @dataProvider getSampleXAccelMappings + */ + public function testXAccelMapping($realpath, $mapping, $virtual) + { + $request = Request::create('/'); + $request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect'); + $request->headers->set('X-Accel-Mapping', $mapping); + + $file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test'); + + BinaryFileResponse::trustXSendfileTypeHeader(); + $response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream')); + $reflection = new \ReflectionObject($response); + $property = $reflection->getProperty('file'); + $property->setAccessible(true); + $property->setValue($response, $file); + + $response->prepare($request); + $this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect')); + } + + public function testDeleteFileAfterSend() + { + $request = Request::create('/'); + + $path = __DIR__.'/File/Fixtures/to_delete'; + touch($path); + $realPath = realpath($path); + $this->assertFileExists($realPath); + + $response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream')); + $response->deleteFileAfterSend(true); + + $response->prepare($request); + $response->sendContent(); + + $this->assertFileNotExists($path); + } + + public function testAcceptRangeOnUnsafeMethods() + { + $request = Request::create('/', 'POST'); + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); + $response->prepare($request); + + $this->assertEquals('none', $response->headers->get('Accept-Ranges')); + } + + public function testAcceptRangeNotOverriden() + { + $request = Request::create('/', 'POST'); + $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream')); + $response->headers->set('Accept-Ranges', 'foo'); + $response->prepare($request); + + $this->assertEquals('foo', $response->headers->get('Accept-Ranges')); + } + + public function getSampleXAccelMappings() + { + return array( + array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'), + array('/home/Foo/bar.txt', '/var/www/=/files/,/home/Foo/=/baz/', '/baz/bar.txt'), + array('/home/Foo/bar.txt', '"/var/www/"="/files/", "/home/Foo/"="/baz/"', '/baz/bar.txt'), + ); + } + + public function testStream() + { + $request = Request::create('/'); + $response = new BinaryFileResponse(new Stream(__DIR__.'/../README.md'), 200, array('Content-Type' => 'text/plain')); + $response->prepare($request); + + $this->assertNull($response->headers->get('Content-Length')); + } + + protected function provideResponse() + { + return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream')); + } + + public static function tearDownAfterClass() + { + $path = __DIR__.'/../Fixtures/to_delete'; + if (file_exists($path)) { + @unlink($path); + } + } +} diff --git a/vendor/symfony/http-foundation/Tests/CookieTest.php b/vendor/symfony/http-foundation/Tests/CookieTest.php new file mode 100644 index 0000000..390d42a --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/CookieTest.php @@ -0,0 +1,235 @@ + + * + * 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'); + } + + public function testConstructorWithDateTimeImmutable() + { + $expire = new \DateTimeImmutable(); + $cookie = new Cookie('foo', 'bar', $expire); + + $this->assertEquals($expire->format('U'), $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date'); + } + + public function testGetExpiresTimeWithStringValue() + { + $value = '+1 day'; + $cookie = new Cookie('foo', 'bar', $value); + $expire = strtotime($value); + + $this->assertEquals($expire, $cookie->getExpiresTime(), '->getExpiresTime() returns the expire date', 1); + } + + public function testGetDomain() + { + $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com'); + + $this->assertEquals('.myfoodomain.com', $cookie->getDomain(), '->getDomain() returns the domain name on which the cookie is valid'); + } + + public function testIsSecure() + { + $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', true); + + $this->assertTrue($cookie->isSecure(), '->isSecure() returns whether the cookie is transmitted over HTTPS'); + } + + public function testIsHttpOnly() + { + $cookie = new Cookie('foo', 'bar', 0, '/', '.myfoodomain.com', false, true); + + $this->assertTrue($cookie->isHttpOnly(), '->isHttpOnly() returns whether the cookie is only transmitted over HTTP'); + } + + public function testCookieIsNotCleared() + { + $cookie = new Cookie('foo', 'bar', time() + 3600 * 24); + + $this->assertFalse($cookie->isCleared(), '->isCleared() returns false if the cookie did not expire yet'); + } + + public function testCookieIsCleared() + { + $cookie = new Cookie('foo', 'bar', time() - 20); + + $this->assertTrue($cookie->isCleared(), '->isCleared() returns true if the cookie has expired'); + + $cookie = new Cookie('foo', 'bar'); + + $this->assertFalse($cookie->isCleared()); + + $cookie = new Cookie('foo', 'bar', 0); + + $this->assertFalse($cookie->isCleared()); + + $cookie = new Cookie('foo', 'bar', -1); + + $this->assertFalse($cookie->isCleared()); + } + + public function testToString() + { + $cookie = new Cookie('foo', 'bar', $expire = strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); + $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie'); + + $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); + $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; Max-Age=0; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)'); + + $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com'); + $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', $expire = time() - 31536001).'; Max-Age=0; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL'); + + $cookie = new Cookie('foo', 'bar', 0, '/', ''); + $this->assertEquals('foo=bar; path=/; httponly', (string) $cookie); + } + + public function testRawCookie() + { + $cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false); + $this->assertFalse($cookie->isRaw()); + $this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie); + + $cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true); + $this->assertTrue($cookie->isRaw()); + $this->assertEquals('foo=b+a+r; path=/', (string) $cookie); + } + + public function testGetMaxAge() + { + $cookie = new Cookie('foo', 'bar'); + $this->assertEquals(0, $cookie->getMaxAge()); + + $cookie = new Cookie('foo', 'bar', $expire = time() + 100); + $this->assertEquals($expire - time(), $cookie->getMaxAge()); + + $cookie = new Cookie('foo', 'bar', $expire = time() - 100); + $this->assertEquals(0, $cookie->getMaxAge()); + } + + public function testFromString() + { + $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly'); + $this->assertEquals(new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true, true, true), $cookie); + + $cookie = Cookie::fromString('foo=bar', true); + $this->assertEquals(new Cookie('foo', 'bar', 0, '/', null, false, false), $cookie); + + $cookie = Cookie::fromString('foo', true); + $this->assertEquals(new Cookie('foo', null, 0, '/', null, false, false), $cookie); + } + + public function testFromStringWithHttpOnly() + { + $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly'); + $this->assertTrue($cookie->isHttpOnly()); + + $cookie = Cookie::fromString('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure'); + $this->assertFalse($cookie->isHttpOnly()); + } + + public function testSameSiteAttributeIsCaseInsensitive() + { + $cookie = new Cookie('foo', 'bar', 0, '/', null, false, true, false, 'Lax'); + $this->assertEquals('lax', $cookie->getSameSite()); + } +} diff --git a/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php b/vendor/symfony/http-foundation/Tests/ExpressionRequestMatcherTest.php new file mode 100644 index 0000000..1152e46 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/File/FakeFile.php b/vendor/symfony/http-foundation/Tests/File/FakeFile.php new file mode 100644 index 0000000..c415989 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/File/FileTest.php b/vendor/symfony/http-foundation/Tests/File/FileTest.php new file mode 100644 index 0000000..dbd9c44 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension b/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension new file mode 100644 index 0000000..4d1ae35 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/File/Fixtures/.unknownextension @@ -0,0 +1 @@ +f \ No newline at end of file diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty b/vendor/symfony/http-foundation/Tests/File/Fixtures/directory/.empty new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example b/vendor/symfony/http-foundation/Tests/File/Fixtures/other-file.example new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-foundation/Tests/File/Fixtures/test b/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/vendor/symfony/http-foundation/Tests/File/Fixtures/test.gif b/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/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php b/vendor/symfony/http-foundation/Tests/File/MimeType/MimeTypeTest.php new file mode 100644 index 0000000..bb88807 --- /dev/null +++ b/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\FileBinaryMimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; + +/** + * @requires extension fileinfo + */ +class MimeTypeTest extends TestCase +{ + protected $path; + + public function testGuessImageWithoutExtension() + { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithDirectory() + { + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + + MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/directory'); + } + + public function testGuessImageWithFileBinaryMimeTypeGuesser() + { + $guesser = MimeTypeGuesser::getInstance(); + $guesser->register(new FileBinaryMimeTypeGuesser()); + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test')); + } + + public function testGuessImageWithKnownExtension() + { + $this->assertEquals('image/gif', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/test.gif')); + } + + public function testGuessFileWithUnknownExtension() + { + $this->assertEquals('application/octet-stream', MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/.unknownextension')); + } + + public function testGuessWithIncorrectPath() + { + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException'); + MimeTypeGuesser::getInstance()->guess(__DIR__.'/../Fixtures/not_here'); + } + + public function testGuessWithNonReadablePath() + { + if ('\\' === \DIRECTORY_SEPARATOR) { + $this->markTestSkipped('Can not verify chmod operations on Windows'); + } + + if (!getenv('USER') || 'root' === getenv('USER')) { + $this->markTestSkipped('This test will fail if run under superuser'); + } + + $path = __DIR__.'/../Fixtures/to_delete'; + touch($path); + @chmod($path, 0333); + + if ('0333' == substr(sprintf('%o', fileperms($path)), -4)) { + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException'); + MimeTypeGuesser::getInstance()->guess($path); + } else { + $this->markTestSkipped('Can not verify chmod operations, change of file permissions failed'); + } + } + + public static function tearDownAfterClass() + { + $path = __DIR__.'/../Fixtures/to_delete'; + if (file_exists($path)) { + @chmod($path, 0666); + @unlink($path); + } + } +} diff --git a/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php b/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php new file mode 100644 index 0000000..6a0b550 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/File/UploadedFileTest.php @@ -0,0 +1,346 @@ + + * + * 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\Exception\CannotWriteFileException; +use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException; +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException; +use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException; +use Symfony\Component\HttpFoundation\File\Exception\NoFileException; +use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException; +use Symfony\Component\HttpFoundation\File\Exception\PartialFileException; +use Symfony\Component\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, + 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, + UPLOAD_ERR_OK + ); + + $this->assertEquals('application/octet-stream', $file->getClientMimeType()); + } + + public function testGuessClientExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + null + ); + + $this->assertEquals('gif', $file->guessClientExtension()); + } + + public function testGuessClientExtensionWithIncorrectMimeType() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/jpeg', + null + ); + + $this->assertEquals('jpeg', $file->guessClientExtension()); + } + + public function testErrorIsOkByDefault() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + null + ); + + $this->assertEquals(UPLOAD_ERR_OK, $file->getError()); + } + + public function testGetClientOriginalName() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + null + ); + + $this->assertEquals('original.gif', $file->getClientOriginalName()); + } + + public function testGetClientOriginalExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/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', + UPLOAD_ERR_OK + ); + + $movedFile = $file->move(__DIR__.'/Fixtures/directory'); + } + + public function failedUploadedFile() + { + foreach (array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_EXTENSION, -1) as $error) { + yield array(new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + $error + )); + } + } + + /** + * @dataProvider failedUploadedFile + */ + public function testMoveFailed(UploadedFile $file) + { + switch ($file->getError()) { + case UPLOAD_ERR_INI_SIZE: + $exceptionClass = IniSizeFileException::class; + break; + case UPLOAD_ERR_FORM_SIZE: + $exceptionClass = FormSizeFileException::class; + break; + case UPLOAD_ERR_PARTIAL: + $exceptionClass = PartialFileException::class; + break; + case UPLOAD_ERR_NO_FILE: + $exceptionClass = NoFileException::class; + break; + case UPLOAD_ERR_CANT_WRITE: + $exceptionClass = CannotWriteFileException::class; + break; + case UPLOAD_ERR_NO_TMP_DIR: + $exceptionClass = NoTmpDirFileException::class; + break; + case UPLOAD_ERR_EXTENSION: + $exceptionClass = ExtensionFileException::class; + break; + default: + $exceptionClass = FileException::class; + } + + $this->expectException($exceptionClass); + + $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', + 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' + ); + + $this->assertEquals('original.gif', $file->getClientOriginalName()); + } + + public function testGetSize() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif' + ); + + $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()); + } + + /** + * @group legacy + * @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1. + */ + public function testConstructDeprecatedSize() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif'), + UPLOAD_ERR_OK, + false + ); + + $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); + } + + /** + * @group legacy + * @expectedDeprecation Passing a size as 4th argument to the constructor of "Symfony\Component\HttpFoundation\File\UploadedFile" is deprecated since Symfony 4.1. + */ + public function testConstructDeprecatedSizeWhenPassingOnlyThe4Needed() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + 'image/gif', + filesize(__DIR__.'/Fixtures/test.gif') + ); + + $this->assertEquals(filesize(__DIR__.'/Fixtures/test.gif'), $file->getSize()); + } + + public function testGetExtension() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif' + ); + + $this->assertEquals('gif', $file->getExtension()); + } + + public function testIsValid() + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + UPLOAD_ERR_OK, + true + ); + + $this->assertTrue($file->isValid()); + } + + /** + * @dataProvider uploadedFileErrorProvider + */ + public function testIsInvalidOnUploadError($error) + { + $file = new UploadedFile( + __DIR__.'/Fixtures/test.gif', + 'original.gif', + null, + $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, + UPLOAD_ERR_OK + ); + + $this->assertFalse($file->isValid()); + } +} diff --git a/vendor/symfony/http-foundation/Tests/FileBagTest.php b/vendor/symfony/http-foundation/Tests/FileBagTest.php new file mode 100644 index 0000000..06136e2 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/FileBagTest.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\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'); + + $bag = new FileBag(array('file' => array( + 'name' => basename($tmpFile), + 'type' => 'text/plain', + 'tmp_name' => $tmpFile, + 'error' => 0, + 'size' => null, + ))); + + $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'); + + $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' => null, + ), + ), + )); + + $files = $bag->all(); + $this->assertEquals($file, $files['child']['file']); + } + + public function testShouldConvertNestedUploadedFilesWithPhpBug() + { + $tmpFile = $this->createTempFile(); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain'); + + $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' => null), + ), + ), + )); + + $files = $bag->all(); + $this->assertEquals($file, $files['child']['sub']['file']); + } + + public function testShouldNotConvertNestedUploadedFiles() + { + $tmpFile = $this->createTempFile(); + $file = new UploadedFile($tmpFile, basename($tmpFile), 'text/plain'); + $bag = new FileBag(array('image' => array('file' => $file))); + + $files = $bag->all(); + $this->assertEquals($file, $files['image']['file']); + } + + protected function createTempFile() + { + $tempFile = tempnam(sys_get_temp_dir().'/form_test', 'FormTest'); + file_put_contents($tempFile, '1'); + + return $tempFile; + } + + protected function setUp() + { + mkdir(sys_get_temp_dir().'/form_test', 0777, true); + } + + protected function tearDown() + { + foreach (glob(sys_get_temp_dir().'/form_test/*') as $file) { + unlink($file); + } + + rmdir(sys_get_temp_dir().'/form_test'); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc new file mode 100644 index 0000000..f9c40a9 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/common.inc @@ -0,0 +1,43 @@ +headers->set('Date', 'Sat, 12 Nov 1955 20:04:00 GMT'); + +return $r; diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected new file mode 100644 index 0000000..bdb9d02 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.expected @@ -0,0 +1,11 @@ + +Warning: Expiry date cannot have a year greater than 9999 in %scookie_max_age.php on line 10 + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: foo=bar; expires=Sat, 01-Jan-10000 02:46:40 GMT; Max-Age=%d; path=/ +) +shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php new file mode 100644 index 0000000..8775a5c --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_max_age.php @@ -0,0 +1,10 @@ +headers->setCookie(new Cookie('foo', 'bar', 253402310800, '', null, false, false)); +$r->sendHeaders(); + +setcookie('foo2', 'bar', 253402310800, '/'); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected new file mode 100644 index 0000000..0c09797 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.expected @@ -0,0 +1,10 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/ + [4] => Set-Cookie: ?*():@&+$/%#[]=?*():@&+$/%#[]; path=/ +) +shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php new file mode 100644 index 0000000..2ca5b59 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_raw_urlencode.php @@ -0,0 +1,12 @@ +headers->setCookie(new Cookie($str, $str, 0, '/', null, false, false, true)); +$r->sendHeaders(); + +setrawcookie($str, $str, 0, '/', null, false, false); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected new file mode 100644 index 0000000..cbde2cb --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.expected @@ -0,0 +1,9 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: CookieSamesiteLaxTest=LaxValue; path=/; httponly; samesite=lax +) +shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php new file mode 100644 index 0000000..9a476f1 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_lax.php @@ -0,0 +1,8 @@ +headers->setCookie(new Cookie('CookieSamesiteLaxTest', 'LaxValue', 0, '/', null, false, true, false, Cookie::SAMESITE_LAX)); +$r->sendHeaders(); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected new file mode 100644 index 0000000..adc491f --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.expected @@ -0,0 +1,9 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: CookieSamesiteStrictTest=StrictValue; path=/; httponly; samesite=strict +) +shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php new file mode 100644 index 0000000..3bcb41f --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_samesite_strict.php @@ -0,0 +1,8 @@ +headers->setCookie(new Cookie('CookieSamesiteStrictTest', 'StrictValue', 0, '/', null, false, true, false, Cookie::SAMESITE_STRICT)); +$r->sendHeaders(); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected new file mode 100644 index 0000000..14e44a3 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.expected @@ -0,0 +1,10 @@ + +Array +( + [0] => Content-Type: text/plain; charset=utf-8 + [1] => Cache-Control: no-cache, private + [2] => Date: Sat, 12 Nov 1955 20:04:00 GMT + [3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ + [4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/ +) +shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php new file mode 100644 index 0000000..05b9af3 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/cookie_urlencode.php @@ -0,0 +1,12 @@ +headers->setCookie(new Cookie($str, $str, 0, '', null, false, false)); +$r->sendHeaders(); + +setcookie($str, $str, 0, '/'); diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected new file mode 100644 index 0000000..2b560f0 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.expected @@ -0,0 +1,6 @@ +The cookie name "Hello + world" contains invalid characters. +Array +( + [0] => Content-Type: text/plain; charset=utf-8 +) +shutdown diff --git a/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php new file mode 100644 index 0000000..3fe1571 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Fixtures/response-functional/invalid_cookie_name.php @@ -0,0 +1,11 @@ +headers->setCookie(new Cookie('Hello + world', 'hodor')); +} catch (\InvalidArgumentException $e) { + echo $e->getMessage(); +} diff --git a/vendor/symfony/http-foundation/Tests/HeaderBagTest.php b/vendor/symfony/http-foundation/Tests/HeaderBagTest.php new file mode 100644 index 0000000..c5a437f --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/HeaderUtilsTest.php b/vendor/symfony/http-foundation/Tests/HeaderUtilsTest.php new file mode 100644 index 0000000..2f5fdc2 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/HeaderUtilsTest.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\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\HeaderUtils; + +class HeaderUtilsTest extends TestCase +{ + public function testSplit() + { + $this->assertSame(array('foo=123', 'bar'), HeaderUtils::split('foo=123,bar', ',')); + $this->assertSame(array('foo=123', 'bar'), HeaderUtils::split('foo=123, bar', ',')); + $this->assertSame(array(array('foo=123', 'bar')), HeaderUtils::split('foo=123; bar', ',;')); + $this->assertSame(array(array('foo=123'), array('bar')), HeaderUtils::split('foo=123, bar', ',;')); + $this->assertSame(array('foo', '123, bar'), HeaderUtils::split('foo=123, bar', '=')); + $this->assertSame(array('foo', '123, bar'), HeaderUtils::split(' foo = 123, bar ', '=')); + $this->assertSame(array(array('foo', '123'), array('bar')), HeaderUtils::split('foo=123, bar', ',=')); + $this->assertSame(array(array(array('foo', '123')), array(array('bar'), array('foo', '456'))), HeaderUtils::split('foo=123, bar; foo=456', ',;=')); + $this->assertSame(array(array(array('foo', 'a,b;c=d'))), HeaderUtils::split('foo="a,b;c=d"', ',;=')); + + $this->assertSame(array('foo', 'bar'), HeaderUtils::split('foo,,,, bar', ',')); + $this->assertSame(array('foo', 'bar'), HeaderUtils::split(',foo, bar,', ',')); + $this->assertSame(array('foo', 'bar'), HeaderUtils::split(' , foo, bar, ', ',')); + $this->assertSame(array('foo bar'), HeaderUtils::split('foo "bar"', ',')); + $this->assertSame(array('foo bar'), HeaderUtils::split('"foo" bar', ',')); + $this->assertSame(array('foo bar'), HeaderUtils::split('"foo" "bar"', ',')); + + // These are not a valid header values. We test that they parse anyway, + // and that both the valid and invalid parts are returned. + $this->assertSame(array(), HeaderUtils::split('', ',')); + $this->assertSame(array(), HeaderUtils::split(',,,', ',')); + $this->assertSame(array('foo', 'bar', 'baz'), HeaderUtils::split('foo, "bar", "baz', ',')); + $this->assertSame(array('foo', 'bar, baz'), HeaderUtils::split('foo, "bar, baz', ',')); + $this->assertSame(array('foo', 'bar, baz\\'), HeaderUtils::split('foo, "bar, baz\\', ',')); + $this->assertSame(array('foo', 'bar, baz\\'), HeaderUtils::split('foo, "bar, baz\\\\', ',')); + } + + public function testCombine() + { + $this->assertSame(array('foo' => '123'), HeaderUtils::combine(array(array('foo', '123')))); + $this->assertSame(array('foo' => true), HeaderUtils::combine(array(array('foo')))); + $this->assertSame(array('foo' => true), HeaderUtils::combine(array(array('Foo')))); + $this->assertSame(array('foo' => '123', 'bar' => true), HeaderUtils::combine(array(array('foo', '123'), array('bar')))); + } + + public function testToString() + { + $this->assertSame('foo', HeaderUtils::toString(array('foo' => true), ',')); + $this->assertSame('foo; bar', HeaderUtils::toString(array('foo' => true, 'bar' => true), ';')); + $this->assertSame('foo=123', HeaderUtils::toString(array('foo' => '123'), ',')); + $this->assertSame('foo="1 2 3"', HeaderUtils::toString(array('foo' => '1 2 3'), ',')); + $this->assertSame('foo="1 2 3", bar', HeaderUtils::toString(array('foo' => '1 2 3', 'bar' => true), ',')); + } + + public function testQuote() + { + $this->assertSame('foo', HeaderUtils::quote('foo')); + $this->assertSame('az09!#$%&\'*.^_`|~-', HeaderUtils::quote('az09!#$%&\'*.^_`|~-')); + $this->assertSame('"foo bar"', HeaderUtils::quote('foo bar')); + $this->assertSame('"foo [bar]"', HeaderUtils::quote('foo [bar]')); + $this->assertSame('"foo \"bar\""', HeaderUtils::quote('foo "bar"')); + $this->assertSame('"foo \\\\ bar"', HeaderUtils::quote('foo \\ bar')); + } + + public function testUnquote() + { + $this->assertEquals('foo', HeaderUtils::unquote('foo')); + $this->assertEquals('az09!#$%&\'*.^_`|~-', HeaderUtils::unquote('az09!#$%&\'*.^_`|~-')); + $this->assertEquals('foo bar', HeaderUtils::unquote('"foo bar"')); + $this->assertEquals('foo [bar]', HeaderUtils::unquote('"foo [bar]"')); + $this->assertEquals('foo "bar"', HeaderUtils::unquote('"foo \"bar\""')); + $this->assertEquals('foo "bar"', HeaderUtils::unquote('"foo \"\b\a\r\""')); + $this->assertEquals('foo \\ bar', HeaderUtils::unquote('"foo \\\\ bar"')); + } +} diff --git a/vendor/symfony/http-foundation/Tests/IpUtilsTest.php b/vendor/symfony/http-foundation/Tests/IpUtilsTest.php new file mode 100644 index 0000000..232a204 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/JsonResponseTest.php b/vendor/symfony/http-foundation/Tests/JsonResponseTest.php new file mode 100644 index 0000000..201839f --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/ParameterBagTest.php b/vendor/symfony/http-foundation/Tests/ParameterBagTest.php new file mode 100644 index 0000000..dccfd4f --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php b/vendor/symfony/http-foundation/Tests/RedirectResponseTest.php new file mode 100644 index 0000000..d389e83 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php b/vendor/symfony/http-foundation/Tests/RequestMatcherTest.php new file mode 100644 index 0000000..10d764a --- /dev/null +++ b/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\Request; +use Symfony\Component\HttpFoundation\RequestMatcher; + +class RequestMatcherTest extends TestCase +{ + /** + * @dataProvider getMethodData + */ + public function testMethod($requestMethod, $matcherMethod, $isMatch) + { + $matcher = new RequestMatcher(); + $matcher->matchMethod($matcherMethod); + $request = Request::create('', $requestMethod); + $this->assertSame($isMatch, $matcher->matches($request)); + + $matcher = new RequestMatcher(null, null, $matcherMethod); + $request = Request::create('', $requestMethod); + $this->assertSame($isMatch, $matcher->matches($request)); + } + + public function getMethodData() + { + return array( + array('get', 'get', true), + array('get', array('get', 'post'), true), + array('get', 'post', false), + array('get', 'GET', true), + array('get', array('GET', 'POST'), true), + array('get', 'POST', false), + ); + } + + public function testScheme() + { + $httpRequest = $request = $request = Request::create(''); + $httpsRequest = $request = $request = Request::create('', 'get', array(), array(), array(), array('HTTPS' => 'on')); + + $matcher = new RequestMatcher(); + $matcher->matchScheme('https'); + $this->assertFalse($matcher->matches($httpRequest)); + $this->assertTrue($matcher->matches($httpsRequest)); + + $matcher->matchScheme('http'); + $this->assertFalse($matcher->matches($httpsRequest)); + $this->assertTrue($matcher->matches($httpRequest)); + + $matcher = new RequestMatcher(); + $this->assertTrue($matcher->matches($httpsRequest)); + $this->assertTrue($matcher->matches($httpRequest)); + } + + /** + * @dataProvider getHostData + */ + public function testHost($pattern, $isMatch) + { + $matcher = new RequestMatcher(); + $request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com')); + + $matcher->matchHost($pattern); + $this->assertSame($isMatch, $matcher->matches($request)); + + $matcher = new RequestMatcher(null, $pattern); + $this->assertSame($isMatch, $matcher->matches($request)); + } + + public function getHostData() + { + return array( + array('.*\.example\.com', true), + array('\.example\.com$', true), + array('^.*\.example\.com$', true), + array('.*\.sensio\.com', false), + array('.*\.example\.COM', true), + array('\.example\.COM$', true), + array('^.*\.example\.COM$', true), + array('.*\.sensio\.COM', false), + ); + } + + public function testPath() + { + $matcher = new RequestMatcher(); + + $request = Request::create('/admin/foo'); + + $matcher->matchPath('/admin/.*'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchPath('/admin'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchPath('^/admin/.*$'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchMethod('/blog/.*'); + $this->assertFalse($matcher->matches($request)); + } + + public function testPathWithLocaleIsNotSupported() + { + $matcher = new RequestMatcher(); + $request = Request::create('/en/login'); + $request->setLocale('en'); + + $matcher->matchPath('^/{_locale}/login$'); + $this->assertFalse($matcher->matches($request)); + } + + public function testPathWithEncodedCharacters() + { + $matcher = new RequestMatcher(); + $request = Request::create('/admin/fo%20o'); + $matcher->matchPath('^/admin/fo o*$'); + $this->assertTrue($matcher->matches($request)); + } + + public function testAttributes() + { + $matcher = new RequestMatcher(); + + $request = Request::create('/admin/foo'); + $request->attributes->set('foo', 'foo_bar'); + + $matcher->matchAttribute('foo', 'foo_.*'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchAttribute('foo', 'foo'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchAttribute('foo', '^foo_bar$'); + $this->assertTrue($matcher->matches($request)); + + $matcher->matchAttribute('foo', 'babar'); + $this->assertFalse($matcher->matches($request)); + } +} diff --git a/vendor/symfony/http-foundation/Tests/RequestStackTest.php b/vendor/symfony/http-foundation/Tests/RequestStackTest.php new file mode 100644 index 0000000..a84fb26 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/RequestTest.php b/vendor/symfony/http-foundation/Tests/RequestTest.php new file mode 100644 index 0000000..52392ad --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/RequestTest.php @@ -0,0 +1,2221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; + +class RequestTest extends TestCase +{ + protected function tearDown() + { + Request::setTrustedProxies(array(), -1); + Request::setTrustedHosts(array()); + } + + public function testInitialize() + { + $request = new Request(); + + $request->initialize(array('foo' => 'bar')); + $this->assertEquals('bar', $request->query->get('foo'), '->initialize() takes an array of query parameters as its first argument'); + + $request->initialize(array(), array('foo' => 'bar')); + $this->assertEquals('bar', $request->request->get('foo'), '->initialize() takes an array of request parameters as its second argument'); + + $request->initialize(array(), array(), array('foo' => 'bar')); + $this->assertEquals('bar', $request->attributes->get('foo'), '->initialize() takes an array of attributes as its third argument'); + + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_FOO' => 'bar')); + $this->assertEquals('bar', $request->headers->get('FOO'), '->initialize() takes an array of HTTP headers as its sixth argument'); + } + + public function testGetLocale() + { + $request = new Request(); + $request->setLocale('pl'); + $locale = $request->getLocale(); + $this->assertEquals('pl', $locale); + } + + public function testGetUser() + { + $request = Request::create('http://user:password@test.com'); + $user = $request->getUser(); + + $this->assertEquals('user', $user); + } + + public function testGetPassword() + { + $request = Request::create('http://user:password@test.com'); + $password = $request->getPassword(); + + $this->assertEquals('password', $password); + } + + public function testIsNoCache() + { + $request = new Request(); + $isNoCache = $request->isNoCache(); + + $this->assertFalse($isNoCache); + } + + public function testGetContentType() + { + $request = new Request(); + $contentType = $request->getContentType(); + + $this->assertNull($contentType); + } + + public function testSetDefaultLocale() + { + $request = new Request(); + $request->setDefaultLocale('pl'); + $locale = $request->getLocale(); + + $this->assertEquals('pl', $locale); + } + + public function testCreate() + { + $request = Request::create('http://test.com/foo?bar=baz'); + $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com/foo', 'GET', array('bar' => 'baz')); + $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz')); + $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('https://test.com/foo?bar=baz'); + $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('bar=baz', $request->getQueryString()); + $this->assertEquals(443, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('test.com:90/foo'); + $this->assertEquals('http://test.com:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('test.com', $request->getHost()); + $this->assertEquals('test.com:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('https://test.com:90/foo'); + $this->assertEquals('https://test.com:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('test.com', $request->getHost()); + $this->assertEquals('test.com:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://127.0.0.1:90/foo'); + $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('127.0.0.1', $request->getHost()); + $this->assertEquals('127.0.0.1:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://[::1]:90/foo'); + $this->assertEquals('https://[::1]:90/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('[::1]', $request->getHost()); + $this->assertEquals('[::1]:90', $request->getHttpHost()); + $this->assertEquals(90, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $request = Request::create('https://[::1]/foo'); + $this->assertEquals('https://[::1]/foo', $request->getUri()); + $this->assertEquals('/foo', $request->getPathInfo()); + $this->assertEquals('[::1]', $request->getHost()); + $this->assertEquals('[::1]', $request->getHttpHost()); + $this->assertEquals(443, $request->getPort()); + $this->assertTrue($request->isSecure()); + + $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}'; + $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json); + $this->assertEquals($json, $request->getContent()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com'); + $this->assertEquals('http://test.com/', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com?test=1'); + $this->assertEquals('http://test.com/?test=1', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('test=1', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com:90/?test=1'); + $this->assertEquals('http://test.com:90/?test=1', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('test=1', $request->getQueryString()); + $this->assertEquals(90, $request->getPort()); + $this->assertEquals('test.com:90', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://username:password@test.com'); + $this->assertEquals('http://test.com/', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertEquals('username', $request->getUser()); + $this->assertEquals('password', $request->getPassword()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://username@test.com'); + $this->assertEquals('http://test.com/', $request->getUri()); + $this->assertEquals('/', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertEquals('username', $request->getUser()); + $this->assertSame('', $request->getPassword()); + $this->assertFalse($request->isSecure()); + + $request = Request::create('http://test.com/?foo'); + $this->assertEquals('/?foo', $request->getRequestUri()); + $this->assertEquals(array('foo' => ''), $request->query->all()); + + // assume rewrite rule: (.*) --> app/app.php; app/ is a symlink to a symfony web/ directory + $request = Request::create('http://test.com/apparthotel-1234', 'GET', array(), array(), array(), + array( + 'DOCUMENT_ROOT' => '/var/www/www.test.com', + 'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php', + 'SCRIPT_NAME' => '/app/app.php', + 'PHP_SELF' => '/app/app.php/apparthotel-1234', + )); + $this->assertEquals('http://test.com/apparthotel-1234', $request->getUri()); + $this->assertEquals('/apparthotel-1234', $request->getPathInfo()); + $this->assertEquals('', $request->getQueryString()); + $this->assertEquals(80, $request->getPort()); + $this->assertEquals('test.com', $request->getHttpHost()); + $this->assertFalse($request->isSecure()); + } + + 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%5B0%5D=1&foo%5B1%5D=2', 'allows array notation'), + array('foo=1&foo=2', 'foo=2', 'merges 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_Doe=&John_Doe=', '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'), + + // Don't reorder nested query string keys + array('foo[]=Z&foo[]=A', 'foo%5B0%5D=Z&foo%5B1%5D=A', 'keeps order of values'), + array('foo[Z]=B&foo[A]=B', 'foo%5BZ%5D=B&foo%5BA%5D=B', 'keeps order of keys'), + + array('utf8=✓', 'utf8=%E2%9C%93', 'encodes UTF-8'), + ); + } + + public function testGetQueryStringReturnsNull() + { + $request = new Request(); + + $this->assertNull($request->getQueryString(), '->getQueryString() returns null for non-existent query string'); + + $request->server->set('QUERY_STRING', ''); + $this->assertNull($request->getQueryString(), '->getQueryString() returns null for empty query string'); + } + + public function testGetHost() + { + $request = new Request(); + + $request->initialize(array('foo' => 'bar')); + $this->assertEquals('', $request->getHost(), '->getHost() return empty string if not initialized'); + + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com')); + $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header'); + + // Host header with port number + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com:8080')); + $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header with port number'); + + // Server values + $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com')); + $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from server name'); + + $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com', 'HTTP_HOST' => 'www.host.com')); + $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME '); + } + + public function testGetPort() + { + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https', + 'HTTP_X_FORWARDED_PORT' => '443', + )); + $port = $request->getPort(); + + $this->assertEquals(80, $port, 'Without trusted proxies FORWARDED_PROTO and FORWARDED_PORT are ignored.'); + + Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL); + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https', + 'HTTP_X_FORWARDED_PORT' => '8443', + )); + $this->assertEquals(80, $request->getPort(), 'With PROTO and PORT on untrusted connection server value takes precedence.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(8443, $request->getPort(), 'With PROTO and PORT set PORT takes precedence.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'https', + )); + $this->assertEquals(80, $request->getPort(), 'With only PROTO set getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(443, $request->getPort(), 'With only PROTO set getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'http', + )); + $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() returns port of the original request.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'On', + )); + $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is On, getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is On, getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => '1', + )); + $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is 1, getPort() ignores trusted headers on untrusted connection.'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is 1, getPort() defaults to 443.'); + + $request = Request::create('http://example.com', 'GET', array(), array(), array(), array( + 'HTTP_X_FORWARDED_PROTO' => 'something-else', + )); + $port = $request->getPort(); + $this->assertEquals(80, $port, 'With only PROTO set and value is not recognized, getPort() defaults to 80.'); + } + + /** + * @expectedException \RuntimeException + */ + public function testGetHostWithFakeHttpHostValue() + { + $request = new Request(); + $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.host.com?query=string')); + $request->getHost(); + } + + public function testGetSetMethod() + { + $request = new Request(); + + $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns GET if no method is defined'); + + $request->setMethod('get'); + $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns an uppercased string'); + + $request->setMethod('PURGE'); + $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method even if it is not a standard one'); + + $request->setMethod('POST'); + $this->assertEquals('POST', $request->getMethod(), '->getMethod() returns the method POST if no _method is defined'); + + $request->setMethod('POST'); + $request->request->set('_method', 'purge'); + $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled'); + + $request = new Request(); + $request->setMethod('POST'); + $request->request->set('_method', 'purge'); + + $this->assertFalse(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be disabled by default'); + + Request::enableHttpMethodParameterOverride(); + + $this->assertTrue(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be enabled now but it is not'); + + $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); + $this->disableHttpMethodParameterOverride(); + + $request = new Request(); + $request->setMethod('POST'); + $request->query->set('_method', 'purge'); + $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled'); + + $request = new Request(); + $request->setMethod('POST'); + $request->query->set('_method', 'purge'); + Request::enableHttpMethodParameterOverride(); + $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST'); + $this->disableHttpMethodParameterOverride(); + + $request = new Request(); + $request->setMethod('POST'); + $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete'); + $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override even though _method is set if defined and POST'); + + $request = new Request(); + $request->setMethod('POST'); + $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete'); + $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override if defined and POST'); + + $request = new Request(); + $request->setMethod('POST'); + $request->query->set('_method', array('delete', 'patch')); + $this->assertSame('POST', $request->getMethod(), '->getMethod() returns the request method if invalid type is defined in query'); + } + + /** + * @dataProvider getClientIpsProvider + */ + public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies); + + $this->assertEquals($expected[0], $request->getClientIp()); + } + + /** + * @dataProvider getClientIpsProvider + */ + public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies); + + $this->assertEquals($expected, $request->getClientIps()); + } + + /** + * @dataProvider getClientIpsForwardedProvider + */ + public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies) + { + $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies); + + $this->assertEquals($expected, $request->getClientIps()); + } + + public function getClientIpsForwardedProvider() + { + // $expected $remoteAddr $httpForwarded $trustedProxies + return array( + array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', null), + array(array('127.0.0.1'), '127.0.0.1', 'for="_gazonk"', array('127.0.0.1')), + array(array('88.88.88.88'), '127.0.0.1', 'for="88.88.88.88:80"', array('127.0.0.1')), + array(array('192.0.2.60'), '::1', 'for=192.0.2.60;proto=http;by=203.0.113.43', array('::1')), + array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1', 'for=192.0.2.43, for="[2620:0:1cfe:face:b00c::3]"', array('::1')), + array(array('2001:db8:cafe::17'), '::1', 'for="[2001:db8:cafe::17]:4711', array('::1')), + ); + } + + public function getClientIpsProvider() + { + // $expected $remoteAddr $httpForwardedFor $trustedProxies + return array( + // simple IPv4 + array(array('88.88.88.88'), '88.88.88.88', null, null), + // trust the IPv4 remote addr + array(array('88.88.88.88'), '88.88.88.88', null, array('88.88.88.88')), + + // simple IPv6 + array(array('::1'), '::1', null, null), + // trust the IPv6 remote addr + array(array('::1'), '::1', null, array('::1')), + + // forwarded for with remote IPv4 addr not trusted + array(array('127.0.0.1'), '127.0.0.1', '88.88.88.88', null), + // forwarded for with remote IPv4 addr trusted + comma + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88,', array('127.0.0.1')), + // forwarded for with remote IPv4 and all FF addrs trusted + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88', array('127.0.0.1', '88.88.88.88')), + // forwarded for with remote IPv4 range trusted + array(array('88.88.88.88'), '123.45.67.89', '88.88.88.88', array('123.45.67.0/24')), + + // forwarded for with remote IPv6 addr not trusted + array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', null), + // forwarded for with remote IPv6 addr trusted + array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')), + // forwarded for with remote IPv6 range trusted + array(array('88.88.88.88'), '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88', array('2a01:198:603:0::/65')), + + // multiple forwarded for with remote IPv4 addr trusted + array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')), + // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted + array(array('87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')), + // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle + array(array('88.88.88.88', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21')), + // multiple forwarded for with remote IPv4 addr and all reverse proxies trusted + array(array('127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21', '88.88.88.88', '127.0.0.1')), + + // multiple forwarded for with remote IPv6 addr trusted + array(array('2620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')), + // multiple forwarded for with remote IPv6 addr and some reverse proxies trusted + array(array('3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3')), + // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle + array(array('2620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3,3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3')), + + // client IP with port + array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88:12345, 127.0.0.1', array('127.0.0.1')), + + // invalid forwarded IP is ignored + array(array('88.88.88.88'), '127.0.0.1', 'unknown,88.88.88.88', array('127.0.0.1')), + array(array('88.88.88.88'), '127.0.0.1', '}__test|O:21:"JDatabaseDriverMysqli":3:{s:2,88.88.88.88', array('127.0.0.1')), + ); + } + + /** + * @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException + * @dataProvider getClientIpsWithConflictingHeadersProvider + */ + public function testGetClientIpsWithConflictingHeaders($httpForwarded, $httpXForwardedFor) + { + $request = new Request(); + + $server = array( + 'REMOTE_ADDR' => '88.88.88.88', + 'HTTP_FORWARDED' => $httpForwarded, + 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, + ); + + Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_ALL | Request::HEADER_FORWARDED); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $request->getClientIps(); + } + + /** + * @dataProvider getClientIpsWithConflictingHeadersProvider + */ + public function testGetClientIpsOnlyXHttpForwardedForTrusted($httpForwarded, $httpXForwardedFor) + { + $request = new Request(); + + $server = array( + 'REMOTE_ADDR' => '88.88.88.88', + 'HTTP_FORWARDED' => $httpForwarded, + 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, + ); + + Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_FOR); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $this->assertSame(array_reverse(explode(',', $httpXForwardedFor)), $request->getClientIps()); + } + + public function getClientIpsWithConflictingHeadersProvider() + { + // $httpForwarded $httpXForwardedFor + return array( + array('for=87.65.43.21', '192.0.2.60'), + array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60'), + array('for=192.0.2.60', '192.0.2.60,87.65.43.21'), + array('for="::face", for=192.0.2.60', '192.0.2.60,192.0.2.43'), + array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60,87.65.43.21'), + ); + } + + /** + * @dataProvider getClientIpsWithAgreeingHeadersProvider + */ + public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwardedFor, $expectedIps) + { + $request = new Request(); + + $server = array( + 'REMOTE_ADDR' => '88.88.88.88', + 'HTTP_FORWARDED' => $httpForwarded, + 'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor, + ); + + Request::setTrustedProxies(array('88.88.88.88'), -1); + + $request->initialize(array(), array(), array(), array(), array(), $server); + + $clientIps = $request->getClientIps(); + + $this->assertSame($expectedIps, $clientIps); + } + + public function getClientIpsWithAgreeingHeadersProvider() + { + // $httpForwarded $httpXForwardedFor + return array( + array('for="192.0.2.60"', '192.0.2.60', array('192.0.2.60')), + array('for=192.0.2.60, for=87.65.43.21', '192.0.2.60,87.65.43.21', array('87.65.43.21', '192.0.2.60')), + array('for="[::face]", for=192.0.2.60', '::face,192.0.2.60', array('192.0.2.60', '::face')), + array('for="192.0.2.60:80"', '192.0.2.60', array('192.0.2.60')), + array('for=192.0.2.60;proto=http;by=203.0.113.43', '192.0.2.60', array('192.0.2.60')), + array('for="[2001:db8:cafe::17]:4711"', '2001:db8:cafe::17', array('2001:db8:cafe::17')), + ); + } + + public function testGetContentWorksTwiceInDefaultMode() + { + $req = new Request(); + $this->assertEquals('', $req->getContent()); + $this->assertEquals('', $req->getContent()); + } + + public function testGetContentReturnsResource() + { + $req = new Request(); + $retval = $req->getContent(true); + $this->assertInternalType('resource', $retval); + $this->assertEquals('', fread($retval, 1)); + $this->assertTrue(feof($retval)); + } + + public function testGetContentReturnsResourceWhenContentSetInConstructor() + { + $req = new Request(array(), array(), array(), array(), array(), array(), 'MyContent'); + $resource = $req->getContent(true); + + $this->assertInternalType('resource', $resource); + $this->assertEquals('MyContent', stream_get_contents($resource)); + } + + public function testContentAsResource() + { + $resource = fopen('php://memory', 'r+'); + fwrite($resource, 'My other content'); + rewind($resource); + + $req = new Request(array(), array(), array(), array(), array(), array(), $resource); + $this->assertEquals('My other content', stream_get_contents($req->getContent(true))); + $this->assertEquals('My other content', $req->getContent()); + } + + public function getContentCantBeCalledTwiceWithResourcesProvider() + { + return array( + 'Resource then fetch' => array(true, false), + 'Resource then resource' => array(true, true), + ); + } + + /** + * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider + */ + 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); + } + + /** + * @group legacy + * @expectedDeprecation Calling "Symfony\Component\HttpFoundation\Request::getSession()" when no session has been set is deprecated since Symfony 4.1 and will throw an exception in 5.0. Use "hasSession()" instead. + */ + public function testGetSessionNullable() + { + (new Request())->getSession(); + } + + 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()); + } + + 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()); + } + + /** + * @dataProvider iisRequestUriProvider + */ + public function testIISRequestUri($headers, $server, $expectedRequestUri) + { + $request = new Request(); + $request->headers->replace($headers); + $request->server->replace($server); + + $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct'); + + $subRequestUri = '/bar/foo'; + $subRequest = Request::create($subRequestUri, 'get', array(), array(), array(), $request->server->all()); + $this->assertEquals($subRequestUri, $subRequest->getRequestUri(), '->getRequestUri() is correct in sub request'); + } + + public function iisRequestUriProvider() + { + return array( + array( + array(), + array( + 'IIS_WasUrlRewritten' => '1', + 'UNENCODED_URL' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array(), + array( + 'ORIG_PATH_INFO' => '/foo/bar', + ), + '/foo/bar', + ), + array( + array(), + array( + 'ORIG_PATH_INFO' => '/foo/bar', + 'QUERY_STRING' => 'foo=bar', + ), + '/foo/bar?foo=bar', + ), + ); + } + + public function testTrustedHosts() + { + // create a request + $request = Request::create('/'); + + // no trusted host set -> no host check + $request->headers->set('host', 'evil.com'); + $this->assertEquals('evil.com', $request->getHost()); + + // add a trusted domain and all its subdomains + Request::setTrustedHosts(array('^([a-z]{9}\.)?trusted\.com$')); + + // untrusted host + $request->headers->set('host', 'evil.com'); + try { + $request->getHost(); + $this->fail('Request::getHost() should throw an exception when host is not trusted.'); + } catch (SuspiciousOperationException $e) { + $this->assertEquals('Untrusted Host "evil.com".', $e->getMessage()); + } + + // trusted hosts + $request->headers->set('host', 'trusted.com'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(80, $request->getPort()); + + $request->server->set('HTTPS', true); + $request->headers->set('host', 'trusted.com'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(443, $request->getPort()); + $request->server->set('HTTPS', false); + + $request->headers->set('host', 'trusted.com:8000'); + $this->assertEquals('trusted.com', $request->getHost()); + $this->assertEquals(8000, $request->getPort()); + + $request->headers->set('host', 'subdomain.trusted.com'); + $this->assertEquals('subdomain.trusted.com', $request->getHost()); + } + + public function testSetTrustedHostsDoesNotBreakOnSpecialCharacters() + { + Request::setTrustedHosts(array('localhost(\.local){0,1}#,example.com', 'localhost')); + + $request = Request::create('/'); + $request->headers->set('host', 'localhost'); + $this->assertSame('localhost', $request->getHost()); + } + + public function testFactory() + { + Request::setFactory(function (array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { + return new NewRequest(); + }); + + $this->assertEquals('foo', Request::create('/')->getFoo()); + + Request::setFactory(null); + } + + /** + * @dataProvider getLongHostNames + */ + public function testVeryLongHosts($host) + { + $start = microtime(true); + + $request = Request::create('/'); + $request->headers->set('host', $host); + $this->assertEquals($host, $request->getHost()); + $this->assertLessThan(5, microtime(true) - $start); + } + + /** + * @dataProvider getHostValidities + */ + public function testHostValidity($host, $isValid, $expectedHost = null, $expectedPort = null) + { + $request = Request::create('/'); + $request->headers->set('host', $host); + + if ($isValid) { + $this->assertSame($expectedHost ?: $host, $request->getHost()); + if ($expectedPort) { + $this->assertSame($expectedPort, $request->getPort()); + } + } else { + if (method_exists($this, 'expectException')) { + $this->expectException(SuspiciousOperationException::class); + $this->expectExceptionMessage('Invalid Host'); + } else { + $this->setExpectedException(SuspiciousOperationException::class, 'Invalid Host'); + } + + $request->getHost(); + } + } + + public function getHostValidities() + { + return array( + array('.a', false), + array('a..', false), + array('a.', true), + array("\xE9", false), + array('[::1]', true), + array('[::1]:80', true, '[::1]', 80), + array(str_repeat('.', 101), false), + ); + } + + public function getLongHostNames() + { + return array( + array('a'.str_repeat('.a', 40000)), + array(str_repeat(':', 101)), + ); + } + + /** + * @dataProvider methodIdempotentProvider + */ + public function testMethodIdempotent($method, $idempotent) + { + $request = new Request(); + $request->setMethod($method); + $this->assertEquals($idempotent, $request->isMethodIdempotent()); + } + + public function methodIdempotentProvider() + { + return array( + array('HEAD', true), + array('GET', true), + array('POST', false), + array('PUT', true), + array('PATCH', false), + array('DELETE', true), + array('PURGE', true), + array('OPTIONS', true), + array('TRACE', true), + array('CONNECT', false), + ); + } + + /** + * @dataProvider methodSafeProvider + */ + public function testMethodSafe($method, $safe) + { + $request = new Request(); + $request->setMethod($method); + $this->assertEquals($safe, $request->isMethodSafe(false)); + } + + public function methodSafeProvider() + { + return array( + array('HEAD', true), + array('GET', true), + array('POST', false), + array('PUT', false), + array('PATCH', false), + array('DELETE', false), + array('PURGE', false), + array('OPTIONS', true), + array('TRACE', true), + array('CONNECT', false), + ); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testMethodSafeChecksCacheable() + { + $request = new Request(); + $request->setMethod('OPTIONS'); + $request->isMethodSafe(); + } + + /** + * @dataProvider methodCacheableProvider + */ + public function testMethodCacheable($method, $cacheable) + { + $request = new Request(); + $request->setMethod($method); + $this->assertEquals($cacheable, $request->isMethodCacheable()); + } + + public function methodCacheableProvider() + { + return array( + array('HEAD', true), + array('GET', true), + array('POST', false), + array('PUT', false), + array('PATCH', false), + array('DELETE', false), + array('PURGE', false), + array('OPTIONS', false), + array('TRACE', false), + array('CONNECT', false), + ); + } + + /** + * @dataProvider protocolVersionProvider + */ + public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expected) + { + if ($trustedProxy) { + Request::setTrustedProxies(array('1.1.1.1'), -1); + } + + $request = new Request(); + $request->server->set('SERVER_PROTOCOL', $serverProtocol); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Via', $via); + + $this->assertSame($expected, $request->getProtocolVersion()); + } + + public function protocolVersionProvider() + { + return array( + 'untrusted without via' => array('HTTP/2.0', false, '', 'HTTP/2.0'), + 'untrusted with via' => array('HTTP/2.0', false, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/2.0'), + 'trusted without via' => array('HTTP/2.0', true, '', 'HTTP/2.0'), + 'trusted with via' => array('HTTP/2.0', true, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'), + 'trusted with via and protocol name' => array('HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'), + 'trusted with broken via' => array('HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'), + 'trusted with partially-broken via' => array('HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'), + ); + } + + public function nonstandardRequestsData() + { + return array( + array('', '', '/', 'http://host:8080/', ''), + array('/', '', '/', 'http://host:8080/', ''), + + array('hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), + array('/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'), + + array('', 'a=b', '/', 'http://host:8080/?a=b'), + array('?a=b', 'a=b', '/', 'http://host:8080/?a=b'), + array('/?a=b', 'a=b', '/', 'http://host:8080/?a=b'), + + array('x', 'a=b', '/x', 'http://host:8080/x?a=b'), + array('x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), + array('/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'), + + array('hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), + array('/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'), + + array('hello/app.php/x', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + array('hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + array('/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'), + ); + } + + /** + * @dataProvider nonstandardRequestsData + */ + public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null) + { + if (null === $expectedBaseUrl) { + $expectedBaseUrl = $expectedBasePath; + } + + $server = array( + 'HTTP_HOST' => 'host:8080', + 'SERVER_PORT' => '8080', + 'QUERY_STRING' => $queryString, + 'PHP_SELF' => '/hello/app.php', + 'SCRIPT_FILENAME' => '/some/path/app.php', + 'REQUEST_URI' => $requestUri, + ); + + $request = new Request(array(), array(), array(), array(), array(), $server); + + $this->assertEquals($expectedPathInfo, $request->getPathInfo()); + $this->assertEquals($expectedUri, $request->getUri()); + $this->assertEquals($queryString, $request->getQueryString()); + $this->assertEquals(8080, $request->getPort()); + $this->assertEquals('host:8080', $request->getHttpHost()); + $this->assertEquals($expectedBaseUrl, $request->getBaseUrl()); + $this->assertEquals($expectedBasePath, $request->getBasePath()); + } + + public function testTrustedHost() + { + Request::setTrustedProxies(array('1.1.1.1'), -1); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host=localhost:8080'); + $request->headers->set('X-Forwarded-Host', 'localhost:8080'); + + $this->assertSame('localhost:8080', $request->getHttpHost()); + $this->assertSame(8080, $request->getPort()); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host="[::1]:443"'); + $request->headers->set('X-Forwarded-Host', '[::1]:443'); + $request->headers->set('X-Forwarded-Port', 443); + + $this->assertSame('[::1]:443', $request->getHttpHost()); + $this->assertSame(443, $request->getPort()); + } + + public function testTrustedPort() + { + Request::setTrustedProxies(array('1.1.1.1'), -1); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host=localhost:8080'); + $request->headers->set('X-Forwarded-Port', 8080); + + $this->assertSame(8080, $request->getPort()); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host=localhost'); + $request->headers->set('X-Forwarded-Port', 80); + + $this->assertSame(80, $request->getPort()); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '1.1.1.1'); + $request->headers->set('Forwarded', 'host="[::1]"'); + $request->headers->set('X-Forwarded-Proto', 'https'); + $request->headers->set('X-Forwarded-Port', 443); + + $this->assertSame(443, $request->getPort()); + } +} + +class RequestContentProxy extends Request +{ + public function getContent($asResource = false) + { + return http_build_query(array('_method' => 'PUT', 'content' => 'mycontent'), '', '&'); + } +} + +class NewRequest extends Request +{ + public function getFoo() + { + return 'foo'; + } +} diff --git a/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php b/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.php new file mode 100644 index 0000000..22f25e9 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/ResponseFunctionalTest.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\HttpFoundation\Tests; + +use PHPUnit\Framework\TestCase; + +/** + * @requires PHP 7.0 + */ +class ResponseFunctionalTest extends TestCase +{ + private static $server; + + public static function setUpBeforeClass() + { + $spec = array( + 1 => array('file', '/dev/null', 'w'), + 2 => array('file', '/dev/null', 'w'), + ); + if (!self::$server = @proc_open('exec php -S localhost:8054', $spec, $pipes, __DIR__.'/Fixtures/response-functional')) { + self::markTestSkipped('PHP server unable to start.'); + } + sleep(1); + } + + public static function tearDownAfterClass() + { + if (self::$server) { + proc_terminate(self::$server); + proc_close(self::$server); + } + } + + /** + * @dataProvider provideCookie + */ + public function testCookie($fixture) + { + $result = file_get_contents(sprintf('http://localhost:8054/%s.php', $fixture)); + $this->assertStringMatchesFormatFile(__DIR__.sprintf('/Fixtures/response-functional/%s.expected', $fixture), $result); + } + + public function provideCookie() + { + foreach (glob(__DIR__.'/Fixtures/response-functional/*.php') as $file) { + yield array(pathinfo($file, PATHINFO_FILENAME)); + } + } +} diff --git a/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php b/vendor/symfony/http-foundation/Tests/ResponseHeaderBagTest.php new file mode 100644 index 0000000..e987677 --- /dev/null +++ b/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\Cookie; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; + +/** + * @group time-sensitive + */ +class ResponseHeaderBagTest extends TestCase +{ + public function testAllPreserveCase() + { + $headers = array( + 'fOo' => 'BAR', + 'ETag' => 'xyzzy', + 'Content-MD5' => 'Q2hlY2sgSW50ZWdyaXR5IQ==', + 'P3P' => 'CP="CAO PSA OUR"', + 'WWW-Authenticate' => 'Basic realm="WallyWorld"', + 'X-UA-Compatible' => 'IE=edge,chrome=1', + 'X-XSS-Protection' => '1; mode=block', + ); + + $bag = new ResponseHeaderBag($headers); + $allPreservedCase = $bag->allPreserveCase(); + + foreach (array_keys($headers) as $headerName) { + $this->assertArrayHasKey($headerName, $allPreservedCase, '->allPreserveCase() gets all input keys in original case'); + } + } + + public function testCacheControlHeader() + { + $bag = new ResponseHeaderBag(array()); + $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + + $bag = new ResponseHeaderBag(array('Cache-Control' => 'public')); + $this->assertEquals('public', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('public')); + + $bag = new ResponseHeaderBag(array('ETag' => 'abcde')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('private')); + $this->assertTrue($bag->hasCacheControlDirective('must-revalidate')); + $this->assertFalse($bag->hasCacheControlDirective('max-age')); + + $bag = new ResponseHeaderBag(array('Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array( + 'Expires' => 'Wed, 16 Feb 2011 14:17:43 GMT', + 'Cache-Control' => 'max-age=3600', + )); + $this->assertEquals('max-age=3600, private', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('Last-Modified' => 'abcde')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('Etag' => 'abcde', 'Last-Modified' => 'abcde')); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 'max-age=100')); + $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 's-maxage=100')); + $this->assertEquals('s-maxage=100', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 'private, max-age=100')); + $this->assertEquals('max-age=100, private', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(array('cache-control' => 'public, max-age=100')); + $this->assertEquals('max-age=100, public', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(); + $bag->set('Last-Modified', 'abcde'); + $this->assertEquals('private, must-revalidate', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(); + $bag->set('Cache-Control', array('public', 'must-revalidate')); + $this->assertCount(1, $bag->get('Cache-Control', null, false)); + $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control')); + + $bag = new ResponseHeaderBag(); + $bag->set('Cache-Control', 'public'); + $bag->set('Cache-Control', 'must-revalidate', false); + $this->assertCount(1, $bag->get('Cache-Control', null, false)); + $this->assertEquals('must-revalidate, public', $bag->get('Cache-Control')); + } + + public function testCacheControlClone() + { + $headers = array('foo' => 'bar'); + $bag1 = new ResponseHeaderBag($headers); + $bag2 = new ResponseHeaderBag($bag1->allPreserveCase()); + $this->assertEquals($bag1->allPreserveCase(), $bag2->allPreserveCase()); + } + + public function testToStringIncludesCookieHeaders() + { + $bag = new ResponseHeaderBag(array()); + $bag->setCookie(new Cookie('foo', 'bar')); + + $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag); + + $bag->clearCookie('foo'); + + $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; httponly', $bag); + } + + public function testClearCookieSecureNotHttpOnly() + { + $bag = new ResponseHeaderBag(array()); + + $bag->clearCookie('foo', '/', null, true, false); + + $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag); + } + + public function testReplace() + { + $bag = new ResponseHeaderBag(array()); + $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + + $bag->replace(array('Cache-Control' => 'public')); + $this->assertEquals('public', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('public')); + } + + public function testReplaceWithRemove() + { + $bag = new ResponseHeaderBag(array()); + $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + + $bag->remove('Cache-Control'); + $bag->replace(array()); + $this->assertEquals('no-cache, private', $bag->get('Cache-Control')); + $this->assertTrue($bag->hasCacheControlDirective('no-cache')); + } + + public function testCookiesWithSameNames() + { + $bag = new ResponseHeaderBag(); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'foo.bar')); + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/bar', 'bar.foo')); + $bag->setCookie(new Cookie('foo', 'bar')); + + $this->assertCount(4, $bag->getCookies()); + $this->assertEquals('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag->get('set-cookie')); + $this->assertEquals(array( + 'foo=bar; path=/path/foo; domain=foo.bar; httponly', + 'foo=bar; path=/path/bar; domain=foo.bar; httponly', + 'foo=bar; path=/path/bar; domain=bar.foo; httponly', + 'foo=bar; path=/; httponly', + ), $bag->get('set-cookie', null, false)); + + $this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag); + $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag); + $this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag); + $this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + + $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/foo']); + $this->assertArrayHasKey('foo', $cookies['foo.bar']['/path/bar']); + $this->assertArrayHasKey('foo', $cookies['bar.foo']['/path/bar']); + $this->assertArrayHasKey('foo', $cookies['']['/']); + } + + public function testRemoveCookie() + { + $bag = new ResponseHeaderBag(); + $this->assertFalse($bag->has('set-cookie')); + + $bag->setCookie(new Cookie('foo', 'bar', 0, '/path/foo', 'foo.bar')); + $bag->setCookie(new Cookie('bar', 'foo', 0, '/path/bar', 'foo.bar')); + $this->assertTrue($bag->has('set-cookie')); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertArrayHasKey('/path/foo', $cookies['foo.bar']); + + $bag->removeCookie('foo', '/path/foo', 'foo.bar'); + $this->assertTrue($bag->has('set-cookie')); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertArrayNotHasKey('/path/foo', $cookies['foo.bar']); + + $bag->removeCookie('bar', '/path/bar', 'foo.bar'); + $this->assertFalse($bag->has('set-cookie')); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertArrayNotHasKey('foo.bar', $cookies); + } + + public function testRemoveCookieWithNullRemove() + { + $bag = new ResponseHeaderBag(); + $bag->setCookie(new Cookie('foo', 'bar', 0)); + $bag->setCookie(new Cookie('bar', 'foo', 0)); + + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertArrayHasKey('/', $cookies['']); + + $bag->removeCookie('foo', null); + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertArrayNotHasKey('foo', $cookies['']['/']); + + $bag->removeCookie('bar', null); + $cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY); + $this->assertFalse(isset($cookies['']['/']['bar'])); + } + + public function testSetCookieHeader() + { + $bag = new ResponseHeaderBag(); + $bag->set('set-cookie', 'foo=bar'); + $this->assertEquals(array(new Cookie('foo', 'bar', 0, '/', null, false, false, true)), $bag->getCookies()); + + $bag->set('set-cookie', 'foo2=bar2', false); + $this->assertEquals(array( + new Cookie('foo', 'bar', 0, '/', null, false, false, true), + new Cookie('foo2', 'bar2', 0, '/', null, false, false, true), + ), $bag->getCookies()); + + $bag->remove('set-cookie'); + $this->assertEquals(array(), $bag->getCookies()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testGetCookiesWithInvalidArgument() + { + $bag = new ResponseHeaderBag(); + + $bag->getCookies('invalid_argument'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testMakeDispositionInvalidDisposition() + { + $headers = new ResponseHeaderBag(); + + $headers->makeDisposition('invalid', 'foo.html'); + } + + /** + * @dataProvider provideMakeDisposition + */ + public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected) + { + $headers = new ResponseHeaderBag(); + + $this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback)); + } + + public function testToStringDoesntMessUpHeaders() + { + $headers = new ResponseHeaderBag(); + + $headers->set('Location', 'http://www.symfony.com'); + $headers->set('Content-type', 'text/html'); + + (string) $headers; + + $allHeaders = $headers->allPreserveCase(); + $this->assertEquals(array('http://www.symfony.com'), $allHeaders['Location']); + $this->assertEquals(array('text/html'), $allHeaders['Content-type']); + } + + public function provideMakeDisposition() + { + return array( + array('attachment', 'foo.html', 'foo.html', 'attachment; filename=foo.html'), + array('attachment', 'foo.html', '', 'attachment; filename=foo.html'), + array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'), + array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'), + array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'), + array('attachment', 'föö.html', 'foo.html', 'attachment; filename=foo.html; filename*=utf-8\'\'f%C3%B6%C3%B6.html'), + ); + } + + /** + * @dataProvider provideMakeDispositionFail + * @expectedException \InvalidArgumentException + */ + public function testMakeDispositionFail($disposition, $filename) + { + $headers = new ResponseHeaderBag(); + + $headers->makeDisposition($disposition, $filename); + } + + public function provideMakeDispositionFail() + { + return array( + array('attachment', 'foo%20bar.html'), + array('attachment', 'foo/bar.html'), + array('attachment', '/foo.html'), + array('attachment', 'foo\bar.html'), + array('attachment', '\foo.html'), + array('attachment', 'föö.html'), + ); + } + + public function testDateHeaderAddedOnCreation() + { + $now = time(); + + $bag = new ResponseHeaderBag(); + $this->assertTrue($bag->has('Date')); + + $this->assertEquals($now, $bag->getDate('Date')->getTimestamp()); + } + + public function testDateHeaderCanBeSetOnCreation() + { + $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT'; + $bag = new ResponseHeaderBag(array('Date' => $someDate)); + + $this->assertEquals($someDate, $bag->get('Date')); + } + + public function testDateHeaderWillBeRecreatedWhenRemoved() + { + $someDate = 'Thu, 23 Mar 2017 09:15:12 GMT'; + $bag = new ResponseHeaderBag(array('Date' => $someDate)); + $bag->remove('Date'); + + // a (new) Date header is still present + $this->assertTrue($bag->has('Date')); + $this->assertNotEquals($someDate, $bag->get('Date')); + } + + public function testDateHeaderWillBeRecreatedWhenHeadersAreReplaced() + { + $bag = new ResponseHeaderBag(); + $bag->replace(array()); + + $this->assertTrue($bag->has('Date')); + } + + private function assertSetCookieHeader($expected, ResponseHeaderBag $actual) + { + $this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual)); + } +} diff --git a/vendor/symfony/http-foundation/Tests/ResponseTest.php b/vendor/symfony/http-foundation/Tests/ResponseTest.php new file mode 100644 index 0000000..f9b5ef4 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/ResponseTest.php @@ -0,0 +1,1045 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * @group time-sensitive + */ +class ResponseTest extends ResponseTestCase +{ + public function testCreate() + { + $response = Response::create('foo', 301, array('Foo' => 'bar')); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response); + $this->assertEquals(301, $response->getStatusCode()); + $this->assertEquals('bar', $response->headers->get('foo')); + } + + public function testToString() + { + $response = new Response(); + $response = explode("\r\n", $response); + $this->assertEquals('HTTP/1.0 200 OK', $response[0]); + $this->assertEquals('Cache-Control: no-cache, private', $response[1]); + } + + public function testClone() + { + $response = new Response(); + $responseClone = clone $response; + $this->assertEquals($response, $responseClone); + } + + public function testSendHeaders() + { + $response = new Response(); + $headers = $response->sendHeaders(); + $this->assertObjectHasAttribute('headers', $headers); + $this->assertObjectHasAttribute('content', $headers); + $this->assertObjectHasAttribute('version', $headers); + $this->assertObjectHasAttribute('statusCode', $headers); + $this->assertObjectHasAttribute('statusText', $headers); + $this->assertObjectHasAttribute('charset', $headers); + } + + public function testSend() + { + $response = new Response(); + $responseSend = $response->send(); + $this->assertObjectHasAttribute('headers', $responseSend); + $this->assertObjectHasAttribute('content', $responseSend); + $this->assertObjectHasAttribute('version', $responseSend); + $this->assertObjectHasAttribute('statusCode', $responseSend); + $this->assertObjectHasAttribute('statusText', $responseSend); + $this->assertObjectHasAttribute('charset', $responseSend); + } + + public function testGetCharset() + { + $response = new Response(); + $charsetOrigin = 'UTF-8'; + $response->setCharset($charsetOrigin); + $charset = $response->getCharset(); + $this->assertEquals($charsetOrigin, $charset); + } + + public function testIsCacheable() + { + $response = new Response(); + $this->assertFalse($response->isCacheable()); + } + + public function testIsCacheableWithErrorCode() + { + $response = new Response('', 500); + $this->assertFalse($response->isCacheable()); + } + + public function testIsCacheableWithNoStoreDirective() + { + $response = new Response(); + $response->headers->set('cache-control', 'private'); + $this->assertFalse($response->isCacheable()); + } + + public function testIsCacheableWithSetTtl() + { + $response = new Response(); + $response->setTtl(10); + $this->assertTrue($response->isCacheable()); + } + + public function testMustRevalidate() + { + $response = new Response(); + $this->assertFalse($response->mustRevalidate()); + } + + public function testMustRevalidateWithMustRevalidateCacheControlHeader() + { + $response = new Response(); + $response->headers->set('cache-control', 'must-revalidate'); + + $this->assertTrue($response->mustRevalidate()); + } + + public function testMustRevalidateWithProxyRevalidateCacheControlHeader() + { + $response = new Response(); + $response->headers->set('cache-control', 'proxy-revalidate'); + + $this->assertTrue($response->mustRevalidate()); + } + + public function testSetNotModified() + { + $response = new Response('foo'); + $modified = $response->setNotModified(); + $this->assertObjectHasAttribute('headers', $modified); + $this->assertObjectHasAttribute('content', $modified); + $this->assertObjectHasAttribute('version', $modified); + $this->assertObjectHasAttribute('statusCode', $modified); + $this->assertObjectHasAttribute('statusText', $modified); + $this->assertObjectHasAttribute('charset', $modified); + $this->assertEquals(304, $modified->getStatusCode()); + + ob_start(); + $modified->sendContent(); + $string = ob_get_clean(); + $this->assertEmpty($string); + } + + public function testIsSuccessful() + { + $response = new Response(); + $this->assertTrue($response->isSuccessful()); + } + + public function testIsNotModified() + { + $response = new Response(); + $modified = $response->isNotModified(new Request()); + $this->assertFalse($modified); + } + + public function testIsNotModifiedNotSafe() + { + $request = Request::create('/homepage', 'POST'); + + $response = new Response(); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsNotModifiedLastModified() + { + $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; + $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; + $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; + + $request = new Request(); + $request->headers->set('If-Modified-Since', $modified); + + $response = new Response(); + + $response->headers->set('Last-Modified', $modified); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('Last-Modified', $before); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('Last-Modified', $after); + $this->assertFalse($response->isNotModified($request)); + + $response->headers->set('Last-Modified', ''); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsNotModifiedEtag() + { + $etagOne = 'randomly_generated_etag'; + $etagTwo = 'randomly_generated_etag_2'; + + $request = new Request(); + $request->headers->set('if_none_match', sprintf('%s, %s, %s', $etagOne, $etagTwo, 'etagThree')); + + $response = new Response(); + + $response->headers->set('ETag', $etagOne); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', $etagTwo); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', ''); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsNotModifiedLastModifiedAndEtag() + { + $before = 'Sun, 25 Aug 2013 18:32:31 GMT'; + $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; + $after = 'Sun, 25 Aug 2013 19:33:31 GMT'; + $etag = 'randomly_generated_etag'; + + $request = new Request(); + $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); + $request->headers->set('If-Modified-Since', $modified); + + $response = new Response(); + + $response->headers->set('ETag', $etag); + $response->headers->set('Last-Modified', $after); + $this->assertFalse($response->isNotModified($request)); + + $response->headers->set('ETag', 'non-existent-etag'); + $response->headers->set('Last-Modified', $before); + $this->assertFalse($response->isNotModified($request)); + + $response->headers->set('ETag', $etag); + $response->headers->set('Last-Modified', $modified); + $this->assertTrue($response->isNotModified($request)); + } + + public function testIsNotModifiedIfModifiedSinceAndEtagWithoutLastModified() + { + $modified = 'Sun, 25 Aug 2013 18:33:31 GMT'; + $etag = 'randomly_generated_etag'; + + $request = new Request(); + $request->headers->set('if_none_match', sprintf('%s, %s', $etag, 'etagThree')); + $request->headers->set('If-Modified-Since', $modified); + + $response = new Response(); + + $response->headers->set('ETag', $etag); + $this->assertTrue($response->isNotModified($request)); + + $response->headers->set('ETag', 'non-existent-etag'); + $this->assertFalse($response->isNotModified($request)); + } + + public function testIsValidateable() + { + $response = new Response('', 200, array('Last-Modified' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); + $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if Last-Modified is present'); + + $response = new Response('', 200, array('ETag' => '"12345"')); + $this->assertTrue($response->isValidateable(), '->isValidateable() returns true if ETag is present'); + + $response = new Response(); + $this->assertFalse($response->isValidateable(), '->isValidateable() returns false when no validator is present'); + } + + public function testGetDate() + { + $oneHourAgo = $this->createDateTimeOneHourAgo(); + $response = new Response('', 200, array('Date' => $oneHourAgo->format(DATE_RFC2822))); + $date = $response->getDate(); + $this->assertEquals($oneHourAgo->getTimestamp(), $date->getTimestamp(), '->getDate() returns the Date header if present'); + + $response = new Response(); + $date = $response->getDate(); + $this->assertEquals(time(), $date->getTimestamp(), '->getDate() returns the current Date if no Date header present'); + + $response = new Response('', 200, array('Date' => $this->createDateTimeOneHourAgo()->format(DATE_RFC2822))); + $now = $this->createDateTimeNow(); + $response->headers->set('Date', $now->format(DATE_RFC2822)); + $date = $response->getDate(); + $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified'); + + $response = new Response('', 200); + $now = $this->createDateTimeNow(); + $response->headers->remove('Date'); + $date = $response->getDate(); + $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed'); + } + + public function testGetMaxAge() + { + $response = new Response(); + $response->headers->set('Cache-Control', 's-maxage=600, max-age=0'); + $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() uses s-maxage cache control directive when present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=600'); + $this->assertEquals(600, $response->getMaxAge(), '->getMaxAge() falls back to max-age when no s-maxage directive present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'must-revalidate'); + $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); + $this->assertEquals(3600, $response->getMaxAge(), '->getMaxAge() falls back to Expires when no max-age or s-maxage directive present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'must-revalidate'); + $response->headers->set('Expires', -1); + $this->assertLessThanOrEqual(time() - 2 * 86400, $response->getExpires()->format('U')); + + $response = new Response(); + $this->assertNull($response->getMaxAge(), '->getMaxAge() returns null if no freshness information available'); + } + + public function testSetSharedMaxAge() + { + $response = new Response(); + $response->setSharedMaxAge(20); + + $cacheControl = $response->headers->get('Cache-Control'); + $this->assertEquals('public, s-maxage=20', $cacheControl); + } + + public function testIsPrivate() + { + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=100'); + $response->setPrivate(); + $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); + $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'public, max-age=100'); + $response->setPrivate(); + $this->assertEquals(100, $response->headers->getCacheControlDirective('max-age'), '->isPrivate() adds the private Cache-Control directive when set to true'); + $this->assertTrue($response->headers->getCacheControlDirective('private'), '->isPrivate() adds the private Cache-Control directive when set to true'); + $this->assertFalse($response->headers->hasCacheControlDirective('public'), '->isPrivate() removes the public Cache-Control directive'); + } + + public function testExpire() + { + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=100'); + $response->expire(); + $this->assertEquals(100, $response->headers->get('Age'), '->expire() sets the Age to max-age when present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=100, s-maxage=500'); + $response->expire(); + $this->assertEquals(500, $response->headers->get('Age'), '->expire() sets the Age to s-maxage when both max-age and s-maxage are present'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=5, s-maxage=500'); + $response->headers->set('Age', '1000'); + $response->expire(); + $this->assertEquals(1000, $response->headers->get('Age'), '->expire() does nothing when the response is already stale/expired'); + + $response = new Response(); + $response->expire(); + $this->assertFalse($response->headers->has('Age'), '->expire() does nothing when the response does not include freshness information'); + + $response = new Response(); + $response->headers->set('Expires', -1); + $response->expire(); + $this->assertNull($response->headers->get('Age'), '->expire() does not set the Age when the response is expired'); + + $response = new Response(); + $response->headers->set('Expires', date(DATE_RFC2822, time() + 600)); + $response->expire(); + $this->assertNull($response->headers->get('Expires'), '->expire() removes the Expires header when the response is fresh'); + } + + public function testGetTtl() + { + $response = new Response(); + $this->assertNull($response->getTtl(), '->getTtl() returns null when no Expires or Cache-Control headers are present'); + + $response = new Response(); + $response->headers->set('Expires', $this->createDateTimeOneHourLater()->format(DATE_RFC2822)); + $this->assertEquals(3600, $response->getTtl(), '->getTtl() uses the Expires header when no max-age is present'); + + $response = new Response(); + $response->headers->set('Expires', $this->createDateTimeOneHourAgo()->format(DATE_RFC2822)); + $this->assertLessThan(0, $response->getTtl(), '->getTtl() returns negative values when Expires is in past'); + + $response = new Response(); + $response->headers->set('Expires', $response->getDate()->format(DATE_RFC2822)); + $response->headers->set('Age', 0); + $this->assertSame(0, $response->getTtl(), '->getTtl() correctly handles zero'); + + $response = new Response(); + $response->headers->set('Cache-Control', 'max-age=60'); + $this->assertEquals(60, $response->getTtl(), '->getTtl() uses Cache-Control max-age when present'); + } + + public function testSetClientTtl() + { + $response = new Response(); + $response->setClientTtl(10); + + $this->assertEquals($response->getMaxAge(), $response->getAge() + 10); + } + + public function testGetSetProtocolVersion() + { + $response = new Response(); + + $this->assertEquals('1.0', $response->getProtocolVersion()); + + $response->setProtocolVersion('1.1'); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + } + + public function testGetVary() + { + $response = new Response(); + $this->assertEquals(array(), $response->getVary(), '->getVary() returns an empty array if no Vary header is present'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language'); + $this->assertEquals(array('Accept-Language'), $response->getVary(), '->getVary() parses a single header name value'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language User-Agent X-Foo'); + $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by spaces'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language,User-Agent, X-Foo'); + $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->getVary() parses multiple header name values separated by commas'); + + $vary = array('Accept-Language', 'User-Agent', 'X-foo'); + + $response = new Response(); + $response->headers->set('Vary', $vary); + $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); + + $response = new Response(); + $response->headers->set('Vary', 'Accept-Language, User-Agent, X-foo'); + $this->assertEquals($vary, $response->getVary(), '->getVary() parses multiple header name values in arrays'); + } + + public function testSetVary() + { + $response = new Response(); + $response->setVary('Accept-Language'); + $this->assertEquals(array('Accept-Language'), $response->getVary()); + + $response->setVary('Accept-Language, User-Agent'); + $this->assertEquals(array('Accept-Language', 'User-Agent'), $response->getVary(), '->setVary() replace the vary header by default'); + + $response->setVary('X-Foo', false); + $this->assertEquals(array('Accept-Language', 'User-Agent', 'X-Foo'), $response->getVary(), '->setVary() doesn\'t wipe out earlier Vary headers if replace is set to false'); + } + + public function testDefaultContentType() + { + $headerMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\ResponseHeaderBag')->setMethods(array('set'))->getMock(); + $headerMock->expects($this->at(0)) + ->method('set') + ->with('Content-Type', 'text/html'); + $headerMock->expects($this->at(1)) + ->method('set') + ->with('Content-Type', 'text/html; charset=UTF-8'); + + $response = new Response('foo'); + $response->headers = $headerMock; + + $response->prepare(new Request()); + } + + public function testContentTypeCharset() + { + $response = new Response(); + $response->headers->set('Content-Type', 'text/css'); + + // force fixContentType() to be called + $response->prepare(new Request()); + + $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type')); + } + + public function testPrepareDoesNothingIfContentTypeIsSet() + { + $response = new Response('foo'); + $response->headers->set('Content-Type', 'text/plain'); + + $response->prepare(new Request()); + + $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type')); + } + + public function testPrepareDoesNothingIfRequestFormatIsNotDefined() + { + $response = new Response('foo'); + + $response->prepare(new Request()); + + $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type')); + } + + public function testPrepareSetContentType() + { + $response = new Response('foo'); + $request = Request::create('/'); + $request->setRequestFormat('json'); + + $response->prepare($request); + + $this->assertEquals('application/json', $response->headers->get('content-type')); + } + + public function testPrepareRemovesContentForHeadRequests() + { + $response = new Response('foo'); + $request = Request::create('/', 'HEAD'); + + $length = 12345; + $response->headers->set('Content-Length', $length); + $response->prepare($request); + + $this->assertEquals('', $response->getContent()); + $this->assertEquals($length, $response->headers->get('Content-Length'), 'Content-Length should be as if it was GET; see RFC2616 14.13'); + } + + public function testPrepareRemovesContentForInformationalResponse() + { + $response = new Response('foo'); + $request = Request::create('/'); + + $response->setContent('content'); + $response->setStatusCode(101); + $response->prepare($request); + $this->assertEquals('', $response->getContent()); + $this->assertFalse($response->headers->has('Content-Type')); + $this->assertFalse($response->headers->has('Content-Type')); + + $response->setContent('content'); + $response->setStatusCode(304); + $response->prepare($request); + $this->assertEquals('', $response->getContent()); + $this->assertFalse($response->headers->has('Content-Type')); + $this->assertFalse($response->headers->has('Content-Length')); + } + + public function testPrepareRemovesContentLength() + { + $response = new Response('foo'); + $request = Request::create('/'); + + $response->headers->set('Content-Length', 12345); + $response->prepare($request); + $this->assertEquals(12345, $response->headers->get('Content-Length')); + + $response->headers->set('Transfer-Encoding', 'chunked'); + $response->prepare($request); + $this->assertFalse($response->headers->has('Content-Length')); + } + + public function testPrepareSetsPragmaOnHttp10Only() + { + $request = Request::create('/', 'GET'); + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); + + $response = new Response('foo'); + $response->prepare($request); + $this->assertEquals('no-cache', $response->headers->get('pragma')); + $this->assertEquals('-1', $response->headers->get('expires')); + + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); + $response = new Response('foo'); + $response->prepare($request); + $this->assertFalse($response->headers->has('pragma')); + $this->assertFalse($response->headers->has('expires')); + } + + public function testSetCache() + { + $response = new Response(); + //array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public') + try { + $response->setCache(array('wrong option' => 'value')); + $this->fail('->setCache() throws an InvalidArgumentException if an option is not supported'); + } catch (\Exception $e) { + $this->assertInstanceOf('InvalidArgumentException', $e, '->setCache() throws an InvalidArgumentException if an option is not supported'); + $this->assertContains('"wrong option"', $e->getMessage()); + } + + $options = array('etag' => '"whatever"'); + $response->setCache($options); + $this->assertEquals($response->getEtag(), '"whatever"'); + + $now = $this->createDateTimeNow(); + $options = array('last_modified' => $now); + $response->setCache($options); + $this->assertEquals($response->getLastModified()->getTimestamp(), $now->getTimestamp()); + + $options = array('max_age' => 100); + $response->setCache($options); + $this->assertEquals($response->getMaxAge(), 100); + + $options = array('s_maxage' => 200); + $response->setCache($options); + $this->assertEquals($response->getMaxAge(), 200); + + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('public' => true)); + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('public' => false)); + $this->assertFalse($response->headers->hasCacheControlDirective('public')); + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('private' => true)); + $this->assertFalse($response->headers->hasCacheControlDirective('public')); + $this->assertTrue($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('private' => false)); + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + + $response->setCache(array('immutable' => true)); + $this->assertTrue($response->headers->hasCacheControlDirective('immutable')); + + $response->setCache(array('immutable' => false)); + $this->assertFalse($response->headers->hasCacheControlDirective('immutable')); + } + + public function testSendContent() + { + $response = new Response('test response rendering', 200); + + ob_start(); + $response->sendContent(); + $string = ob_get_clean(); + $this->assertContains('test response rendering', $string); + } + + public function testSetPublic() + { + $response = new Response(); + $response->setPublic(); + + $this->assertTrue($response->headers->hasCacheControlDirective('public')); + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + } + + public function testSetImmutable() + { + $response = new Response(); + $response->setImmutable(); + + $this->assertTrue($response->headers->hasCacheControlDirective('immutable')); + } + + public function testIsImmutable() + { + $response = new Response(); + $response->setImmutable(); + + $this->assertTrue($response->isImmutable()); + } + + public function testSetDate() + { + $response = new Response(); + $response->setDate(\DateTime::createFromFormat(\DateTime::ATOM, '2013-01-26T09:21:56+0100', new \DateTimeZone('Europe/Berlin'))); + + $this->assertEquals('2013-01-26T08:21:56+00:00', $response->getDate()->format(\DateTime::ATOM)); + } + + public function testSetDateWithImmutable() + { + $response = new Response(); + $response->setDate(\DateTimeImmutable::createFromFormat(\DateTime::ATOM, '2013-01-26T09:21:56+0100', new \DateTimeZone('Europe/Berlin'))); + + $this->assertEquals('2013-01-26T08:21:56+00:00', $response->getDate()->format(\DateTime::ATOM)); + } + + 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 testSetExpiresWithImmutable() + { + $response = new Response(); + + $now = $this->createDateTimeImmutableNow(); + $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 testSetLastModifiedWithImmutable() + { + $response = new Response(); + $response->setLastModified($this->createDateTimeImmutableNow()); + $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 createDateTimeImmutableNow() + { + $date = new \DateTimeImmutable(); + + 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 +{ +} diff --git a/vendor/symfony/http-foundation/Tests/ResponseTestCase.php b/vendor/symfony/http-foundation/Tests/ResponseTestCase.php new file mode 100644 index 0000000..4ead34c --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/ServerBagTest.php b/vendor/symfony/http-foundation/Tests/ServerBagTest.php new file mode 100644 index 0000000..f8becec --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Attribute/AttributeBagTest.php new file mode 100644 index 0000000..43644e2 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php new file mode 100644 index 0000000..ec4cd5a --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Attribute/NamespacedAttributeBagTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Attribute; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; + +/** + * Tests NamespacedAttributeBag. + * + * @author Drak + */ +class NamespacedAttributeBagTest extends TestCase +{ + private $array = array(); + + /** + * @var NamespacedAttributeBag + */ + private $bag; + + protected function setUp() + { + $this->array = array( + 'hello' => 'world', + 'always' => 'be happy', + 'user.login' => 'drak', + 'csrf.token' => array( + 'a' => '1234', + 'b' => '4321', + ), + 'category' => array( + 'fishing' => array( + 'first' => 'cod', + 'second' => 'sole', + ), + ), + ); + $this->bag = new NamespacedAttributeBag('_sf2', '/'); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->bag = null; + $this->array = array(); + } + + public function testInitialize() + { + $bag = new NamespacedAttributeBag(); + $bag->initialize($this->array); + $this->assertEquals($this->array, $this->bag->all()); + $array = array('should' => 'not stick'); + $bag->initialize($array); + + // should have remained the same + $this->assertEquals($this->array, $this->bag->all()); + } + + public function testGetStorageKey() + { + $this->assertEquals('_sf2', $this->bag->getStorageKey()); + $attributeBag = new NamespacedAttributeBag('test'); + $this->assertEquals('test', $attributeBag->getStorageKey()); + } + + /** + * @dataProvider attributesProvider + */ + public function testHas($key, $value, $exists) + { + $this->assertEquals($exists, $this->bag->has($key)); + } + + /** + * @dataProvider attributesProvider + */ + public function testHasNoSideEffect($key, $value, $expected) + { + $expected = json_encode($this->bag->all()); + $this->bag->has($key); + + $this->assertEquals($expected, json_encode($this->bag->all())); + } + + /** + * @dataProvider attributesProvider + */ + public function testGet($key, $value, $expected) + { + $this->assertEquals($value, $this->bag->get($key)); + } + + public function testGetDefaults() + { + $this->assertNull($this->bag->get('user2.login')); + $this->assertEquals('default', $this->bag->get('user2.login', 'default')); + } + + /** + * @dataProvider attributesProvider + */ + public function testGetNoSideEffect($key, $value, $expected) + { + $expected = json_encode($this->bag->all()); + $this->bag->get($key); + + $this->assertEquals($expected, json_encode($this->bag->all())); + } + + /** + * @dataProvider attributesProvider + */ + public function testSet($key, $value, $expected) + { + $this->bag->set($key, $value); + $this->assertEquals($value, $this->bag->get($key)); + } + + public function testAll() + { + $this->assertEquals($this->array, $this->bag->all()); + + $this->bag->set('hello', 'fabien'); + $array = $this->array; + $array['hello'] = 'fabien'; + $this->assertEquals($array, $this->bag->all()); + } + + public function testReplace() + { + $array = array(); + $array['name'] = 'jack'; + $array['foo.bar'] = 'beep'; + $this->bag->replace($array); + $this->assertEquals($array, $this->bag->all()); + $this->assertNull($this->bag->get('hello')); + $this->assertNull($this->bag->get('always')); + $this->assertNull($this->bag->get('user.login')); + } + + public function testRemove() + { + $this->assertEquals('world', $this->bag->get('hello')); + $this->bag->remove('hello'); + $this->assertNull($this->bag->get('hello')); + + $this->assertEquals('be happy', $this->bag->get('always')); + $this->bag->remove('always'); + $this->assertNull($this->bag->get('always')); + + $this->assertEquals('drak', $this->bag->get('user.login')); + $this->bag->remove('user.login'); + $this->assertNull($this->bag->get('user.login')); + } + + public function testRemoveExistingNamespacedAttribute() + { + $this->assertSame('cod', $this->bag->remove('category/fishing/first')); + } + + public function testRemoveNonexistingNamespacedAttribute() + { + $this->assertNull($this->bag->remove('foo/bar/baz')); + } + + public function testClear() + { + $this->bag->clear(); + $this->assertEquals(array(), $this->bag->all()); + } + + public function attributesProvider() + { + return array( + array('hello', 'world', true), + array('always', 'be happy', true), + array('user.login', 'drak', true), + array('csrf.token', array('a' => '1234', 'b' => '4321'), true), + array('csrf.token/a', '1234', true), + array('csrf.token/b', '4321', true), + array('category', array('fishing' => array('first' => 'cod', 'second' => 'sole')), true), + array('category/fishing', array('first' => 'cod', 'second' => 'sole'), true), + array('category/fishing/missing/first', null, false), + array('category/fishing/first', 'cod', true), + array('category/fishing/second', 'sole', true), + array('category/fishing/missing/second', null, false), + array('user2.login', null, false), + array('never', null, false), + array('bye', null, false), + array('bye/for/now', null, false), + ); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Flash/AutoExpireFlashBagTest.php new file mode 100644 index 0000000..fa8626a --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php new file mode 100644 index 0000000..905a1f7 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Flash/FlashBagTest.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Flash; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; + +/** + * FlashBagTest. + * + * @author Drak + */ +class FlashBagTest extends TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface + */ + private $bag; + + protected $array = array(); + + protected function setUp() + { + parent::setUp(); + $this->bag = new FlashBag(); + $this->array = array('notice' => array('A previous flash message')); + $this->bag->initialize($this->array); + } + + protected function tearDown() + { + $this->bag = null; + parent::tearDown(); + } + + public function testInitialize() + { + $bag = new FlashBag(); + $bag->initialize($this->array); + $this->assertEquals($this->array, $bag->peekAll()); + $array = array('should' => array('change')); + $bag->initialize($array); + $this->assertEquals($array, $bag->peekAll()); + } + + public function testGetStorageKey() + { + $this->assertEquals('_symfony_flashes', $this->bag->getStorageKey()); + $attributeBag = new FlashBag('test'); + $this->assertEquals('test', $attributeBag->getStorageKey()); + } + + public function testGetSetName() + { + $this->assertEquals('flashes', $this->bag->getName()); + $this->bag->setName('foo'); + $this->assertEquals('foo', $this->bag->getName()); + } + + public function testPeek() + { + $this->assertEquals(array(), $this->bag->peek('non_existing')); + $this->assertEquals(array('default'), $this->bag->peek('not_existing', array('default'))); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + $this->assertEquals(array('A previous flash message'), $this->bag->peek('notice')); + } + + public function testAdd() + { + $tab = array('bar' => 'baz'); + $this->bag->add('string_message', 'lorem'); + $this->bag->add('object_message', new \stdClass()); + $this->bag->add('array_message', $tab); + + $this->assertEquals(array('lorem'), $this->bag->get('string_message')); + $this->assertEquals(array(new \stdClass()), $this->bag->get('object_message')); + $this->assertEquals(array($tab), $this->bag->get('array_message')); + } + + public function testGet() + { + $this->assertEquals(array(), $this->bag->get('non_existing')); + $this->assertEquals(array('default'), $this->bag->get('not_existing', array('default'))); + $this->assertEquals(array('A previous flash message'), $this->bag->get('notice')); + $this->assertEquals(array(), $this->bag->get('notice')); + } + + public function testAll() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('error', 'Bar'); + $this->assertEquals(array( + 'notice' => array('Foo'), + 'error' => array('Bar'), ), $this->bag->all() + ); + + $this->assertEquals(array(), $this->bag->all()); + } + + public function testSet() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('notice', 'Bar'); + $this->assertEquals(array('Bar'), $this->bag->peek('notice')); + } + + public function testHas() + { + $this->assertFalse($this->bag->has('nothing')); + $this->assertTrue($this->bag->has('notice')); + } + + public function testKeys() + { + $this->assertEquals(array('notice'), $this->bag->keys()); + } + + public function testSetAll() + { + $this->bag->add('one_flash', 'Foo'); + $this->bag->add('another_flash', 'Bar'); + $this->assertTrue($this->bag->has('one_flash')); + $this->assertTrue($this->bag->has('another_flash')); + $this->bag->setAll(array('unique_flash' => 'FooBar')); + $this->assertFalse($this->bag->has('one_flash')); + $this->assertFalse($this->bag->has('another_flash')); + $this->assertSame(array('unique_flash' => 'FooBar'), $this->bag->all()); + $this->assertSame(array(), $this->bag->all()); + } + + public function testPeekAll() + { + $this->bag->set('notice', 'Foo'); + $this->bag->set('error', 'Bar'); + $this->assertEquals(array( + 'notice' => array('Foo'), + 'error' => array('Bar'), + ), $this->bag->peekAll() + ); + $this->assertTrue($this->bag->has('notice')); + $this->assertTrue($this->bag->has('error')); + $this->assertEquals(array( + 'notice' => array('Foo'), + 'error' => array('Bar'), + ), $this->bag->peekAll() + ); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/SessionTest.php b/vendor/symfony/http-foundation/Tests/Session/SessionTest.php new file mode 100644 index 0000000..63351e5 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/SessionTest.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\HttpFoundation\Tests\Session; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; + +/** + * SessionTest. + * + * @author Fabien Potencier + * @author Robert Schönthal + * @author Drak + */ +class SessionTest extends TestCase +{ + /** + * @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface + */ + protected $storage; + + /** + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + + protected function setUp() + { + $this->storage = new MockArraySessionStorage(); + $this->session = new Session($this->storage, new AttributeBag(), new FlashBag()); + } + + protected function tearDown() + { + $this->storage = null; + $this->session = null; + } + + public function testStart() + { + $this->assertEquals('', $this->session->getId()); + $this->assertTrue($this->session->start()); + $this->assertNotEquals('', $this->session->getId()); + } + + public function testIsStarted() + { + $this->assertFalse($this->session->isStarted()); + $this->session->start(); + $this->assertTrue($this->session->isStarted()); + } + + public function testSetId() + { + $this->assertEquals('', $this->session->getId()); + $this->session->setId('0123456789abcdef'); + $this->session->start(); + $this->assertEquals('0123456789abcdef', $this->session->getId()); + } + + public function testSetIdAfterStart() + { + $this->session->start(); + $id = $this->session->getId(); + + $e = null; + try { + $this->session->setId($id); + } catch (\Exception $e) { + } + + $this->assertNull($e); + + try { + $this->session->setId('different'); + } catch (\Exception $e) { + } + + $this->assertInstanceOf('\LogicException', $e); + } + + public function testSetName() + { + $this->assertEquals('MOCKSESSID', $this->session->getName()); + $this->session->setName('session.test.com'); + $this->session->start(); + $this->assertEquals('session.test.com', $this->session->getName()); + } + + public function testGet() + { + // tests defaults + $this->assertNull($this->session->get('foo')); + $this->assertEquals(1, $this->session->get('foo', 1)); + } + + /** + * @dataProvider setProvider + */ + public function testSet($key, $value) + { + $this->session->set($key, $value); + $this->assertEquals($value, $this->session->get($key)); + } + + /** + * @dataProvider setProvider + */ + public function testHas($key, $value) + { + $this->session->set($key, $value); + $this->assertTrue($this->session->has($key)); + $this->assertFalse($this->session->has($key.'non_value')); + } + + public function testReplace() + { + $this->session->replace(array('happiness' => 'be good', 'symfony' => 'awesome')); + $this->assertEquals(array('happiness' => 'be good', 'symfony' => 'awesome'), $this->session->all()); + $this->session->replace(array()); + $this->assertEquals(array(), $this->session->all()); + } + + /** + * @dataProvider setProvider + */ + public function testAll($key, $value, $result) + { + $this->session->set($key, $value); + $this->assertEquals($result, $this->session->all()); + } + + /** + * @dataProvider setProvider + */ + public function testClear($key, $value) + { + $this->session->set('hi', 'fabien'); + $this->session->set($key, $value); + $this->session->clear(); + $this->assertEquals(array(), $this->session->all()); + } + + public function setProvider() + { + return array( + array('foo', 'bar', array('foo' => 'bar')), + array('foo.bar', 'too much beer', array('foo.bar' => 'too much beer')), + array('great', 'symfony is great', array('great' => 'symfony is great')), + ); + } + + /** + * @dataProvider setProvider + */ + public function testRemove($key, $value) + { + $this->session->set('hi.world', 'have a nice day'); + $this->session->set($key, $value); + $this->session->remove($key); + $this->assertEquals(array('hi.world' => 'have a nice day'), $this->session->all()); + } + + public function testInvalidate() + { + $this->session->set('invalidate', 123); + $this->session->invalidate(); + $this->assertEquals(array(), $this->session->all()); + } + + public function testMigrate() + { + $this->session->set('migrate', 321); + $this->session->migrate(); + $this->assertEquals(321, $this->session->get('migrate')); + } + + public function testMigrateDestroy() + { + $this->session->set('migrate', 333); + $this->session->migrate(true); + $this->assertEquals(333, $this->session->get('migrate')); + } + + public function testSave() + { + $this->session->start(); + $this->session->save(); + + $this->assertFalse($this->session->isStarted()); + } + + public function testGetId() + { + $this->assertEquals('', $this->session->getId()); + $this->session->start(); + $this->assertNotEquals('', $this->session->getId()); + } + + public function testGetFlashBag() + { + $this->assertInstanceOf('Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface', $this->session->getFlashBag()); + } + + public function testGetIterator() + { + $attributes = array('hello' => 'world', 'symfony' => 'rocks'); + foreach ($attributes as $key => $val) { + $this->session->set($key, $val); + } + + $i = 0; + foreach ($this->session as $key => $val) { + $this->assertEquals($attributes[$key], $val); + ++$i; + } + + $this->assertEquals(\count($attributes), $i); + } + + public function testGetCount() + { + $this->session->set('hello', 'world'); + $this->session->set('symfony', 'rocks'); + + $this->assertCount(2, $this->session); + } + + public function testGetMeta() + { + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\MetadataBag', $this->session->getMetadataBag()); + } + + public function testIsEmpty() + { + $this->assertTrue($this->session->isEmpty()); + + $this->session->set('hello', 'world'); + $this->assertFalse($this->session->isEmpty()); + + $this->session->remove('hello'); + $this->assertTrue($this->session->isEmpty()); + + $flash = $this->session->getFlashBag(); + $flash->set('hello', 'world'); + $this->assertFalse($this->session->isEmpty()); + + $flash->get('hello'); + $this->assertTrue($this->session->isEmpty()); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php new file mode 100644 index 0000000..a2bf168 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractRedisSessionHandlerTestCase.php @@ -0,0 +1,145 @@ + + * + * 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\RedisSessionHandler; + +/** + * @requires extension redis + * @group time-sensitive + */ +abstract class AbstractRedisSessionHandlerTestCase extends TestCase +{ + protected const PREFIX = 'prefix_'; + + /** + * @var RedisSessionHandler + */ + protected $storage; + + /** + * @var \Redis|\RedisArray|\RedisCluster|\Predis\Client + */ + protected $redisClient; + + /** + * @return \Redis|\RedisArray|\RedisCluster|\Predis\Client + */ + abstract protected function createRedisClient(string $host); + + protected function setUp() + { + parent::setUp(); + + if (!\extension_loaded('redis')) { + self::markTestSkipped('Extension redis required.'); + } + + $host = getenv('REDIS_HOST') ?: 'localhost'; + + $this->redisClient = $this->createRedisClient($host); + $this->storage = new RedisSessionHandler( + $this->redisClient, + array('prefix' => self::PREFIX) + ); + } + + protected function tearDown() + { + $this->redisClient = 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->redisClient->set(self::PREFIX.'id1', null); + $this->redisClient->set(self::PREFIX.'id2', 'abc123'); + + $this->assertEquals('', $this->storage->read('id1')); + $this->assertEquals('abc123', $this->storage->read('id2')); + } + + public function testWriteSession() + { + $this->assertTrue($this->storage->write('id', 'data')); + + $this->assertTrue((bool) $this->redisClient->exists(self::PREFIX.'id')); + $this->assertEquals('data', $this->redisClient->get(self::PREFIX.'id')); + } + + public function testUseSessionGcMaxLifetimeAsTimeToLive() + { + $this->storage->write('id', 'data'); + $ttl = $this->redisClient->ttl(self::PREFIX.'id'); + + $this->assertLessThanOrEqual(ini_get('session.gc_maxlifetime'), $ttl); + $this->assertGreaterThanOrEqual(0, $ttl); + } + + public function testDestroySession() + { + $this->redisClient->set(self::PREFIX.'id', 'foo'); + + $this->assertTrue((bool) $this->redisClient->exists(self::PREFIX.'id')); + $this->assertTrue($this->storage->destroy('id')); + $this->assertFalse((bool) $this->redisClient->exists(self::PREFIX.'id')); + } + + public function testGcSession() + { + $this->assertTrue($this->storage->gc(123)); + } + + public function testUpdateTimestamp() + { + $lowTtl = 10; + + $this->redisClient->setex(self::PREFIX.'id', $lowTtl, 'foo'); + $this->storage->updateTimestamp('id', array()); + + $this->assertGreaterThan($lowTtl, $this->redisClient->ttl(self::PREFIX.'id')); + } + + /** + * @dataProvider getOptionFixtures + */ + public function testSupportedParam(array $options, bool $supported) + { + try { + new RedisSessionHandler($this->redisClient, $options); + $this->assertTrue($supported); + } catch (\InvalidArgumentException $e) { + $this->assertFalse($supported); + } + } + + public function getOptionFixtures(): array + { + return array( + array(array('prefix' => 'session'), true), + array(array('prefix' => 'sfs', 'foo' => 'bar'), false), + ); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.php new file mode 100644 index 0000000..6566d6e --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/AbstractSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +use PHPUnit\Framework\TestCase; + +class AbstractSessionHandlerTest extends TestCase +{ + private static $server; + + public static function setUpBeforeClass() + { + $spec = array( + 1 => array('file', '/dev/null', 'w'), + 2 => array('file', '/dev/null', 'w'), + ); + if (!self::$server = @proc_open('exec php -S localhost:8053', $spec, $pipes, __DIR__.'/Fixtures')) { + self::markTestSkipped('PHP server unable to start.'); + } + sleep(1); + } + + public static function tearDownAfterClass() + { + if (self::$server) { + proc_terminate(self::$server); + proc_close(self::$server); + } + } + + /** + * @dataProvider provideSession + */ + public function testSession($fixture) + { + $context = array('http' => array('header' => "Cookie: sid=123abc\r\n")); + $context = stream_context_create($context); + $result = file_get_contents(sprintf('http://localhost:8053/%s.php', $fixture), false, $context); + + $this->assertStringEqualsFile(__DIR__.sprintf('/Fixtures/%s.expected', $fixture), $result); + } + + public function provideSession() + { + foreach (glob(__DIR__.'/Fixtures/*.php') as $file) { + yield array(pathinfo($file, PATHINFO_FILENAME)); + } + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/common.inc new file mode 100644 index 0000000..7a064c7 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.expected new file mode 100644 index 0000000..8203714 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/empty_destroys.php new file mode 100644 index 0000000..3cfc125 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/read_only.php new file mode 100644 index 0000000..3e62fb9 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/regenerate.php new file mode 100644 index 0000000..a0f635c --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/storage.php new file mode 100644 index 0000000..96dca3c --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.expected new file mode 100644 index 0000000..33da0a5 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie.php new file mode 100644 index 0000000..ffb5b20 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/Fixtures/with_cookie_and_session.php new file mode 100644 index 0000000..ec51193 --- /dev/null +++ b/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\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() + { + parent::setUp(); + + if (version_compare(phpversion('memcached'), '2.2.0', '>=') && version_compare(phpversion('memcached'), '3.0.0b1', '<')) { + $this->markTestSkipped('Tests can only be run with memcached extension 2.1.0 or lower, or 3.0.0b1 or higher'); + } + + $this->memcached = $this->getMockBuilder('Memcached')->getMock(); + $this->storage = new MemcachedSessionHandler( + $this->memcached, + array('prefix' => self::PREFIX, 'expiretime' => self::TTL) + ); + } + + protected function tearDown() + { + $this->memcached = null; + $this->storage = null; + parent::tearDown(); + } + + public function testOpenSession() + { + $this->assertTrue($this->storage->open('', '')); + } + + public function testCloseSession() + { + $this->assertTrue($this->storage->close()); + } + + public function testReadSession() + { + $this->memcached + ->expects($this->once()) + ->method('get') + ->with(self::PREFIX.'id') + ; + + $this->assertEquals('', $this->storage->read('id')); + } + + public function testWriteSession() + { + $this->memcached + ->expects($this->once()) + ->method('set') + ->with(self::PREFIX.'id', 'data', $this->equalTo(time() + self::TTL, 2)) + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->write('id', 'data')); + } + + public function testDestroySession() + { + $this->memcached + ->expects($this->once()) + ->method('delete') + ->with(self::PREFIX.'id') + ->will($this->returnValue(true)) + ; + + $this->assertTrue($this->storage->destroy('id')); + } + + public function testGcSession() + { + $this->assertTrue($this->storage->gc(123)); + } + + /** + * @dataProvider getOptionFixtures + */ + public function testSupportedOptions($options, $supported) + { + try { + new MemcachedSessionHandler($this->memcached, $options); + $this->assertTrue($supported); + } catch (\InvalidArgumentException $e) { + $this->assertFalse($supported); + } + } + + public function getOptionFixtures() + { + return array( + array(array('prefix' => 'session'), true), + array(array('expiretime' => 100), true), + array(array('prefix' => 'session', 'expiretime' => 200), true), + array(array('expiretime' => 100, 'foo' => 'bar'), false), + ); + } + + public function testGetConnection() + { + $method = new \ReflectionMethod($this->storage, 'getMemcached'); + $method->setAccessible(true); + + $this->assertInstanceOf('\Memcached', $method->invoke($this->storage)); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MigratingSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MigratingSessionHandlerTest.php new file mode 100644 index 0000000..1c0f3ca --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MigratingSessionHandlerTest.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\Storage\Handler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MigratingSessionHandler; + +class MigratingSessionHandlerTest extends TestCase +{ + private $dualHandler; + private $currentHandler; + private $writeOnlyHandler; + + protected function setUp() + { + $this->currentHandler = $this->createMock(\SessionHandlerInterface::class); + $this->writeOnlyHandler = $this->createMock(\SessionHandlerInterface::class); + + $this->dualHandler = new MigratingSessionHandler($this->currentHandler, $this->writeOnlyHandler); + } + + public function testInstanceOf() + { + $this->assertInstanceOf(\SessionHandlerInterface::class, $this->dualHandler); + $this->assertInstanceOf(\SessionUpdateTimestampHandlerInterface::class, $this->dualHandler); + } + + public function testClose() + { + $this->currentHandler->expects($this->once()) + ->method('close') + ->will($this->returnValue(true)); + + $this->writeOnlyHandler->expects($this->once()) + ->method('close') + ->will($this->returnValue(false)); + + $result = $this->dualHandler->close(); + + $this->assertTrue($result); + } + + public function testDestroy() + { + $sessionId = 'xyz'; + + $this->currentHandler->expects($this->once()) + ->method('destroy') + ->with($sessionId) + ->will($this->returnValue(true)); + + $this->writeOnlyHandler->expects($this->once()) + ->method('destroy') + ->with($sessionId) + ->will($this->returnValue(false)); + + $result = $this->dualHandler->destroy($sessionId); + + $this->assertTrue($result); + } + + public function testGc() + { + $maxlifetime = 357; + + $this->currentHandler->expects($this->once()) + ->method('gc') + ->with($maxlifetime) + ->will($this->returnValue(true)); + + $this->writeOnlyHandler->expects($this->once()) + ->method('gc') + ->with($maxlifetime) + ->will($this->returnValue(false)); + + $result = $this->dualHandler->gc($maxlifetime); + $this->assertTrue($result); + } + + public function testOpen() + { + $savePath = '/path/to/save/location'; + $sessionName = 'xyz'; + + $this->currentHandler->expects($this->once()) + ->method('open') + ->with($savePath, $sessionName) + ->will($this->returnValue(true)); + + $this->writeOnlyHandler->expects($this->once()) + ->method('open') + ->with($savePath, $sessionName) + ->will($this->returnValue(false)); + + $result = $this->dualHandler->open($savePath, $sessionName); + + $this->assertTrue($result); + } + + public function testRead() + { + $sessionId = 'xyz'; + $readValue = 'something'; + + $this->currentHandler->expects($this->once()) + ->method('read') + ->with($sessionId) + ->will($this->returnValue($readValue)); + + $this->writeOnlyHandler->expects($this->never()) + ->method('read') + ->with($this->any()); + + $result = $this->dualHandler->read($sessionId); + + $this->assertSame($readValue, $result); + } + + public function testWrite() + { + $sessionId = 'xyz'; + $data = 'my-serialized-data'; + + $this->currentHandler->expects($this->once()) + ->method('write') + ->with($sessionId, $data) + ->will($this->returnValue(true)); + + $this->writeOnlyHandler->expects($this->once()) + ->method('write') + ->with($sessionId, $data) + ->will($this->returnValue(false)); + + $result = $this->dualHandler->write($sessionId, $data); + + $this->assertTrue($result); + } + + public function testValidateId() + { + $sessionId = 'xyz'; + $readValue = 'something'; + + $this->currentHandler->expects($this->once()) + ->method('read') + ->with($sessionId) + ->will($this->returnValue($readValue)); + + $this->writeOnlyHandler->expects($this->never()) + ->method('read') + ->with($this->any()); + + $result = $this->dualHandler->validateId($sessionId); + + $this->assertTrue($result); + } + + public function testUpdateTimestamp() + { + $sessionId = 'xyz'; + $data = 'my-serialized-data'; + + $this->currentHandler->expects($this->once()) + ->method('write') + ->with($sessionId, $data) + ->will($this->returnValue(true)); + + $this->writeOnlyHandler->expects($this->once()) + ->method('write') + ->with($sessionId, $data) + ->will($this->returnValue(false)); + + $result = $this->dualHandler->updateTimestamp($sessionId, $data); + + $this->assertTrue($result); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.php new file mode 100644 index 0000000..55a3864 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/MongoDbSessionHandlerTest.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\Tests\Session\Storage\Handler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; + +/** + * @author Markus Bachmann + * @group time-sensitive + * @requires extension mongodb + */ +class MongoDbSessionHandlerTest extends TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_MockObject + */ + private $mongo; + private $storage; + public $options; + + protected function setUp() + { + parent::setUp(); + + if (!class_exists(\MongoDB\Client::class)) { + $this->markTestSkipped('The mongodb/mongodb package is required.'); + } + + $this->mongo = $this->getMockBuilder(\MongoDB\Client::class) + ->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 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']]); + + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $criteria[$this->options['expiry_field']]['$gte']); + $this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout); + + return array( + $this->options['id_field'] => 'foo', + $this->options['expiry_field'] => new \MongoDB\BSON\UTCDateTime(), + $this->options['data_field'] => new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY), + ); + })); + + $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)); + + $collection->expects($this->once()) + ->method('updateOne') + ->will($this->returnCallback(function ($criteria, $updateData, $options) { + $this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria); + $this->assertEquals(array('upsert' => true), $options); + + $data = $updateData['$set']; + $expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); + $this->assertInstanceOf(\MongoDB\BSON\Binary::class, $data[$this->options['data_field']]); + $this->assertEquals('bar', $data[$this->options['data_field']]->getData()); + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $data[$this->options['time_field']]); + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $data[$this->options['expiry_field']]); + $this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000)); + })); + + $this->assertTrue($this->storage->write('foo', 'bar')); + } + + 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(); + + $collection->expects($this->exactly(2)) + ->method('updateOne') + ->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) { + $data = $updateData; + })); + + $this->storage->write('foo', 'bar'); + $this->storage->write('foo', 'foobar'); + + $this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData()); + } + + public function testDestroy() + { + $collection = $this->createMongoCollectionMock(); + + $this->mongo->expects($this->once()) + ->method('selectCollection') + ->with($this->options['database'], $this->options['collection']) + ->will($this->returnValue($collection)); + + $collection->expects($this->once()) + ->method('deleteOne') + ->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)); + + $collection->expects($this->once()) + ->method('deleteMany') + ->will($this->returnCallback(function ($criteria) { + $this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $criteria[$this->options['expiry_field']]['$lt']); + $this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000)); + })); + + $this->assertTrue($this->storage->gc(1)); + } + + public function testGetConnection() + { + $method = new \ReflectionMethod($this->storage, 'getMongo'); + $method->setAccessible(true); + + $this->assertInstanceOf(\MongoDB\Client::class, $method->invoke($this->storage)); + } + + private function createMongoCollectionMock() + { + $collection = $this->getMockBuilder(\MongoDB\Collection::class) + ->disableOriginalConstructor() + ->getMock(); + + return $collection; + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.php new file mode 100644 index 0000000..95e725f --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NativeFileSessionHandlerTest.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\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('user', ini_get('session.save_handler')); + + $this->assertEquals(sys_get_temp_dir(), ini_get('session.save_path')); + $this->assertEquals('TESTING', ini_get('session.name')); + } + + /** + * @dataProvider savePathDataProvider + */ + public function testConstructSavePath($savePath, $expectedSavePath, $path) + { + $handler = new NativeFileSessionHandler($savePath); + $this->assertEquals($expectedSavePath, ini_get('session.save_path')); + $this->assertTrue(is_dir(realpath($path))); + + rmdir($path); + } + + public function savePathDataProvider() + { + $base = sys_get_temp_dir(); + + return array( + array("$base/foo", "$base/foo", "$base/foo"), + array("5;$base/foo", "5;$base/foo", "$base/foo"), + array("5;0600;$base/foo", "5;0600;$base/foo", "$base/foo"), + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testConstructException() + { + $handler = new NativeFileSessionHandler('something;invalid;with;too-many-args'); + } + + public function testConstructDefault() + { + $path = ini_get('session.save_path'); + $storage = new NativeSessionStorage(array('name' => 'TESTING'), new NativeFileSessionHandler()); + + $this->assertEquals($path, ini_get('session.save_path')); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/NullSessionHandlerTest.php new file mode 100644 index 0000000..9a2212b --- /dev/null +++ b/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\Session; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * Test class for NullSessionHandler. + * + * @author Drak + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class NullSessionHandlerTest extends TestCase +{ + public function testSaveHandlers() + { + $storage = $this->getStorage(); + $this->assertEquals('user', ini_get('session.save_handler')); + } + + public function testSession() + { + session_id('nullsessionstorage'); + $storage = $this->getStorage(); + $session = new Session($storage); + $this->assertNull($session->get('something')); + $session->set('something', 'unique'); + $this->assertEquals('unique', $session->get('something')); + } + + public function testNothingIsPersisted() + { + session_id('nullsessionstorage'); + $storage = $this->getStorage(); + $session = new Session($storage); + $session->start(); + $this->assertEquals('nullsessionstorage', $session->getId()); + $this->assertNull($session->get('something')); + } + + public function getStorage() + { + return new NativeSessionStorage(array(), new NullSessionHandler()); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.php new file mode 100644 index 0000000..e847818 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PdoSessionHandlerTest.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\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() + { + $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 (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)); + } + + /** + * @dataProvider provideUrlDsnPairs + */ + public function testUrlDsn($url, $expectedDsn, $expectedUser = null, $expectedPassword = null) + { + $storage = new PdoSessionHandler($url); + + $this->assertAttributeEquals($expectedDsn, 'dsn', $storage); + + if (null !== $expectedUser) { + $this->assertAttributeEquals($expectedUser, 'username', $storage); + } + + if (null !== $expectedPassword) { + $this->assertAttributeEquals($expectedPassword, 'password', $storage); + } + } + + public function provideUrlDsnPairs() + { + yield array('mysql://localhost/test', 'mysql:host=localhost;dbname=test;'); + yield array('mysql://localhost:56/test', 'mysql:host=localhost;port=56;dbname=test;'); + yield array('mysql2://root:pwd@localhost/test', 'mysql:host=localhost;dbname=test;', 'root', 'pwd'); + yield array('postgres://localhost/test', 'pgsql:host=localhost;dbname=test;'); + yield array('postgresql://localhost:5634/test', 'pgsql:host=localhost;port=5634;dbname=test;'); + yield array('postgres://root:pwd@localhost/test', 'pgsql:host=localhost;dbname=test;', 'root', 'pwd'); + yield 'sqlite relative path' => array('sqlite://localhost/tmp/test', 'sqlite:tmp/test'); + yield 'sqlite absolute path' => array('sqlite://localhost//tmp/test', 'sqlite:/tmp/test'); + yield 'sqlite relative path without host' => array('sqlite:///tmp/test', 'sqlite:tmp/test'); + yield 'sqlite absolute path without host' => array('sqlite3:////tmp/test', 'sqlite:/tmp/test'); + yield array('sqlite://localhost/:memory:', 'sqlite::memory:'); + yield array('mssql://localhost/test', 'sqlsrv:server=localhost;Database=test'); + yield array('mssql://localhost:56/test', 'sqlsrv:server=localhost,56;Database=test'); + } + + private function createStream($content) + { + $stream = tmpfile(); + fwrite($stream, $content); + fseek($stream, 0); + + return $stream; + } +} + +class MockPdo extends \PDO +{ + public $prepareResult; + private $driverName; + private $errorMode; + + public function __construct($driverName = null, $errorMode = null) + { + $this->driverName = $driverName; + $this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION; + } + + public function getAttribute($attribute) + { + if (\PDO::ATTR_ERRMODE === $attribute) { + return $this->errorMode; + } + + if (\PDO::ATTR_DRIVER_NAME === $attribute) { + return $this->driverName; + } + + return parent::getAttribute($attribute); + } + + public function prepare($statement, $driverOptions = array()) + { + return \is_callable($this->prepareResult) + ? \call_user_func($this->prepareResult, $statement, $driverOptions) + : $this->prepareResult; + } + + public function beginTransaction() + { + } + + public function rollBack() + { + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.php new file mode 100644 index 0000000..4581001 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisClusterSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +use Predis\Client; + +class PredisClusterSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): Client + { + return new Client(array(array('host' => $host))); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.php new file mode 100644 index 0000000..a9db4eb --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/PredisSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +use Predis\Client; + +class PredisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): Client + { + return new Client(array('host' => $host)); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.php new file mode 100644 index 0000000..d263e18 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisArraySessionHandlerTest.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\Tests\Session\Storage\Handler; + +class RedisArraySessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): \RedisArray + { + return new \RedisArray(array($host)); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.php new file mode 100644 index 0000000..7d85a59 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisClusterSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +class RedisClusterSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + public static function setupBeforeClass() + { + if (!class_exists('RedisCluster')) { + self::markTestSkipped('The RedisCluster class is required.'); + } + + if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) { + self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.'); + } + } + + protected function createRedisClient(string $host): \RedisCluster + { + return new \RedisCluster(null, explode(' ', getenv('REDIS_CLUSTER_HOSTS'))); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.php new file mode 100644 index 0000000..afdb6c5 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/RedisSessionHandlerTest.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\HttpFoundation\Tests\Session\Storage\Handler; + +class RedisSessionHandlerTest extends AbstractRedisSessionHandlerTestCase +{ + protected function createRedisClient(string $host): \Redis + { + $client = new \Redis(); + $client->connect($host); + + return $client; + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Handler/StrictSessionHandlerTest.php new file mode 100644 index 0000000..b02c41a --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MetadataBagTest.php new file mode 100644 index 0000000..69cf616 --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MockArraySessionStorageTest.php new file mode 100644 index 0000000..893e120 --- /dev/null +++ b/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\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; + +/** + * Test class for MockArraySessionStorage. + * + * @author Drak + */ +class MockArraySessionStorageTest extends TestCase +{ + /** + * @var MockArraySessionStorage + */ + private $storage; + + /** + * @var AttributeBag + */ + private $attributes; + + /** + * @var FlashBag + */ + private $flashes; + + private $data; + + protected function setUp() + { + $this->attributes = new AttributeBag(); + $this->flashes = new FlashBag(); + + $this->data = array( + $this->attributes->getStorageKey() => array('foo' => 'bar'), + $this->flashes->getStorageKey() => array('notice' => 'hello'), + ); + + $this->storage = new MockArraySessionStorage(); + $this->storage->registerBag($this->flashes); + $this->storage->registerBag($this->attributes); + $this->storage->setSessionData($this->data); + } + + protected function tearDown() + { + $this->data = null; + $this->flashes = null; + $this->attributes = null; + $this->storage = null; + } + + public function testStart() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $id = $this->storage->getId(); + $this->assertNotEquals('', $id); + $this->storage->start(); + $this->assertEquals($id, $this->storage->getId()); + } + + public function testRegenerate() + { + $this->storage->start(); + $id = $this->storage->getId(); + $this->storage->regenerate(); + $this->assertNotEquals($id, $this->storage->getId()); + $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all()); + $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll()); + + $id = $this->storage->getId(); + $this->storage->regenerate(true); + $this->assertNotEquals($id, $this->storage->getId()); + $this->assertEquals(array('foo' => 'bar'), $this->storage->getBag('attributes')->all()); + $this->assertEquals(array('notice' => 'hello'), $this->storage->getBag('flashes')->peekAll()); + } + + public function testGetId() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $this->assertNotEquals('', $this->storage->getId()); + } + + public function testClearClearsBags() + { + $this->storage->clear(); + + $this->assertSame(array(), $this->storage->getBag('attributes')->all()); + $this->assertSame(array(), $this->storage->getBag('flashes')->peekAll()); + } + + public function testClearStartsSession() + { + $this->storage->clear(); + + $this->assertTrue($this->storage->isStarted()); + } + + public function testClearWithNoBagsStartsSession() + { + $storage = new MockArraySessionStorage(); + + $storage->clear(); + + $this->assertTrue($storage->isStarted()); + } + + /** + * @expectedException \RuntimeException + */ + public function testUnstartedSave() + { + $this->storage->save(); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/MockFileSessionStorageTest.php new file mode 100644 index 0000000..1695798 --- /dev/null +++ b/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\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; + +/** + * Test class for MockFileSessionStorage. + * + * @author Drak + */ +class MockFileSessionStorageTest extends TestCase +{ + /** + * @var string + */ + private $sessionDir; + + /** + * @var MockFileSessionStorage + */ + protected $storage; + + protected function setUp() + { + $this->sessionDir = sys_get_temp_dir().'/sf2test'; + $this->storage = $this->getStorage(); + } + + protected function tearDown() + { + $this->sessionDir = null; + $this->storage = null; + array_map('unlink', glob($this->sessionDir.'/*.session')); + if (is_dir($this->sessionDir)) { + rmdir($this->sessionDir); + } + } + + public function testStart() + { + $this->assertEquals('', $this->storage->getId()); + $this->assertTrue($this->storage->start()); + $id = $this->storage->getId(); + $this->assertNotEquals('', $this->storage->getId()); + $this->assertTrue($this->storage->start()); + $this->assertEquals($id, $this->storage->getId()); + } + + public function testRegenerate() + { + $this->storage->start(); + $this->storage->getBag('attributes')->set('regenerate', 1234); + $this->storage->regenerate(); + $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate')); + $this->storage->regenerate(true); + $this->assertEquals(1234, $this->storage->getBag('attributes')->get('regenerate')); + } + + public function testGetId() + { + $this->assertEquals('', $this->storage->getId()); + $this->storage->start(); + $this->assertNotEquals('', $this->storage->getId()); + } + + public function testSave() + { + $this->storage->start(); + $id = $this->storage->getId(); + $this->assertNotEquals('108', $this->storage->getBag('attributes')->get('new')); + $this->assertFalse($this->storage->getBag('flashes')->has('newkey')); + $this->storage->getBag('attributes')->set('new', '108'); + $this->storage->getBag('flashes')->set('newkey', 'test'); + $this->storage->save(); + + $storage = $this->getStorage(); + $storage->setId($id); + $storage->start(); + $this->assertEquals('108', $storage->getBag('attributes')->get('new')); + $this->assertTrue($storage->getBag('flashes')->has('newkey')); + $this->assertEquals(array('test'), $storage->getBag('flashes')->peek('newkey')); + } + + public function testMultipleInstances() + { + $storage1 = $this->getStorage(); + $storage1->start(); + $storage1->getBag('attributes')->set('foo', 'bar'); + $storage1->save(); + + $storage2 = $this->getStorage(); + $storage2->setId($storage1->getId()); + $storage2->start(); + $this->assertEquals('bar', $storage2->getBag('attributes')->get('foo'), 'values persist between instances'); + } + + /** + * @expectedException \RuntimeException + */ + public function testSaveWithoutStart() + { + $storage1 = $this->getStorage(); + $storage1->save(); + } + + private function getStorage() + { + $storage = new MockFileSessionStorage($this->sessionDir); + $storage->registerBag(new FlashBag()); + $storage->registerBag(new AttributeBag()); + + return $storage; + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php new file mode 100644 index 0000000..52da294 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/NativeSessionStorageTest.php @@ -0,0 +1,294 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * Test class for NativeSessionStorage. + * + * @author Drak + * + * These tests require separate processes. + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class NativeSessionStorageTest extends TestCase +{ + private $savePath; + + protected function setUp() + { + $this->iniSet('session.save_handler', 'files'); + $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test'); + if (!is_dir($this->savePath)) { + mkdir($this->savePath); + } + } + + protected function tearDown() + { + session_write_close(); + array_map('unlink', glob($this->savePath.'/*')); + if (is_dir($this->savePath)) { + rmdir($this->savePath); + } + + $this->savePath = null; + } + + /** + * @return NativeSessionStorage + */ + protected function getStorage(array $options = array()) + { + $storage = new NativeSessionStorage($options); + $storage->registerBag(new AttributeBag()); + + return $storage; + } + + public function testBag() + { + $storage = $this->getStorage(); + $bag = new FlashBag(); + $storage->registerBag($bag); + $this->assertSame($bag, $storage->getBag($bag->getName())); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testRegisterBagException() + { + $storage = $this->getStorage(); + $storage->getBag('non_existing'); + } + + /** + * @expectedException \LogicException + */ + public function testRegisterBagForAStartedSessionThrowsException() + { + $storage = $this->getStorage(); + $storage->start(); + $storage->registerBag(new AttributeBag()); + } + + public function testGetId() + { + $storage = $this->getStorage(); + $this->assertSame('', $storage->getId(), 'Empty ID before starting session'); + + $storage->start(); + $id = $storage->getId(); + $this->assertInternalType('string', $id); + $this->assertNotSame('', $id); + + $storage->save(); + $this->assertSame($id, $storage->getId(), 'ID stays after saving session'); + } + + public function testRegenerate() + { + $storage = $this->getStorage(); + $storage->start(); + $id = $storage->getId(); + $storage->getBag('attributes')->set('lucky', 7); + $storage->regenerate(); + $this->assertNotEquals($id, $storage->getId()); + $this->assertEquals(7, $storage->getBag('attributes')->get('lucky')); + } + + public function testRegenerateDestroy() + { + $storage = $this->getStorage(); + $storage->start(); + $id = $storage->getId(); + $storage->getBag('attributes')->set('legs', 11); + $storage->regenerate(true); + $this->assertNotEquals($id, $storage->getId()); + $this->assertEquals(11, $storage->getBag('attributes')->get('legs')); + } + + public function testSessionGlobalIsUpToDateAfterIdRegeneration() + { + $storage = $this->getStorage(); + $storage->start(); + $storage->getBag('attributes')->set('lucky', 7); + $storage->regenerate(); + $storage->getBag('attributes')->set('lucky', 42); + + $this->assertEquals(42, $_SESSION['_sf2_attributes']['lucky']); + } + + public function testRegenerationFailureDoesNotFlagStorageAsStarted() + { + $storage = $this->getStorage(); + $this->assertFalse($storage->regenerate()); + $this->assertFalse($storage->isStarted()); + } + + public function testDefaultSessionCacheLimiter() + { + $this->iniSet('session.cache_limiter', 'nocache'); + + $storage = new NativeSessionStorage(); + $this->assertEquals('', ini_get('session.cache_limiter')); + } + + public function testExplicitSessionCacheLimiter() + { + $this->iniSet('session.cache_limiter', 'nocache'); + + $storage = new NativeSessionStorage(array('cache_limiter' => 'public')); + $this->assertEquals('public', ini_get('session.cache_limiter')); + } + + public function testCookieOptions() + { + $options = array( + 'cookie_lifetime' => 123456, + 'cookie_path' => '/my/cookie/path', + 'cookie_domain' => 'symfony.example.com', + 'cookie_secure' => true, + 'cookie_httponly' => false, + ); + + $this->getStorage($options); + $temp = session_get_cookie_params(); + $gco = array(); + + foreach ($temp as $key => $value) { + $gco['cookie_'.$key] = $value; + } + + $this->assertEquals($options, $gco); + } + + public function testSessionOptions() + { + if (\defined('HHVM_VERSION')) { + $this->markTestSkipped('HHVM is not handled in this test case.'); + } + + $options = array( + 'url_rewriter.tags' => 'a=href', + 'cache_expire' => '200', + ); + + $this->getStorage($options); + + $this->assertSame('a=href', ini_get('url_rewriter.tags')); + $this->assertSame('200', ini_get('session.cache_expire')); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testSetSaveHandlerException() + { + $storage = $this->getStorage(); + $storage->setSaveHandler(new \stdClass()); + } + + public function testSetSaveHandler() + { + $this->iniSet('session.save_handler', 'files'); + $storage = $this->getStorage(); + $storage->setSaveHandler(); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(null); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler())); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new NativeFileSessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler())); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + $storage->setSaveHandler(new NullSessionHandler()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); + } + + /** + * @expectedException \RuntimeException + */ + public function testStarted() + { + $storage = $this->getStorage(); + + $this->assertFalse($storage->getSaveHandler()->isActive()); + $this->assertFalse($storage->isStarted()); + + session_start(); + $this->assertTrue(isset($_SESSION)); + $this->assertTrue($storage->getSaveHandler()->isActive()); + + // PHP session might have started, but the storage driver has not, so false is correct here + $this->assertFalse($storage->isStarted()); + + $key = $storage->getMetadataBag()->getStorageKey(); + $this->assertArrayNotHasKey($key, $_SESSION); + $storage->start(); + } + + public function testRestart() + { + $storage = $this->getStorage(); + $storage->start(); + $id = $storage->getId(); + $storage->getBag('attributes')->set('lucky', 7); + $storage->save(); + $storage->start(); + $this->assertSame($id, $storage->getId(), 'Same session ID after restarting'); + $this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available'); + } + + public function testCanCreateNativeSessionStorageWhenSessionAlreadyStarted() + { + session_start(); + $this->getStorage(); + + // Assert no exception has been thrown by `getStorage()` + $this->addToAssertionCount(1); + } + + public function testSetSessionOptionsOnceSessionStartedIsIgnored() + { + session_start(); + $this->getStorage(array( + 'name' => 'something-else', + )); + + // Assert no exception has been thrown by `getStorage()` + $this->addToAssertionCount(1); + } + + public function testGetBagsOnceSessionStartedIsIgnored() + { + session_start(); + $bag = new AttributeBag(); + $bag->setName('flashes'); + + $storage = $this->getStorage(); + $storage->registerBag($bag); + + $this->assertEquals($storage->getBag('flashes'), $bag); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.php new file mode 100644 index 0000000..303f36d --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/PhpBridgeSessionStorageTest.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\HttpFoundation\Tests\Session\Storage; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; + +/** + * Test class for PhpSessionStorage. + * + * @author Drak + * + * These tests require separate processes. + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class PhpBridgeSessionStorageTest extends TestCase +{ + private $savePath; + + protected function setUp() + { + $this->iniSet('session.save_handler', 'files'); + $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sf2test'); + if (!is_dir($this->savePath)) { + mkdir($this->savePath); + } + } + + protected function tearDown() + { + session_write_close(); + array_map('unlink', glob($this->savePath.'/*')); + if (is_dir($this->savePath)) { + rmdir($this->savePath); + } + + $this->savePath = null; + } + + /** + * @return PhpBridgeSessionStorage + */ + protected function getStorage() + { + $storage = new PhpBridgeSessionStorage(); + $storage->registerBag(new AttributeBag()); + + return $storage; + } + + public function testPhpSession() + { + $storage = $this->getStorage(); + + $this->assertNotSame(\PHP_SESSION_ACTIVE, session_status()); + $this->assertFalse($storage->isStarted()); + + session_start(); + $this->assertTrue(isset($_SESSION)); + $this->assertSame(\PHP_SESSION_ACTIVE, session_status()); + // PHP session might have started, but the storage driver has not, so false is correct here + $this->assertFalse($storage->isStarted()); + + $key = $storage->getMetadataBag()->getStorageKey(); + $this->assertArrayNotHasKey($key, $_SESSION); + $storage->start(); + $this->assertArrayHasKey($key, $_SESSION); + } + + public function testClear() + { + $storage = $this->getStorage(); + session_start(); + $_SESSION['drak'] = 'loves symfony'; + $storage->getBag('attributes')->set('symfony', 'greatness'); + $key = $storage->getBag('attributes')->getStorageKey(); + $this->assertEquals($_SESSION[$key], array('symfony' => 'greatness')); + $this->assertEquals($_SESSION['drak'], 'loves symfony'); + $storage->clear(); + $this->assertEquals($_SESSION[$key], array()); + $this->assertEquals($_SESSION['drak'], 'loves symfony'); + } +} diff --git a/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/AbstractProxyTest.php new file mode 100644 index 0000000..cbb291f --- /dev/null +++ b/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/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php new file mode 100644 index 0000000..0b48250 --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/Session/Storage/Proxy/SessionHandlerProxyTest.php @@ -0,0 +1,157 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Proxy; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * Tests for SessionHandlerProxy class. + * + * @author Drak + * + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ +class SessionHandlerProxyTest extends TestCase +{ + /** + * @var \PHPUnit_Framework_MockObject_Matcher + */ + private $mock; + + /** + * @var SessionHandlerProxy + */ + private $proxy; + + protected function setUp() + { + $this->mock = $this->getMockBuilder('SessionHandlerInterface')->getMock(); + $this->proxy = new SessionHandlerProxy($this->mock); + } + + protected function tearDown() + { + $this->mock = null; + $this->proxy = null; + } + + public function testOpenTrue() + { + $this->mock->expects($this->once()) + ->method('open') + ->will($this->returnValue(true)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->open('name', 'id'); + $this->assertFalse($this->proxy->isActive()); + } + + public function testOpenFalse() + { + $this->mock->expects($this->once()) + ->method('open') + ->will($this->returnValue(false)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->open('name', 'id'); + $this->assertFalse($this->proxy->isActive()); + } + + public function testClose() + { + $this->mock->expects($this->once()) + ->method('close') + ->will($this->returnValue(true)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->close(); + $this->assertFalse($this->proxy->isActive()); + } + + public function testCloseFalse() + { + $this->mock->expects($this->once()) + ->method('close') + ->will($this->returnValue(false)); + + $this->assertFalse($this->proxy->isActive()); + $this->proxy->close(); + $this->assertFalse($this->proxy->isActive()); + } + + public function testRead() + { + $this->mock->expects($this->once()) + ->method('read'); + + $this->proxy->read('id'); + } + + public function testWrite() + { + $this->mock->expects($this->once()) + ->method('write'); + + $this->proxy->write('id', 'data'); + } + + public function testDestroy() + { + $this->mock->expects($this->once()) + ->method('destroy'); + + $this->proxy->destroy('id'); + } + + public function testGc() + { + $this->mock->expects($this->once()) + ->method('gc'); + + $this->proxy->gc(86400); + } + + /** + * @requires PHPUnit 5.1 + */ + public function testValidateId() + { + $mock = $this->getMockBuilder(array('SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'))->getMock(); + $mock->expects($this->once()) + ->method('validateId'); + + $proxy = new SessionHandlerProxy($mock); + $proxy->validateId('id'); + + $this->assertTrue($this->proxy->validateId('id')); + } + + /** + * @requires PHPUnit 5.1 + */ + public function testUpdateTimestamp() + { + $mock = $this->getMockBuilder(array('SessionHandlerInterface', 'SessionUpdateTimestampHandlerInterface'))->getMock(); + $mock->expects($this->once()) + ->method('updateTimestamp'); + + $proxy = new SessionHandlerProxy($mock); + $proxy->updateTimestamp('id', 'data'); + + $this->mock->expects($this->once()) + ->method('write'); + + $this->proxy->updateTimestamp('id', 'data'); + } +} diff --git a/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php b/vendor/symfony/http-foundation/Tests/StreamedResponseTest.php new file mode 100644 index 0000000..699222e --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/StreamedResponseTest.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\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\StreamedResponse; + +class StreamedResponseTest extends TestCase +{ + public function testConstructor() + { + $response = new StreamedResponse(function () { echo 'foo'; }, 404, array('Content-Type' => 'text/plain')); + + $this->assertEquals(404, $response->getStatusCode()); + $this->assertEquals('text/plain', $response->headers->get('Content-Type')); + } + + public function testPrepareWith11Protocol() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $request = Request::create('/'); + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.1'); + + $response->prepare($request); + + $this->assertEquals('1.1', $response->getProtocolVersion()); + $this->assertNotEquals('chunked', $response->headers->get('Transfer-Encoding'), 'Apache assumes responses with a Transfer-Encoding header set to chunked to already be encoded.'); + } + + public function testPrepareWith10Protocol() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $request = Request::create('/'); + $request->server->set('SERVER_PROTOCOL', 'HTTP/1.0'); + + $response->prepare($request); + + $this->assertEquals('1.0', $response->getProtocolVersion()); + $this->assertNull($response->headers->get('Transfer-Encoding')); + } + + public function testPrepareWithHeadRequest() + { + $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Content-Length' => '123')); + $request = Request::create('/', 'HEAD'); + + $response->prepare($request); + + $this->assertSame('123', $response->headers->get('Content-Length')); + } + + public function testPrepareWithCacheHeaders() + { + $response = new StreamedResponse(function () { echo 'foo'; }, 200, array('Cache-Control' => 'max-age=600, public')); + $request = Request::create('/', 'GET'); + + $response->prepare($request); + $this->assertEquals('max-age=600, public', $response->headers->get('Cache-Control')); + } + + public function testSendContent() + { + $called = 0; + + $response = new StreamedResponse(function () use (&$called) { ++$called; }); + + $response->sendContent(); + $this->assertEquals(1, $called); + + $response->sendContent(); + $this->assertEquals(1, $called); + } + + /** + * @expectedException \LogicException + */ + public function testSendContentWithNonCallable() + { + $response = new StreamedResponse(null); + $response->sendContent(); + } + + /** + * @expectedException \LogicException + */ + public function testSetContent() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $response->setContent('foo'); + } + + public function testGetContent() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $this->assertFalse($response->getContent()); + } + + public function testCreate() + { + $response = StreamedResponse::create(function () {}, 204); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response); + $this->assertEquals(204, $response->getStatusCode()); + } + + public function testReturnThis() + { + $response = new StreamedResponse(function () {}); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendContent()); + + $response = new StreamedResponse(function () {}); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $response->sendHeaders()); + } + + public function testSetNotModified() + { + $response = new StreamedResponse(function () { echo 'foo'; }); + $modified = $response->setNotModified(); + $this->assertObjectHasAttribute('headers', $modified); + $this->assertObjectHasAttribute('content', $modified); + $this->assertObjectHasAttribute('version', $modified); + $this->assertObjectHasAttribute('statusCode', $modified); + $this->assertObjectHasAttribute('statusText', $modified); + $this->assertObjectHasAttribute('charset', $modified); + $this->assertEquals(304, $modified->getStatusCode()); + + ob_start(); + $modified->sendContent(); + $string = ob_get_clean(); + $this->assertEmpty($string); + } +} diff --git a/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng b/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng new file mode 100644 index 0000000..73708ca --- /dev/null +++ b/vendor/symfony/http-foundation/Tests/schema/http-status-codes.rng @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng b/vendor/symfony/http-foundation/Tests/schema/iana-registry.rng new file mode 100644 index 0000000..b9c3ca9 --- /dev/null +++ b/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/vendor/symfony/http-foundation/composer.json b/vendor/symfony/http-foundation/composer.json new file mode 100644 index 0000000..ef4bf82 --- /dev/null +++ b/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": "^7.1.3", + "symfony/polyfill-mbstring": "~1.1" + }, + "require-dev": { + "predis/predis": "~1.0", + "symfony/expression-language": "~3.4|~4.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/http-foundation/phpunit.xml.dist b/vendor/symfony/http-foundation/phpunit.xml.dist new file mode 100644 index 0000000..c1d61f8 --- /dev/null +++ b/vendor/symfony/http-foundation/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/http-kernel/.gitignore b/vendor/symfony/http-kernel/.gitignore new file mode 100644 index 0000000..94a6a25 --- /dev/null +++ b/vendor/symfony/http-kernel/.gitignore @@ -0,0 +1,5 @@ +vendor/ +composer.lock +phpunit.xml +Tests/Fixtures/cache/ +Tests/Fixtures/logs/ diff --git a/vendor/symfony/http-kernel/Bundle/Bundle.php b/vendor/symfony/http-kernel/Bundle/Bundle.php new file mode 100644 index 0000000..23e0933 --- /dev/null +++ b/vendor/symfony/http-kernel/Bundle/Bundle.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\HttpKernel\Bundle; + +use Symfony\Component\Console\Application; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +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 name (the class short name). + * + * @return string The Bundle name + */ + final public function getName() + { + if (null === $this->name) { + $this->parseClassName(); + } + + return $this->name; + } + + public function registerCommands(Application $application) + { + } + + /** + * 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/vendor/symfony/http-kernel/Bundle/BundleInterface.php b/vendor/symfony/http-kernel/Bundle/BundleInterface.php new file mode 100644 index 0000000..88a95d8 --- /dev/null +++ b/vendor/symfony/http-kernel/Bundle/BundleInterface.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\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 (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/vendor/symfony/http-kernel/CHANGELOG.md b/vendor/symfony/http-kernel/CHANGELOG.md new file mode 100644 index 0000000..419e783 --- /dev/null +++ b/vendor/symfony/http-kernel/CHANGELOG.md @@ -0,0 +1,175 @@ +CHANGELOG +========= + +4.0.0 +----- + + * removed the `DataCollector::varToString()` method, use `DataCollector::cloneVar()` + instead + * using the `DataCollector::cloneVar()` method requires the VarDumper component + * removed the `ValueExporter` class + * removed `ControllerResolverInterface::getArguments()` + * removed `TraceableControllerResolver::getArguments()` + * removed `ControllerResolver::getArguments()` and the ability to resolve arguments + * removed the `argument_resolver` service dependency from the `debug.controller_resolver` + * removed `LazyLoadingFragmentHandler::addRendererService()` + * removed `Psr6CacheClearer::addPool()` + * removed `Extension::addClassesToCompile()` and `Extension::getClassesToCompile()` + * removed `Kernel::loadClassCache()`, `Kernel::doLoadClassCache()`, `Kernel::setClassCache()`, + and `Kernel::getEnvParameters()` + * support for the `X-Status-Code` when handling exceptions in the `HttpKernel` + has been dropped, use the `HttpKernel::allowCustomResponseCode()` method + instead + * removed convention-based commands registration + * removed the `ChainCacheClearer::add()` method + * removed the `CacheaWarmerAggregate::add()` and `setWarmers()` methods + * made `CacheWarmerAggregate` and `ChainCacheClearer` classes final + +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/vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php b/vendor/symfony/http-kernel/CacheClearer/CacheClearerInterface.php new file mode 100644 index 0000000..675c584 --- /dev/null +++ b/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/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php b/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.php new file mode 100644 index 0000000..a646119 --- /dev/null +++ b/vendor/symfony/http-kernel/CacheClearer/ChainCacheClearer.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\CacheClearer; + +/** + * ChainCacheClearer. + * + * @author Dustin Dobervich + * + * @final + */ +class ChainCacheClearer implements CacheClearerInterface +{ + private $clearers; + + public function __construct(iterable $clearers = array()) + { + $this->clearers = $clearers; + } + + /** + * {@inheritdoc} + */ + public function clear($cacheDir) + { + foreach ($this->clearers as $clearer) { + $clearer->clear($cacheDir); + } + } +} diff --git a/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php b/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.php new file mode 100644 index 0000000..f54ca96 --- /dev/null +++ b/vendor/symfony/http-kernel/CacheClearer/Psr6CacheClearer.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\CacheClearer; + +/** + * @author Nicolas Grekas + */ +class Psr6CacheClearer implements CacheClearerInterface +{ + private $pools = array(); + + public function __construct(array $pools = array()) + { + $this->pools = $pools; + } + + 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/vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php b/vendor/symfony/http-kernel/CacheWarmer/CacheWarmer.php new file mode 100644 index 0000000..52dc2ad --- /dev/null +++ b/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/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php b/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.php new file mode 100644 index 0000000..66dbe6c --- /dev/null +++ b/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerAggregate.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\CacheWarmer; + +/** + * Aggregates several cache warmers into a single one. + * + * @author Fabien Potencier + * + * @final + */ +class CacheWarmerAggregate implements CacheWarmerInterface +{ + private $warmers; + private $optionalsEnabled = false; + + public function __construct(iterable $warmers = array()) + { + $this->warmers = $warmers; + } + + 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; + } +} diff --git a/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php b/vendor/symfony/http-kernel/CacheWarmer/CacheWarmerInterface.php new file mode 100644 index 0000000..8fece5e --- /dev/null +++ b/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/vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php b/vendor/symfony/http-kernel/CacheWarmer/WarmableInterface.php new file mode 100644 index 0000000..25d8ee8 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Client.php b/vendor/symfony/http-kernel/Client.php new file mode 100644 index 0000000..380dd15 --- /dev/null +++ b/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\CookieJar; +use Symfony\Component\BrowserKit\History; +use Symfony\Component\BrowserKit\Request as DomRequest; +use Symfony\Component\BrowserKit\Response as DomResponse; +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/vendor/symfony/http-kernel/Config/FileLocator.php b/vendor/symfony/http-kernel/Config/FileLocator.php new file mode 100644 index 0000000..926f0d1 --- /dev/null +++ b/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, string $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/vendor/symfony/http-kernel/Controller/ArgumentResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver.php new file mode 100644 index 0000000..142a6d5 --- /dev/null +++ b/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, iterable $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(): iterable + { + return array( + new RequestAttributeValueResolver(), + new RequestValueResolver(), + new SessionValueResolver(), + new DefaultValueResolver(), + new VariadicValueResolver(), + ); + } +} diff --git a/vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver/DefaultValueResolver.php new file mode 100644 index 0000000..e58fd3a --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestAttributeValueResolver.php new file mode 100644 index 0000000..05be372 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver/RequestValueResolver.php new file mode 100644 index 0000000..2a5060a --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php new file mode 100644 index 0000000..7bc195f --- /dev/null +++ b/vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.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\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]; + } elseif (!\is_string($controller) || '' === $controller) { + return false; + } + + if ('\\' === $controller[0]) { + $controller = ltrim($controller, '\\'); + } + + return $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]; + } + + if ('\\' === $controller[0]) { + $controller = ltrim($controller, '\\'); + } + + yield $this->container->get($controller)->get($argument->getName()); + } +} diff --git a/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver/SessionValueResolver.php new file mode 100644 index 0000000..9e656d2 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php b/vendor/symfony/http-kernel/Controller/ArgumentResolver/VariadicValueResolver.php new file mode 100644 index 0000000..7ee2d7a --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php b/vendor/symfony/http-kernel/Controller/ArgumentResolverInterface.php new file mode 100644 index 0000000..5c51230 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php b/vendor/symfony/http-kernel/Controller/ArgumentValueResolverInterface.php new file mode 100644 index 0000000..fd7b09e --- /dev/null +++ b/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/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php b/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php new file mode 100644 index 0000000..1704c74 --- /dev/null +++ b/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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) { + } + + $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/vendor/symfony/http-kernel/Controller/ControllerReference.php b/vendor/symfony/http-kernel/Controller/ControllerReference.php new file mode 100644 index 0000000..f424fdc --- /dev/null +++ b/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(string $controller, array $attributes = array(), array $query = array()) + { + $this->controller = $controller; + $this->attributes = $attributes; + $this->query = $query; + } +} diff --git a/vendor/symfony/http-kernel/Controller/ControllerResolver.php b/vendor/symfony/http-kernel/Controller/ControllerResolver.php new file mode 100644 index 0000000..512b54d --- /dev/null +++ b/vendor/symfony/http-kernel/Controller/ControllerResolver.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\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 ControllerResolverInterface +{ + private $logger; + + public function __construct(LoggerInterface $logger = null) + { + $this->logger = $logger; + } + + /** + * {@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; + } + + /** + * 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/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php b/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.php new file mode 100644 index 0000000..30b73f6 --- /dev/null +++ b/vendor/symfony/http-kernel/Controller/ControllerResolverInterface.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; + +/** + * 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); +} diff --git a/vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php b/vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php new file mode 100644 index 0000000..3984812 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * @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/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php b/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php new file mode 100644 index 0000000..bf6b6aa --- /dev/null +++ b/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.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\HttpFoundation\Request; +use Symfony\Component\Stopwatch\Stopwatch; + +/** + * @author Fabien Potencier + */ +class TraceableControllerResolver implements ControllerResolverInterface +{ + private $resolver; + private $stopwatch; + + public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch) + { + $this->resolver = $resolver; + $this->stopwatch = $stopwatch; + } + + /** + * {@inheritdoc} + */ + public function getController(Request $request) + { + $e = $this->stopwatch->start('controller.get_callable'); + + $ret = $this->resolver->getController($request); + + $e->stop(); + + return $ret; + } +} diff --git a/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php b/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.php new file mode 100644 index 0000000..4ea8ea6 --- /dev/null +++ b/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadata.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\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; + + public function __construct(string $name, ?string $type, bool $isVariadic, bool $hasDefaultValue, $defaultValue, bool $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/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php b/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.php new file mode 100644 index 0000000..10e851b --- /dev/null +++ b/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactory.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\ControllerMetadata; + +/** + * Builds {@see ArgumentMetadata} objects based on the given Controller. + * + * @author Iltar van der Berg + */ +final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface +{ + /** + * {@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, $reflection), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull()); + } + + return $arguments; + } + + /** + * Returns an associated type to the given parameter if available. + * + * @param \ReflectionParameter $parameter + * + * @return null|string + */ + private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function) + { + if (!$type = $parameter->getType()) { + return; + } + $name = $type->getName(); + $lcName = strtolower($name); + + if ('self' !== $lcName && 'parent' !== $lcName) { + return $name; + } + if (!$function instanceof \ReflectionMethod) { + return; + } + if ('self' === $lcName) { + return $function->getDeclaringClass()->name; + } + if ($parent = $function->getDeclaringClass()->getParentClass()) { + return $parent->name; + } + } +} diff --git a/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php b/vendor/symfony/http-kernel/ControllerMetadata/ArgumentMetadataFactoryInterface.php new file mode 100644 index 0000000..6ea179d --- /dev/null +++ b/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/vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php b/vendor/symfony/http-kernel/DataCollector/AjaxDataCollector.php new file mode 100644 index 0000000..370a874 --- /dev/null +++ b/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/vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php b/vendor/symfony/http-kernel/DataCollector/ConfigDataCollector.php new file mode 100644 index 0000000..76ad4a8 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelInterface; +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(string $name = null, string $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/vendor/symfony/http-kernel/DataCollector/DataCollector.php b/vendor/symfony/http-kernel/DataCollector/DataCollector.php new file mode 100644 index 0000000..a3d8c99 --- /dev/null +++ b/vendor/symfony/http-kernel/DataCollector/DataCollector.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\DataCollector; + +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 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)) { + throw new \LogicException(sprintf('The VarDumper component is needed for the %s() method. Install symfony/var-dumper version 3.4 or above.', __METHOD__)); + } + $this->cloner = new VarCloner(); + $this->cloner->setMaxItems(-1); + $this->cloner->addCasters($this->getCasters()); + } + + return $this->cloner->cloneVar($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/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php b/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.php new file mode 100644 index 0000000..549fd5d --- /dev/null +++ b/vendor/symfony/http-kernel/DataCollector/DataCollectorInterface.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\DataCollector; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * DataCollectorInterface. + * + * @author Fabien Potencier + */ +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(); + + /** + * Resets this data collector to its initial state. + */ + public function reset(); +} diff --git a/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php b/vendor/symfony/http-kernel/DataCollector/DumpDataCollector.php new file mode 100644 index 0000000..6ce0582 --- /dev/null +++ b/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\DataDumperInterface; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; +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, string $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->dumper) { + $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 = true; + $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 (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true) && 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/vendor/symfony/http-kernel/DataCollector/EventDataCollector.php b/vendor/symfony/http-kernel/DataCollector/EventDataCollector.php new file mode 100644 index 0000000..ec177d9 --- /dev/null +++ b/vendor/symfony/http-kernel/DataCollector/EventDataCollector.php @@ -0,0 +1,117 @@ + + * + * 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\EventDispatcher\Debug\TraceableEventDispatcherInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * EventDataCollector. + * + * @author Fabien Potencier + */ +class EventDataCollector extends DataCollector implements LateDataCollectorInterface +{ + protected $dispatcher; + + public function __construct(EventDispatcherInterface $dispatcher = null) + { + $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) { + $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/vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php b/vendor/symfony/http-kernel/DataCollector/ExceptionDataCollector.php new file mode 100644 index 0000000..7a25f14 --- /dev/null +++ b/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/vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php b/vendor/symfony/http-kernel/DataCollector/LateDataCollectorInterface.php new file mode 100644 index 0000000..012332d --- /dev/null +++ b/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/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php b/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php new file mode 100644 index 0000000..7a7d5d8 --- /dev/null +++ b/vendor/symfony/http-kernel/DataCollector/LoggerDataCollector.php @@ -0,0 +1,274 @@ + + * + * 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, string $containerPathPrefix = null) + { + if (null !== $logger && $logger instanceof DebugLoggerInterface) { + $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 instanceof DebugLoggerInterface) { + $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/vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php b/vendor/symfony/http-kernel/DataCollector/MemoryDataCollector.php new file mode 100644 index 0000000..310b2f9 --- /dev/null +++ b/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/vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php b/vendor/symfony/http-kernel/DataCollector/RequestDataCollector.php new file mode 100644 index 0000000..e415833 --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +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\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * @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/vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php b/vendor/symfony/http-kernel/DataCollector/RouterDataCollector.php new file mode 100644 index 0000000..481747b --- /dev/null +++ b/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\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +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/vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php b/vendor/symfony/http-kernel/DataCollector/TimeDataCollector.php new file mode 100644 index 0000000..e489d77 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Debug/FileLinkFormatter.php b/vendor/symfony/http-kernel/Debug/FileLinkFormatter.php new file mode 100644 index 0000000..bafb509 --- /dev/null +++ b/vendor/symfony/http-kernel/Debug/FileLinkFormatter.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\Debug; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Exception\ExceptionInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * Formats debug file links. + * + * @author Jérémy Romey + */ +class FileLinkFormatter implements \Serializable +{ + private $fileLinkFormat; + private $requestStack; + private $baseDir; + private $urlFormat; + + /** + * @param string|\Closure $urlFormat the URL format, or a closure that returns it on-demand + */ + public function __construct($fileLinkFormat = null, RequestStack $requestStack = null, string $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) + { + $this->fileLinkFormat = unserialize($serialized, array('allowed_classes' => false)); + } + + /** + * @internal + */ + public static function generateUrlFormat(UrlGeneratorInterface $router, $routeName, $queryString) + { + try { + return $router->generate($routeName).$queryString; + } catch (ExceptionInterface $e) { + return null; + } + } + + private function getFileLinkFormat() + { + if ($this->fileLinkFormat) { + return $this->fileLinkFormat; + } + if ($this->requestStack && $this->baseDir && $this->urlFormat) { + $request = $this->requestStack->getMasterRequest(); + if ($request instanceof Request) { + if ($this->urlFormat instanceof \Closure && !$this->urlFormat = \call_user_func($this->urlFormat)) { + return; + } + + return array( + $request->getSchemeAndHttpHost().$request->getBaseUrl().$this->urlFormat, + $this->baseDir.\DIRECTORY_SEPARATOR, '', + ); + } + } + } +} diff --git a/vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php b/vendor/symfony/http-kernel/Debug/TraceableEventDispatcher.php new file mode 100644 index 0000000..ddf4fa7 --- /dev/null +++ b/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\EventDispatcher\Event; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * 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/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php b/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php new file mode 100644 index 0000000..3e054a7 --- /dev/null +++ b/vendor/symfony/http-kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php @@ -0,0 +1,145 @@ + + * + * 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\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +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) + { + $annotatedClasses = array(); + foreach ($container->getExtensions() as $extension) { + if ($extension instanceof Extension) { + $annotatedClasses = array_merge($annotatedClasses, $extension->getAnnotatedClassesToCompile()); + } + } + + $existingClasses = $this->getClassesInComposerClassMaps(); + + $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/vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php b/vendor/symfony/http-kernel/DependencyInjection/ConfigurableExtension.php new file mode 100644 index 0000000..072c35f --- /dev/null +++ b/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/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php b/vendor/symfony/http-kernel/DependencyInjection/ControllerArgumentValueResolverPass.php new file mode 100644 index 0000000..b3a2506 --- /dev/null +++ b/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(string $argumentResolverService = 'argument_resolver', string $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/vendor/symfony/http-kernel/DependencyInjection/Extension.php b/vendor/symfony/http-kernel/DependencyInjection/Extension.php new file mode 100644 index 0000000..6478755 --- /dev/null +++ b/vendor/symfony/http-kernel/DependencyInjection/Extension.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\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 $annotatedClasses = array(); + + /** + * Gets the annotated classes to cache. + * + * @return array An array of classes + */ + public function getAnnotatedClassesToCompile() + { + return $this->annotatedClasses; + } + + /** + * 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/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php b/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.php new file mode 100644 index 0000000..2a8c1b1 --- /dev/null +++ b/vendor/symfony/http-kernel/DependencyInjection/FragmentRendererPass.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\HttpKernel\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; +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; + + public function __construct(string $handlerService = 'fragment.handler', string $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/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php b/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.php new file mode 100644 index 0000000..4bb4790 --- /dev/null +++ b/vendor/symfony/http-kernel/DependencyInjection/LazyLoadingFragmentHandler.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\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; + private $initialized = array(); + + public function __construct(ContainerInterface $container, RequestStack $requestStack, bool $debug = false) + { + $this->container = $container; + + parent::__construct($requestStack, array(), $debug); + } + + /** + * {@inheritdoc} + */ + public function render($uri, $renderer = 'inline', array $options = array()) + { + 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/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php b/vendor/symfony/http-kernel/DependencyInjection/LoggerPass.php new file mode 100644 index 0000000..b6df1f6 --- /dev/null +++ b/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\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Log\Logger; + +/** + * 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/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php b/vendor/symfony/http-kernel/DependencyInjection/MergeExtensionConfigurationPass.php new file mode 100644 index 0000000..1dbf7f7 --- /dev/null +++ b/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/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php new file mode 100644 index 0000000..7747c62 --- /dev/null +++ b/vendor/symfony/http-kernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.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\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; +use Symfony\Component\HttpFoundation\Request; + +/** + * Creates the service-locators required by ServiceValueResolver. + * + * @author Nicolas Grekas + */ +class RegisterControllerArgumentLocatorsPass implements CompilerPassInterface +{ + private $resolverServiceId; + private $controllerTag; + + public function __construct(string $resolverServiceId = 'argument_resolver.service', string $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 (Request::class === $type) { + 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/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php b/vendor/symfony/http-kernel/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPass.php new file mode 100644 index 0000000..c741269 --- /dev/null +++ b/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(string $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/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php b/vendor/symfony/http-kernel/DependencyInjection/ResettableServicePass.php new file mode 100644 index 0000000..564f879 --- /dev/null +++ b/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(string $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/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php b/vendor/symfony/http-kernel/DependencyInjection/ServicesResetter.php new file mode 100644 index 0000000..b82d2fe --- /dev/null +++ b/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/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php b/vendor/symfony/http-kernel/Event/FilterControllerArgumentsEvent.php new file mode 100644 index 0000000..5e178f2 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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, ?int $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/vendor/symfony/http-kernel/Event/FilterControllerEvent.php b/vendor/symfony/http-kernel/Event/FilterControllerEvent.php new file mode 100644 index 0000000..7eab449 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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, ?int $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/vendor/symfony/http-kernel/Event/FilterResponseEvent.php b/vendor/symfony/http-kernel/Event/FilterResponseEvent.php new file mode 100644 index 0000000..04589f7 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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, int $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/vendor/symfony/http-kernel/Event/FinishRequestEvent.php b/vendor/symfony/http-kernel/Event/FinishRequestEvent.php new file mode 100644 index 0000000..ee72484 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Event/GetResponseEvent.php b/vendor/symfony/http-kernel/Event/GetResponseEvent.php new file mode 100644 index 0000000..c25a0f1 --- /dev/null +++ b/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|null + */ + 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/vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php b/vendor/symfony/http-kernel/Event/GetResponseForControllerResultEvent.php new file mode 100644 index 0000000..a008282 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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, int $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/vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php b/vendor/symfony/http-kernel/Event/GetResponseForExceptionEvent.php new file mode 100644 index 0000000..7fab006 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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, int $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/vendor/symfony/http-kernel/Event/KernelEvent.php b/vendor/symfony/http-kernel/Event/KernelEvent.php new file mode 100644 index 0000000..f3db8a6 --- /dev/null +++ b/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\EventDispatcher\Event; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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, ?int $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/vendor/symfony/http-kernel/Event/PostResponseEvent.php b/vendor/symfony/http-kernel/Event/PostResponseEvent.php new file mode 100644 index 0000000..0981e64 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php b/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.php new file mode 100644 index 0000000..41187d4 --- /dev/null +++ b/vendor/symfony/http-kernel/EventListener/AbstractSessionListener.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\HttpKernel\EventListener; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * Sets the session in the request. + * + * @author Johannes M. Schmitt + */ +abstract class AbstractSessionListener implements EventSubscriberInterface +{ + private $sessionUsageStack = array(); + + public function onKernelRequest(GetResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $request = $event->getRequest(); + $session = $this->getSession(); + $this->sessionUsageStack[] = $session instanceof Session ? $session->getUsageIndex() : null; + 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 instanceof Session ? $session->getUsageIndex() !== end($this->sessionUsageStack) : $session->isStarted()) { + $event->getResponse() + ->setPrivate() + ->setMaxAge(0) + ->headers->addCacheControlDirective('must-revalidate'); + } + } + + /** + * @internal + */ + public function onFinishRequest(FinishRequestEvent $event) + { + if ($event->isMasterRequest()) { + array_pop($this->sessionUsageStack); + } + } + + 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), + KernelEvents::FINISH_REQUEST => array('onFinishRequest'), + ); + } + + /** + * Gets the session object. + * + * @return SessionInterface|null A SessionInterface instance or null if no session is available + */ + abstract protected function getSession(); +} diff --git a/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php b/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php new file mode 100644 index 0000000..cb5da50 --- /dev/null +++ b/vendor/symfony/http-kernel/EventListener/AbstractTestSessionListener.php @@ -0,0 +1,100 @@ + + * + * 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\Cookie; +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; + +/** + * TestSessionListener. + * + * Saves session in test environment. + * + * @author Bulat Shakirzyanov + * @author Fabien Potencier + */ +abstract class AbstractTestSessionListener implements EventSubscriberInterface +{ + private $sessionId; + + 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())) { + $this->sessionId = $cookies->get($session->getName()); + $session->setId($this->sessionId); + } + } + + /** + * 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() || (null !== $this->sessionId && $session->getId() !== $this->sessionId) : $wasStarted) { + $params = session_get_cookie_params(); + + foreach ($event->getResponse()->headers->getCookies() as $cookie) { + if ($session->getName() === $cookie->getName() && $params['path'] === $cookie->getPath() && $params['domain'] == $cookie->getDomain()) { + return; + } + } + + $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'])); + $this->sessionId = $session->getId(); + } + } + + 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/vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php b/vendor/symfony/http-kernel/EventListener/AddRequestFormatsListener.php new file mode 100644 index 0000000..5ec5284 --- /dev/null +++ b/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\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * 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/vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php b/vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php new file mode 100644 index 0000000..47fe05c --- /dev/null +++ b/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\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleEvent; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +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; + +/** + * 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, ?int $throwAt = E_ALL, bool $scream = true, $fileLinkFormat = null, bool $scope = true) + { + $this->exceptionHandler = $exceptionHandler; + $this->logger = $logger; + $this->levels = null === $levels ? E_ALL : $levels; + $this->throwAt = \is_int($throwAt) ? $throwAt : (null === $throwAt ? null : ($throwAt ? E_ALL : null)); + $this->scream = $scream; + $this->fileLinkFormat = $fileLinkFormat; + $this->scope = $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/vendor/symfony/http-kernel/EventListener/DumpListener.php b/vendor/symfony/http-kernel/EventListener/DumpListener.php new file mode 100644 index 0000000..de19e13 --- /dev/null +++ b/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/vendor/symfony/http-kernel/EventListener/ExceptionListener.php b/vendor/symfony/http-kernel/EventListener/ExceptionListener.php new file mode 100644 index 0000000..147e090 --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * 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($wrapper instanceof \Exception ? \Exception::class : \Error::class, '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/vendor/symfony/http-kernel/EventListener/FragmentListener.php b/vendor/symfony/http-kernel/EventListener/FragmentListener.php new file mode 100644 index 0000000..b47aa7c --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\UriSigner; + +/** + * 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, string $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/vendor/symfony/http-kernel/EventListener/LocaleListener.php b/vendor/symfony/http-kernel/EventListener/LocaleListener.php new file mode 100644 index 0000000..1067e8a --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * 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, string $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/vendor/symfony/http-kernel/EventListener/ProfilerListener.php b/vendor/symfony/http-kernel/EventListener/ProfilerListener.php new file mode 100644 index 0000000..e282624 --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RequestMatcherInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Profiler\Profiler; + +/** + * 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, bool $onlyException = false, bool $onlyMasterRequests = false) + { + $this->profiler = $profiler; + $this->matcher = $matcher; + $this->onlyException = $onlyException; + $this->onlyMasterRequests = $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/vendor/symfony/http-kernel/EventListener/ResponseListener.php b/vendor/symfony/http-kernel/EventListener/ResponseListener.php new file mode 100644 index 0000000..0550c02 --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * ResponseListener fixes the Response headers based on the Request. + * + * @author Fabien Potencier + */ +class ResponseListener implements EventSubscriberInterface +{ + private $charset; + + public function __construct(string $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/vendor/symfony/http-kernel/EventListener/RouterListener.php b/vendor/symfony/http-kernel/EventListener/RouterListener.php new file mode 100644 index 0000000..3919284 --- /dev/null +++ b/vendor/symfony/http-kernel/EventListener/RouterListener.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\EventListener; + +use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * 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, string $projectDir = null, bool $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) { + $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 function onKernelException(GetResponseForExceptionEvent $event) + { + if (!$this->debug || !($e = $event->getException()) instanceof NotFoundHttpException) { + return; + } + + if ($e->getPrevious() instanceof NoConfigurationException) { + $event->setResponse($this->createWelcomeResponse()); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array(array('onKernelRequest', 32)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), + KernelEvents::EXCEPTION => array('onKernelException', -64), + ); + } + + 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/vendor/symfony/http-kernel/EventListener/SaveSessionListener.php b/vendor/symfony/http-kernel/EventListener/SaveSessionListener.php new file mode 100644 index 0000000..36809b5 --- /dev/null +++ b/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/vendor/symfony/http-kernel/EventListener/SessionListener.php b/vendor/symfony/http-kernel/EventListener/SessionListener.php new file mode 100644 index 0000000..97eca1c --- /dev/null +++ b/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 + */ +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/vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php b/vendor/symfony/http-kernel/EventListener/StreamedResponseListener.php new file mode 100644 index 0000000..2c616b9 --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * 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/vendor/symfony/http-kernel/EventListener/SurrogateListener.php b/vendor/symfony/http-kernel/EventListener/SurrogateListener.php new file mode 100644 index 0000000..6343533 --- /dev/null +++ b/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\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * 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/vendor/symfony/http-kernel/EventListener/TestSessionListener.php b/vendor/symfony/http-kernel/EventListener/TestSessionListener.php new file mode 100644 index 0000000..f859d09 --- /dev/null +++ b/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 + */ +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/vendor/symfony/http-kernel/EventListener/TranslatorListener.php b/vendor/symfony/http-kernel/EventListener/TranslatorListener.php new file mode 100644 index 0000000..2a5fc71 --- /dev/null +++ b/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\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; +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/vendor/symfony/http-kernel/EventListener/ValidateRequestListener.php b/vendor/symfony/http-kernel/EventListener/ValidateRequestListener.php new file mode 100644 index 0000000..a33853f --- /dev/null +++ b/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/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php b/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php new file mode 100644 index 0000000..3aedbf9 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(403, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/BadRequestHttpException.php b/vendor/symfony/http-kernel/Exception/BadRequestHttpException.php new file mode 100644 index 0000000..ff215db --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/BadRequestHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(400, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/ConflictHttpException.php b/vendor/symfony/http-kernel/Exception/ConflictHttpException.php new file mode 100644 index 0000000..60195da --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/ConflictHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(409, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/GoneHttpException.php b/vendor/symfony/http-kernel/Exception/GoneHttpException.php new file mode 100644 index 0000000..f2f3515 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/GoneHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(410, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/HttpException.php b/vendor/symfony/http-kernel/Exception/HttpException.php new file mode 100644 index 0000000..dab7312 --- /dev/null +++ b/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(int $statusCode, string $message = null, \Exception $previous = null, array $headers = array(), ?int $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/vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php b/vendor/symfony/http-kernel/Exception/HttpExceptionInterface.php new file mode 100644 index 0000000..8aa50a9 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php b/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.php new file mode 100644 index 0000000..46b76ba --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/LengthRequiredHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(411, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php b/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.php new file mode 100644 index 0000000..b0085c1 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/MethodNotAllowedHttpException.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\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 + * @param array $headers + */ + public function __construct(array $allow, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) + { + $headers['Allow'] = strtoupper(implode(', ', $allow)); + + parent::__construct(405, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php b/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.php new file mode 100644 index 0000000..32c3089 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/NotAcceptableHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(406, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/NotFoundHttpException.php b/vendor/symfony/http-kernel/Exception/NotFoundHttpException.php new file mode 100644 index 0000000..433ff9b --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/NotFoundHttpException.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 + */ +class NotFoundHttpException extends HttpException +{ + /** + * @param string $message The internal exception message + * @param \Exception $previous The previous exception + * @param int $code The internal exception code + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(404, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php b/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.php new file mode 100644 index 0000000..1081788 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/PreconditionFailedHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(412, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php b/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.php new file mode 100644 index 0000000..3078328 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/PreconditionRequiredHttpException.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 + * + * @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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(428, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php b/vendor/symfony/http-kernel/Exception/ServiceUnavailableHttpException.php new file mode 100644 index 0000000..6677647 --- /dev/null +++ b/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 + * @param array $headers + */ + public function __construct($retryAfter = null, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) + { + if ($retryAfter) { + $headers['Retry-After'] = $retryAfter; + } + + parent::__construct(503, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php b/vendor/symfony/http-kernel/Exception/TooManyRequestsHttpException.php new file mode 100644 index 0000000..60b024c --- /dev/null +++ b/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 + * @param array $headers + */ + public function __construct($retryAfter = null, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) + { + if ($retryAfter) { + $headers['Retry-After'] = $retryAfter; + } + + parent::__construct(429, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php b/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.php new file mode 100644 index 0000000..17ebb25 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/UnauthorizedHttpException.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\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 + * @param array $headers + */ + public function __construct(string $challenge, string $message = null, \Exception $previous = null, ?int $code = 0, array $headers = array()) + { + $headers['WWW-Authenticate'] = $challenge; + + parent::__construct(401, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php b/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.php new file mode 100644 index 0000000..3a4b40c --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/UnprocessableEntityHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(422, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php b/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.php new file mode 100644 index 0000000..ed68611 --- /dev/null +++ b/vendor/symfony/http-kernel/Exception/UnsupportedMediaTypeHttpException.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 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 + * @param array $headers + */ + public function __construct(string $message = null, \Exception $previous = null, int $code = 0, array $headers = array()) + { + parent::__construct(415, $message, $previous, $headers, $code); + } +} diff --git a/vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/AbstractSurrogateFragmentRenderer.php new file mode 100644 index 0000000..6ab6ec1 --- /dev/null +++ b/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)) { + throw new \InvalidArgumentException('Passing non-scalar values as part of URI attributes to the ESI and SSI rendering strategies is not supported. Use a different rendering strategy or pass scalar values.'); + } + + 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): string + { + 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): bool + { + 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/vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/EsiFragmentRenderer.php new file mode 100644 index 0000000..a4570e3 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Fragment/FragmentHandler.php b/vendor/symfony/http-kernel/Fragment/FragmentHandler.php new file mode 100644 index 0000000..0c31826 --- /dev/null +++ b/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\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +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(), bool $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/vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php b/vendor/symfony/http-kernel/Fragment/FragmentRendererInterface.php new file mode 100644 index 0000000..bcf4e99 --- /dev/null +++ b/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\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; + +/** + * 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/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.php new file mode 100644 index 0000000..3ee0cfa --- /dev/null +++ b/vendor/symfony/http-kernel/Fragment/HIncludeFragmentRenderer.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\HttpKernel\Fragment; + +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\Templating\EngineInterface; +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, string $globalDefaultTemplate = null, string $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)); + } + + private function templateExists(string $template): bool + { + if ($this->templating instanceof EngineInterface) { + try { + return $this->templating->exists($template); + } catch (\Exception $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/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php new file mode 100644 index 0000000..7122485 --- /dev/null +++ b/vendor/symfony/http-kernel/Fragment/InlineFragmentRenderer.php @@ -0,0 +1,135 @@ + + * + * 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\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\HttpCache\SubRequestHandler; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * 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 SubRequestHandler::handle($this->kernel, $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(); + + 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/vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/RoutableFragmentRenderer.php new file mode 100644 index 0000000..0c1b95d --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +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/vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php b/vendor/symfony/http-kernel/Fragment/SsiFragmentRenderer.php new file mode 100644 index 0000000..45e7122 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php b/vendor/symfony/http-kernel/HttpCache/AbstractSurrogate.php new file mode 100644 index 0000000..3b2d5f2 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpCache/Esi.php b/vendor/symfony/http-kernel/HttpCache/Esi.php new file mode 100644 index 0000000..69134c7 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpCache/HttpCache.php b/vendor/symfony/http-kernel/HttpCache/HttpCache.php new file mode 100644 index 0000000..fa3be46 --- /dev/null +++ b/vendor/symfony/http-kernel/HttpCache/HttpCache.php @@ -0,0 +1,684 @@ + + * + * 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\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\TerminableInterface; + +/** + * 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(); + // Keep a clone of the original request for surrogates so they can access it. + // We must clone here to get a separate instance because the application will modify the request during + // the application flow (we know it always does because we do ourselves by setting REMOTE_ADDR to 127.0.0.1 + // and adding the X-Forwarded-For header, see HttpCache::forward()). + $this->request = clone $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); + } + + // always a "master" request (as the real master request can be in cache) + $response = SubRequestHandler::handle($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $catch); + + // 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. + */ + private function record(Request $request, string $event) + { + $this->traces[$this->getTraceKey($request)][] = $event; + } + + /** + * Calculates the key we use in the "trace" array for a given request. + */ + private function getTraceKey(Request $request): string + { + $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. + */ + private function mayServeStaleWhileRevalidate(Response $entry): bool + { + $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. + */ + private function waitForLock(Request $request): bool + { + $wait = 0; + while ($this->store->isLocked($request) && $wait < 100) { + usleep(50000); + ++$wait; + } + + return $wait < 100; + } +} diff --git a/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php b/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategy.php new file mode 100644 index 0000000..672cc89 --- /dev/null +++ b/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() || !$response->isCacheable()) { + $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/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php b/vendor/symfony/http-kernel/HttpCache/ResponseCacheStrategyInterface.php new file mode 100644 index 0000000..e282299 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpCache/Ssi.php b/vendor/symfony/http-kernel/HttpCache/Ssi.php new file mode 100644 index 0000000..58df176 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpCache/Store.php b/vendor/symfony/http-kernel/HttpCache/Store.php new file mode 100644 index 0000000..925ca96 --- /dev/null +++ b/vendor/symfony/http-kernel/HttpCache/Store.php @@ -0,0 +1,489 @@ + + * + * 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; + + /** + * @throws \RuntimeException + */ + public function __construct(string $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')) { + @unlink($tmpFile); + + return false; + } + @fwrite($fp, $data); + @fclose($fp); + + if ($data != file_get_contents($tmpFile)) { + @unlink($tmpFile); + + return false; + } + + if (false === @rename($tmpFile, $path)) { + @unlink($tmpFile); + + 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/vendor/symfony/http-kernel/HttpCache/StoreInterface.php b/vendor/symfony/http-kernel/HttpCache/StoreInterface.php new file mode 100644 index 0000000..8f1cf44 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpCache/SubRequestHandler.php b/vendor/symfony/http-kernel/HttpCache/SubRequestHandler.php new file mode 100644 index 0000000..8c57356 --- /dev/null +++ b/vendor/symfony/http-kernel/HttpCache/SubRequestHandler.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 Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\IpUtils; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class SubRequestHandler +{ + public static function handle(HttpKernelInterface $kernel, Request $request, $type, $catch): Response + { + // save global state related to trusted headers and proxies + $trustedProxies = Request::getTrustedProxies(); + $trustedHeaderSet = Request::getTrustedHeaderSet(); + + // remove untrusted values + $remoteAddr = $request->server->get('REMOTE_ADDR'); + if (!IpUtils::checkIp($remoteAddr, $trustedProxies)) { + $trustedHeaders = array( + 'FORWARDED' => $trustedHeaderSet & Request::HEADER_FORWARDED, + 'X_FORWARDED_FOR' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_FOR, + 'X_FORWARDED_HOST' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_HOST, + 'X_FORWARDED_PROTO' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PROTO, + 'X_FORWARDED_PORT' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PORT, + ); + foreach (array_filter($trustedHeaders) as $name => $key) { + $request->headers->remove($name); + } + } + + // compute trusted values, taking any trusted proxies into account + $trustedIps = array(); + $trustedValues = array(); + foreach (array_reverse($request->getClientIps()) as $ip) { + $trustedIps[] = $ip; + $trustedValues[] = sprintf('for="%s"', $ip); + } + if ($ip !== $remoteAddr) { + $trustedIps[] = $remoteAddr; + $trustedValues[] = sprintf('for="%s"', $remoteAddr); + } + + // set trusted values, reusing as much as possible the global trusted settings + if (Request::HEADER_FORWARDED & $trustedHeaderSet) { + $trustedValues[0] .= sprintf(';host="%s";proto=%s', $request->getHttpHost(), $request->getScheme()); + $request->headers->set('Forwarded', implode(', ', $trustedValues)); + } + if (Request::HEADER_X_FORWARDED_FOR & $trustedHeaderSet) { + $request->headers->set('X-Forwarded-For', implode(', ', $trustedIps)); + } elseif (!(Request::HEADER_FORWARDED & $trustedHeaderSet)) { + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet | Request::HEADER_X_FORWARDED_FOR); + $request->headers->set('X-Forwarded-For', implode(', ', $trustedIps)); + } + + // fix the client IP address by setting it to 127.0.0.1, + // which is the core responsibility of this method + $request->server->set('REMOTE_ADDR', '127.0.0.1'); + + // ensure 127.0.0.1 is set as trusted proxy + if (!IpUtils::checkIp('127.0.0.1', $trustedProxies)) { + Request::setTrustedProxies(array_merge($trustedProxies, array('127.0.0.1')), Request::getTrustedHeaderSet()); + } + + try { + return $kernel->handle($request, $type, $catch); + } finally { + // restore global state + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + } + } +} diff --git a/vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php b/vendor/symfony/http-kernel/HttpCache/SurrogateInterface.php new file mode 100644 index 0000000..85391f8 --- /dev/null +++ b/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/vendor/symfony/http-kernel/HttpKernel.php b/vendor/symfony/http-kernel/HttpKernel.php new file mode 100644 index 0000000..06d2100 --- /dev/null +++ b/vendor/symfony/http-kernel/HttpKernel.php @@ -0,0 +1,284 @@ + + * + * 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\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface; +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\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent; +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\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; + +/** + * 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) { + $this->argumentResolver = new ArgumentResolver(); + } + } + + /** + * {@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, int $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(); + } else { + $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, int $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. + */ + private function finishRequest(Request $request, int $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 (one of HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST) + * + * @throws \Exception + */ + private function handleException(\Exception $e, Request $request, int $type): Response + { + $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 (!$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): string + { + 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/vendor/symfony/http-kernel/HttpKernelInterface.php b/vendor/symfony/http-kernel/HttpKernelInterface.php new file mode 100644 index 0000000..5050bfc --- /dev/null +++ b/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/vendor/symfony/http-kernel/Kernel.php b/vendor/symfony/http-kernel/Kernel.php new file mode 100644 index 0000000..7e75a9e --- /dev/null +++ b/vendor/symfony/http-kernel/Kernel.php @@ -0,0 +1,805 @@ + + * + * 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\Config\ConfigCache; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +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\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; + +/** + * 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 $container; + protected $rootDir; + protected $environment; + protected $debug; + protected $booted = false; + protected $name; + protected $startTime; + + private $projectDir; + private $warmupDir; + private $requestStackSize = 0; + private $resetServices = false; + + const VERSION = '4.0.14'; + const VERSION_ID = 40014; + const MAJOR_VERSION = 4; + const MINOR_VERSION = 0; + const RELEASE_VERSION = 14; + const EXTRA_VERSION = ''; + + const END_OF_MAINTENANCE = '07/2018'; + const END_OF_LIFE = '01/2019'; + + public function __construct(string $environment, bool $debug) + { + $this->environment = $environment; + $this->debug = $debug; + $this->rootDir = $this->getRootDir(); + $this->name = $this->getName(); + } + + public function __clone() + { + $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; + if ($this->debug) { + $this->startTime = microtime(true); + } + } + + return; + } + if ($this->debug) { + $this->startTime = microtime(true); + } + if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) { + putenv('SHELL_VERBOSITY=3'); + $_ENV['SHELL_VERBOSITY'] = 3; + $_SERVER['SHELL_VERBOSITY'] = 3; + } + + // 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) + { + if (!isset($this->bundles[$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))); + } + + return $this->bundles[$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; + $bundle = $this->getBundle($bundleName); + $files = array(); + + 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.'/'.$bundle->getName().$overridePath + )); + } + + $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; + } + + /** + * @internal + */ + public function setAnnotatedClassCache(array $annotatedClasses) + { + file_put_contents(($this->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'; + } + + /** + * Initializes bundles. + * + * @throws \LogicException if two bundles share a common name + */ + protected function initializeBundles() + { + // init bundles + $this->bundles = 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; + } + } + + /** + * 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 (file_exists($cache->getPath()) && \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 && file_exists($cache->getPath())) { + $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. + static $legacyContainers = array(); + $oldContainerDir = \dirname($oldContainer->getFileName()); + $legacyContainers[$oldContainerDir.'.legacy'] = true; + foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy') as $legacyContainer) { + if (!isset($legacyContainers[$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( + 'path' => $bundle->getPath(), + 'namespace' => $bundle->getNamespace(), + ); + } + + return 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(), + ); + } + + /** + * 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)); + + 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); + } + + if ($this->debug) { + $container->addObjectResource($bundle); + } + } + + foreach ($this->bundles as $bundle) { + $bundle->build($container); + } + + $this->build($container); + + foreach ($container->getExtensions() as $extension) { + $extensions[] = $extension->getAlias(); + } + + // 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()); + } + + $content = $dumper->dump(array( + 'class' => $class, + 'base_class' => $baseClass, + 'file' => $cache->getPath(), + 'as_files' => true, + 'debug' => $this->debug, + 'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(), + )); + + $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($container, $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; + + // 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) + { + list($environment, $debug) = unserialize($data, array('allowed_classes' => false)); + + $this->__construct($environment, $debug); + } +} diff --git a/vendor/symfony/http-kernel/KernelEvents.php b/vendor/symfony/http-kernel/KernelEvents.php new file mode 100644 index 0000000..6743763 --- /dev/null +++ b/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/vendor/symfony/http-kernel/KernelInterface.php b/vendor/symfony/http-kernel/KernelInterface.php new file mode 100644 index 0000000..b990407 --- /dev/null +++ b/vendor/symfony/http-kernel/KernelInterface.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\HttpKernel; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * The Kernel is the heart of the Symfony system. + * + * It manages an environment made of application kernel and bundles. + * + * @author Fabien Potencier + */ +interface KernelInterface extends HttpKernelInterface, \Serializable +{ + /** + * Returns an array of bundles to register. + * + * @return iterable|BundleInterface[] An iterable 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. + * + * @param string $name Bundle name + * + * @return BundleInterface A BundleInterface instance + * + * @throws \InvalidArgumentException when the bundle is not enabled + */ + public function getBundle($name); + + /** + * Returns the file path for a given bundle 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/vendor/symfony/http-kernel/LICENSE b/vendor/symfony/http-kernel/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php b/vendor/symfony/http-kernel/Log/DebugLoggerInterface.php new file mode 100644 index 0000000..1d955c4 --- /dev/null +++ b/vendor/symfony/http-kernel/Log/DebugLoggerInterface.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\Log; + +/** + * DebugLoggerInterface. + * + * @author Fabien Potencier + */ +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(); + + /** + * Removes all log records. + */ + public function clear(); +} diff --git a/vendor/symfony/http-kernel/Log/Logger.php b/vendor/symfony/http-kernel/Log/Logger.php new file mode 100644 index 0000000..8b671d8 --- /dev/null +++ b/vendor/symfony/http-kernel/Log/Logger.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\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(string $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)); + } + + private function format(string $level, string $message, array $context): string + { + 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/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php b/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php new file mode 100644 index 0000000..b95c6ee --- /dev/null +++ b/vendor/symfony/http-kernel/Profiler/FileProfilerStorage.php @@ -0,0 +1,290 @@ + + * + * 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" + * + * @throws \RuntimeException + */ + public function __construct(string $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/vendor/symfony/http-kernel/Profiler/Profile.php b/vendor/symfony/http-kernel/Profiler/Profile.php new file mode 100644 index 0000000..cc7efc8 --- /dev/null +++ b/vendor/symfony/http-kernel/Profiler/Profile.php @@ -0,0 +1,284 @@ + + * + * 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(); + + public function __construct(string $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(self $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(self $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/vendor/symfony/http-kernel/Profiler/Profiler.php b/vendor/symfony/http-kernel/Profiler/Profiler.php new file mode 100644 index 0000000..caade13 --- /dev/null +++ b/vendor/symfony/http-kernel/Profiler/Profiler.php @@ -0,0 +1,254 @@ + + * + * 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 Psr\Log\LoggerInterface; +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; + +/** + * Profiler. + * + * @author Fabien Potencier + */ +class Profiler +{ + private $storage; + + /** + * @var DataCollectorInterface[] + */ + private $collectors = array(); + + private $logger; + private $initiallyEnabled = true; + private $enabled = true; + + public function __construct(ProfilerStorageInterface $storage, LoggerInterface $logger = null, bool $enable = true) + { + $this->storage = $storage; + $this->logger = $logger; + $this->initiallyEnabled = $this->enabled = $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) { + $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) + { + $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/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php b/vendor/symfony/http-kernel/Profiler/ProfilerStorageInterface.php new file mode 100644 index 0000000..544fb1f --- /dev/null +++ b/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/vendor/symfony/http-kernel/README.md b/vendor/symfony/http-kernel/README.md new file mode 100644 index 0000000..cc5e74b --- /dev/null +++ b/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/vendor/symfony/http-kernel/RebootableInterface.php b/vendor/symfony/http-kernel/RebootableInterface.php new file mode 100644 index 0000000..58d9ef5 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Resources/welcome.html.php b/vendor/symfony/http-kernel/Resources/welcome.html.php new file mode 100644 index 0000000..caac7fd --- /dev/null +++ b/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 page because debug mode is enabled and you haven't configured any homepage URL. +

    +
    +
    + + diff --git a/vendor/symfony/http-kernel/TerminableInterface.php b/vendor/symfony/http-kernel/TerminableInterface.php new file mode 100644 index 0000000..8aa3319 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php b/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.php new file mode 100644 index 0000000..394e7ed --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Bundle/BundleTest.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\Bundle; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpKernel\Bundle\Bundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionNotValidBundle\ExtensionNotValidBundle; +use Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\ExtensionPresentBundle; + +class BundleTest extends TestCase +{ + public function testGetContainerExtension() + { + $bundle = new ExtensionPresentBundle(); + + $this->assertInstanceOf( + 'Symfony\Component\HttpKernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection\ExtensionPresentExtension', + $bundle->getContainerExtension() + ); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface + */ + public function testGetContainerExtensionWithInvalidClass() + { + $bundle = new ExtensionNotValidBundle(); + $bundle->getContainerExtension(); + } + + 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/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php b/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.php new file mode 100644 index 0000000..06400b4 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/CacheClearer/ChainCacheClearerTest.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\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); + } + + protected function getMockClearer() + { + return $this->getMockBuilder('Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface')->getMock(); + } +} diff --git a/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.php b/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.php new file mode 100644 index 0000000..e1d2fe8 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/CacheClearer/Psr6CacheClearerTest.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\CacheClearer; + +use PHPUnit\Framework\TestCase; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\HttpKernel\CacheClearer\Psr6CacheClearer; + +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'); + } +} diff --git a/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php b/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php new file mode 100644 index 0000000..cfa998c --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.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\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); + } + + 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/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php b/vendor/symfony/http-kernel/Tests/CacheWarmer/CacheWarmerTest.php new file mode 100644 index 0000000..4d34e7b --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/ClientTest.php b/vendor/symfony/http-kernel/Tests/ClientTest.php new file mode 100644 index 0000000..5af2730 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/ClientTest.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\HttpKernel\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpKernel\Client; +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); + + $response = new Response(); + $response->headers->setCookie($cookie1 = 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->assertSame((string) $cookie1, $domResponse->getHeader('Set-Cookie')); + + $response = new Response(); + $response->headers->setCookie($cookie1 = 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($cookie2 = 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->assertSame((string) $cookie1, $domResponse->getHeader('Set-Cookie')); + $this->assertSame(array((string) $cookie1, (string) $cookie2), $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'); + file_put_contents($source, '1'); + $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' => 1, 'error' => UPLOAD_ERR_OK), + new UploadedFile($source, 'original', 'mime/original', 1, 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->assertSame(1, $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/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php b/vendor/symfony/http-kernel/Tests/Config/FileLocatorTest.php new file mode 100644 index 0000000..6265f02 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php b/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php new file mode 100644 index 0000000..7d34172 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.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\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 testExistingControllerWithATrailingBackSlash() + { + $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/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php b/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php new file mode 100644 index 0000000..bb4e066 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Controller/ArgumentResolverTest.php @@ -0,0 +1,338 @@ + + * + * 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\Request; +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; + +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'); + } + + 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)); + } + + /** + * @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); + } + + /** + * @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); + } + + 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)); + } + + 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/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php b/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php new file mode 100644 index 0000000..a9c19fb --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Controller/ContainerControllerResolverTest.php @@ -0,0 +1,297 @@ + + * + * 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\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')); + + $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/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php b/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php new file mode 100644 index 0000000..62e3975 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Controller/ControllerResolverTest.php @@ -0,0 +1,184 @@ + + * + * 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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerResolver; + +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"'), + ); + } + + protected function createControllerResolver(LoggerInterface $logger = null) + { + return new ControllerResolver($logger); + } + + public function __invoke($foo, $bar = null) + { + } + + public function controllerMethod1($foo) + { + } + + protected static function controllerMethod4() + { + } +} + +function some_controller_function($foo, $foobar) +{ +} + +class ControllerTest +{ + public function publicAction() + { + } + + private function privateAction() + { + } + + protected function protectedAction() + { + } + + public static function staticAction() + { + } +} diff --git a/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php new file mode 100644 index 0000000..a667705 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.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\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); + } + + 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); + } + + 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); + } + + 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(self $foo, array $bar, callable $baz) + { + } + + private function signature2(self $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/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php b/vendor/symfony/http-kernel/Tests/ControllerMetadata/ArgumentMetadataTest.php new file mode 100644 index 0000000..0535144 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log b/vendor/symfony/http-kernel/Tests/DataCollector/Compiler.log new file mode 100644 index 0000000..88b6840 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/ConfigDataCollectorTest.php new file mode 100644 index 0000000..a99e34a --- /dev/null +++ b/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\Config\Loader\LoaderInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\ConfigDataCollector; +use Symfony\Component\HttpKernel\Kernel; + +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/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/DataCollectorTest.php new file mode 100644 index 0000000..54fd39e --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php new file mode 100644 index 0000000..fd5ea11 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -0,0 +1,138 @@ + + * + * 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\DataCollector\DumpDataCollector; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * @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 = preg_replace("/\033\[[^m]*m/", '', 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(); + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output); + } + + public function testFlushNothingWhenDataDumperIsProvided() + { + $data = new Data(array(array(456))); + $dumper = new CliDumper('php://output'); + $collector = new DumpDataCollector(null, null, null, null, $dumper); + + ob_start(); + $collector->dump($data); + $line = __LINE__ - 1; + $output = preg_replace("/\033\[[^m]*m/", '', ob_get_clean()); + if (\PHP_VERSION_ID >= 50400) { + $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n456\n", $output); + } else { + $this->assertSame("\"DumpDataCollectorTest.php on line {$line}:\"\n456\n", $output); + } + + ob_start(); + $collector->__destruct(); + $this->assertEmpty(ob_get_clean()); + } +} diff --git a/vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/ExceptionDataCollectorTest.php new file mode 100644 index 0000000..1e8d186 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector; + +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/vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/LoggerDataCollectorTest.php new file mode 100644 index 0000000..3dec3bd --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/MemoryDataCollectorTest.php new file mode 100644 index 0000000..1435d05 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\MemoryDataCollector; + +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/vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/RequestDataCollectorTest.php new file mode 100644 index 0000000..cce08e2 --- /dev/null +++ b/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\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Cookie; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Event\FilterControllerEvent; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +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/vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php b/vendor/symfony/http-kernel/Tests/DataCollector/TimeDataCollectorTest.php new file mode 100644 index 0000000..cf6a866 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\TimeDataCollector; + +/** + * @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/vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php b/vendor/symfony/http-kernel/Tests/Debug/FileLinkFormatterTest.php new file mode 100644 index 0000000..e14780a --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php b/vendor/symfony/http-kernel/Tests/Debug/TraceableEventDispatcherTest.php new file mode 100644 index 0000000..3de147c --- /dev/null +++ b/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\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; +use Symfony\Component\HttpKernel\HttpKernel; +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/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php new file mode 100644 index 0000000..a2fb6af --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/ControllerArgumentValueResolverPassTest.php new file mode 100644 index 0000000..df8977d --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.php new file mode 100644 index 0000000..e8957bb --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/DependencyInjection/FragmentRendererPassTest.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\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +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 + * triggers an exception. + * + * @expectedException \InvalidArgumentException + */ + public function testContentRendererWithoutInterface() + { + $builder = new ContainerBuilder(); + $fragmentHandlerDefinition = $builder->register('fragment.handler'); + $builder->register('my_content_renderer', 'Symfony\Component\DependencyInjection\Definition') + ->addTag('kernel.fragment_renderer', array('alias' => 'foo')); + + $pass = new FragmentRendererPass(); + $pass->process($builder); + + $this->assertEquals(array(array('addRendererService', array('foo', 'my_content_renderer'))), $fragmentHandlerDefinition->getMethodCalls()); + } + + public function testValidContentRenderer() + { + $builder = new ContainerBuilder(); + $fragmentHandlerDefinition = $builder->register('fragment.handler') + ->addArgument(null); + $builder->register('my_content_renderer', 'Symfony\Component\HttpKernel\Tests\DependencyInjection\RendererService') + ->addTag('kernel.fragment_renderer', array('alias' => 'foo')); + + $pass = new FragmentRendererPass(); + $pass->process($builder); + + $serviceLocatorDefinition = $builder->getDefinition((string) $fragmentHandlerDefinition->getArgument(0)); + $this->assertSame(ServiceLocator::class, $serviceLocatorDefinition->getClass()); + $this->assertEquals(array('foo' => new ServiceClosureArgument(new Reference('my_content_renderer'))), $serviceLocatorDefinition->getArgument(0)); + } +} + +class RendererService implements FragmentRendererInterface +{ + public function render($uri, Request $request = null, array $options = array()) + { + } + + public function getName() + { + return 'test'; + } +} diff --git a/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.php new file mode 100644 index 0000000..2bc84a5 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/DependencyInjection/LazyLoadingFragmentHandlerTest.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\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DependencyInjection\LazyLoadingFragmentHandler; + +class LazyLoadingFragmentHandlerTest extends TestCase +{ + 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/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/LoggerPassTest.php new file mode 100644 index 0000000..cb50487 --- /dev/null +++ b/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\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; +use Symfony\Component\HttpKernel\Log\Logger; + +/** + * @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/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php new file mode 100644 index 0000000..ae421d9 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.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\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; + +class MergeExtensionConfigurationPassTest extends TestCase +{ + public function testAutoloadMainExtension() + { + $container = new ContainerBuilder(); + $container->registerExtension(new LoadedExtension()); + $container->registerExtension(new NotLoadedExtension()); + $container->loadFromExtension('loaded', array()); + + $configPass = new MergeExtensionConfigurationPass(array('loaded', 'not_loaded')); + $configPass->process($container); + + $this->assertTrue($container->hasDefinition('loaded.foo')); + $this->assertTrue($container->hasDefinition('not_loaded.bar')); + } +} + +class LoadedExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $container->register('loaded.foo'); + } +} + +class NotLoadedExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $container->register('not_loaded.bar'); + } +} diff --git a/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php new file mode 100644 index 0000000..ca121c7 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -0,0 +1,413 @@ + + * + * 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\ChildDefinition; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\DependencyInjection\TypedReference; +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); + } + + public function testBindingsOnChildDefinitions() + { + $container = new ContainerBuilder(); + $resolver = $container->register('argument_resolver.service')->addArgument(array()); + + $container->register('parent', ArgumentWithoutTypeController::class); + + $container->setDefinition('child', (new ChildDefinition('parent')) + ->setBindings(array('$someArg' => new Reference('parent'))) + ->addTag('controller.service_arguments') + ); + + $pass = new RegisterControllerArgumentLocatorsPass(); + $pass->process($container); + + $locator = $container->getDefinition((string) $resolver->getArgument(0))->getArgument(0); + $this->assertInstanceOf(ServiceClosureArgument::class, $locator['child:fooAction']); + + $locator = $container->getDefinition((string) $locator['child:fooAction']->getValues()[0])->getArgument(0); + $this->assertInstanceOf(ServiceClosureArgument::class, $locator['someArg']); + $this->assertEquals(new Reference('parent'), $locator['someArg']->getValues()[0]); + } +} + +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/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/RemoveEmptyControllerArgumentLocatorsPassTest.php new file mode 100644 index 0000000..9cac968 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/ResettableServicePassTest.php new file mode 100644 index 0000000..f7ea16d --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php b/vendor/symfony/http-kernel/Tests/DependencyInjection/ServicesResetterTest.php new file mode 100644 index 0000000..47c62ab --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php b/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php new file mode 100644 index 0000000..f1e440b --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Event/FilterControllerArgumentsEventTest.php @@ -0,0 +1,17 @@ +assertEquals($filterController->getArguments(), array('test')); + } +} diff --git a/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php b/vendor/symfony/http-kernel/Tests/Event/GetResponseForExceptionEventTest.php new file mode 100644 index 0000000..7242579 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/AddRequestFormatsListenerTest.php new file mode 100644 index 0000000..3ffb9f3 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\AddRequestFormatsListener; +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/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/DebugHandlersListenerTest.php new file mode 100644 index 0000000..9a9c17e --- /dev/null +++ b/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\Command\Command; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleEvent; +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/vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/DumpListenerTest.php new file mode 100644 index 0000000..509f443 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/ExceptionListenerTest.php new file mode 100644 index 0000000..0b73075 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; +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/vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/FragmentListenerTest.php new file mode 100644 index 0000000..edf0498 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\FragmentListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +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/vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/LocaleListenerTest.php new file mode 100644 index 0000000..f442235 --- /dev/null +++ b/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\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\LocaleListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +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/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/ProfilerListenerTest.php new file mode 100644 index 0000000..526b3aa --- /dev/null +++ b/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\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; +use Symfony\Component\HttpKernel\EventListener\ProfilerListener; +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/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/ResponseListenerTest.php new file mode 100644 index 0000000..1d89602 --- /dev/null +++ b/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\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\EventListener\ResponseListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +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/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php new file mode 100644 index 0000000..92e7272 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/EventListener/RouterListenerTest.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\ExceptionListener; +use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\HttpKernel\EventListener\ValidateRequestListener; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +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), + ); + } + + private function createGetResponseEventForUri(string $uri): GetResponseEvent + { + $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/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/SaveSessionListenerTest.php new file mode 100644 index 0000000..5492c3d --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.php new file mode 100644 index 0000000..1b95d19 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/EventListener/SessionListenerTest.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\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\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +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->exactly(2))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1)); + + $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')); + } + + public function testSurrogateMasterRequestIsPublic() + { + $session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock(); + $session->expects($this->exactly(4))->method('getUsageIndex')->will($this->onConsecutiveCalls(0, 1, 1, 1)); + + $container = new Container(); + $container->set('session', $session); + + $listener = new SessionListener($container); + $kernel = $this->getMockBuilder(HttpKernelInterface::class)->disableOriginalConstructor()->getMock(); + + $request = new Request(); + $response = new Response(); + $response->setCache(array('public' => true, 'max_age' => '30')); + $listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST)); + $this->assertTrue($request->hasSession()); + + $subRequest = clone $request; + $this->assertSame($request->getSession(), $subRequest->getSession()); + $listener->onKernelRequest(new GetResponseEvent($kernel, $subRequest, HttpKernelInterface::MASTER_REQUEST)); + $listener->onKernelResponse(new FilterResponseEvent($kernel, $subRequest, HttpKernelInterface::MASTER_REQUEST, $response)); + $listener->onFinishRequest(new FinishRequestEvent($kernel, $subRequest, HttpKernelInterface::MASTER_REQUEST)); + + $this->assertFalse($response->headers->hasCacheControlDirective('private')); + $this->assertFalse($response->headers->hasCacheControlDirective('must-revalidate')); + $this->assertSame('30', $response->headers->getCacheControlDirective('max-age')); + + $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/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/SurrogateListenerTest.php new file mode 100644 index 0000000..b955c07 --- /dev/null +++ b/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\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\EventListener\SurrogateListener; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +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/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php new file mode 100644 index 0000000..0d0985c --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/EventListener/TestSessionListenerTest.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +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\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\SessionListener; +use Symfony\Component\HttpKernel\EventListener\TestSessionListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * 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(); + $this->listener->expects($this->any()) + ->method('getSession') + ->will($this->returnValue($this->session)); + } + + 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 testEmptySessionWithNewSessionIdDoesSendCookie() + { + $this->sessionHasBeenStarted(); + $this->sessionIsEmpty(); + $this->fixSessionId('456'); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('/', 'GET', array(), array('MOCKSESSID' => '123')); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST); + + $this->assertNotEmpty($response->headers->getCookies()); + } + + /** + * @dataProvider anotherCookieProvider + */ + public function testSessionWithNewSessionIdAndNewCookieDoesNotSendAnotherCookie($existing, array $expected) + { + $this->sessionHasBeenStarted(); + $this->sessionIsEmpty(); + $this->fixSessionId('456'); + + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $request = Request::create('/', 'GET', array(), array('MOCKSESSID' => '123')); + $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST); + $this->listener->onKernelRequest($event); + + $response = new Response('', 200, array('Set-Cookie' => $existing)); + + $response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST, $response); + + $this->assertSame($expected, $response->headers->get('Set-Cookie', null, false)); + } + + public function anotherCookieProvider() + { + return array( + 'same' => array('MOCKSESSID=789; path=/', array('MOCKSESSID=789; path=/')), + 'different domain' => array('MOCKSESSID=789; path=/; domain=example.com', array('MOCKSESSID=789; path=/; domain=example.com', 'MOCKSESSID=456; path=/')), + 'different path' => array('MOCKSESSID=789; path=/foo', array('MOCKSESSID=789; path=/foo', 'MOCKSESSID=456; path=/')), + ); + } + + 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, Response $response = null) + { + $request->setSession($this->session); + $response = $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 fixSessionId($sessionId) + { + $this->session->expects($this->any()) + ->method('getId') + ->will($this->returnValue($sessionId)); + } + + 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/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/TranslatorListenerTest.php new file mode 100644 index 0000000..23b8331 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.php b/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.php new file mode 100644 index 0000000..bdab742 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/EventListener/ValidateRequestListenerTest.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\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\EventListener\ValidateRequestListener; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +class ValidateRequestListenerTest extends TestCase +{ + protected function tearDown() + { + Request::setTrustedProxies(array(), -1); + } + + /** + * @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/vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php b/vendor/symfony/http-kernel/Tests/Exception/AccessDeniedHttpExceptionTest.php new file mode 100644 index 0000000..2bfcb2b --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php b/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php new file mode 100644 index 0000000..462d3ca --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Exception/LengthRequiredHttpExceptionTest.php @@ -0,0 +1,13 @@ +assertSame(array('Allow' => 'GET, PUT'), $exception->getHeaders()); + } + + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=1200', + ); + + $exception = new MethodNotAllowedHttpException(array('get'), null, null, null, $headers); + + $headers['Allow'] = 'GET'; + + $this->assertSame($headers, $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = new MethodNotAllowedHttpException(array('GET')); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } +} diff --git a/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php b/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php new file mode 100644 index 0000000..4c0db7a --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Exception/NotAcceptableHttpExceptionTest.php @@ -0,0 +1,13 @@ +assertSame(array('Retry-After' => 10), $exception->getHeaders()); + } + + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=1337', + ); + + $exception = new ServiceUnavailableHttpException(1337, null, null, null, $headers); + + $headers['Retry-After'] = 1337; + + $this->assertSame($headers, $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/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php b/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php new file mode 100644 index 0000000..6ec7f3d --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Exception/TooManyRequestsHttpExceptionTest.php @@ -0,0 +1,42 @@ +assertSame(array('Retry-After' => 10), $exception->getHeaders()); + } + + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=69', + ); + + $exception = new TooManyRequestsHttpException(69, null, null, null, $headers); + + $headers['Retry-After'] = 69; + + $this->assertSame($headers, $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/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php b/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php new file mode 100644 index 0000000..1e93d25 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Exception/UnauthorizedHttpExceptionTest.php @@ -0,0 +1,37 @@ +assertSame(array('WWW-Authenticate' => 'Challenge'), $exception->getHeaders()); + } + + public function testWithHeaderConstruct() + { + $headers = array( + 'Cache-Control' => 'public, s-maxage=1200', + ); + + $exception = new UnauthorizedHttpException('Challenge', null, null, null, $headers); + + $headers['WWW-Authenticate'] = 'Challenge'; + + $this->assertSame($headers, $exception->getHeaders()); + } + + /** + * @dataProvider headerDataProvider + */ + public function testHeadersSetter($headers) + { + $exception = new UnauthorizedHttpException('Challenge'); + $exception->setHeaders($headers); + $this->assertSame($headers, $exception->getHeaders()); + } +} diff --git a/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php b/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.php new file mode 100644 index 0000000..05d8d78 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Exception/UnprocessableEntityHttpExceptionTest.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\_123; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\HttpKernel\Kernel; + +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/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt b/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt b/vendor/symfony/http-kernel/Tests/Fixtures/BaseBundle/Resources/hide.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt b/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt b/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/bar.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt b/vendor/symfony/http-kernel/Tests/Fixtures/Bundle1Bundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt b/vendor/symfony/http-kernel/Tests/Fixtures/Bundle2Bundle/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt b/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt b/vendor/symfony/http-kernel/Tests/Fixtures/ChildBundle/Resources/hide.txt new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php b/vendor/symfony/http-kernel/Tests/Fixtures/ClearableService.php new file mode 100644 index 0000000..35acb41 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php b/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingRequest.php new file mode 100644 index 0000000..9b4754b --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php b/vendor/symfony/http-kernel/Tests/Fixtures/Controller/ExtendingSession.php new file mode 100644 index 0000000..9fa54ee --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.php b/vendor/symfony/http-kernel/Tests/Fixtures/Controller/NullableController.php new file mode 100644 index 0000000..9db4df7 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.php b/vendor/symfony/http-kernel/Tests/Fixtures/Controller/VariadicController.php new file mode 100644 index 0000000..c398124 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php b/vendor/symfony/http-kernel/Tests/Fixtures/DataCollector/CloneVarDataCollector.php new file mode 100644 index 0000000..89dec36 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionAbsentBundle/ExtensionAbsentBundle.php new file mode 100644 index 0000000..c8bfd36 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/DependencyInjection/ExtensionLoadedExtension.php new file mode 100644 index 0000000..b43bc66 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionLoadedBundle/ExtensionLoadedBundle.php new file mode 100644 index 0000000..3af81cb --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php new file mode 100644 index 0000000..0fd6431 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php new file mode 100644 index 0000000..34e2920 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/Command/BarCommand.php new file mode 100644 index 0000000..977976b --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php new file mode 100644 index 0000000..1085717 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php b/vendor/symfony/http-kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php new file mode 100644 index 0000000..36a7ad4 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php b/vendor/symfony/http-kernel/Tests/Fixtures/KernelForOverrideName.php new file mode 100644 index 0000000..f7baaa6 --- /dev/null +++ b/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\Config\Loader\LoaderInterface; +use Symfony\Component\HttpKernel\Kernel; + +class KernelForOverrideName extends Kernel +{ + protected $name = 'overridden'; + + public function registerBundles() + { + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} diff --git a/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php b/vendor/symfony/http-kernel/Tests/Fixtures/KernelForTest.php new file mode 100644 index 0000000..9acee4c --- /dev/null +++ b/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\Config\Loader\LoaderInterface; +use Symfony\Component\HttpKernel\Kernel; + +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/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php b/vendor/symfony/http-kernel/Tests/Fixtures/KernelWithoutBundles.php new file mode 100644 index 0000000..cee1b09 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php b/vendor/symfony/http-kernel/Tests/Fixtures/ResettableService.php new file mode 100644 index 0000000..ffb72a3 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php b/vendor/symfony/http-kernel/Tests/Fixtures/TestEventDispatcher.php new file mode 100644 index 0000000..ca2e6a6 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php b/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.php new file mode 100644 index 0000000..7ac1f4f --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Fragment/EsiFragmentRendererTest.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\Tests\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\UriSigner; + +class EsiFragmentRendererTest extends TestCase +{ + public function testRenderFallbackToInlineStrategyIfEsiNotSupported() + { + $strategy = new EsiFragmentRenderer(new Esi(), $this->getInlineStrategy(true)); + $strategy->render('/', Request::create('/')); + } + + public function testRenderFallbackWithScalar() + { + $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/vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php b/vendor/symfony/http-kernel/Tests/Fragment/FragmentHandlerTest.php new file mode 100644 index 0000000..a296aa0 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Fragment\FragmentHandler; + +/** + * @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/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php b/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.php new file mode 100644 index 0000000..10fbccf --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Fragment/HIncludeFragmentRendererTest.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\Fragment; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer; +use Symfony\Component\HttpKernel\UriSigner; + +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()); + } + + public function testRenderWithEngineAndDefaultText() + { + $engine = $this->getMockBuilder('Symfony\\Component\\Templating\\EngineInterface')->getMock(); + $engine->expects($this->once()) + ->method('exists') + ->with('loading...') + ->will($this->throwException(new \RuntimeException())); + + // only default + $strategy = new HIncludeFragmentRenderer($engine); + $this->assertEquals('loading...', $strategy->render('/foo', Request::create('/'), array('default' => 'loading...'))->getContent()); + } +} diff --git a/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php b/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php new file mode 100644 index 0000000..91552a1 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -0,0 +1,245 @@ + + * + * 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\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\KernelEvents; + +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->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest)); + + $this->assertSame('foo', $strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/'))->getContent()); + } + + public function testRenderWithTrustedHeaderDisabled() + { + Request::setTrustedProxies(array(), 0); + + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + $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->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + + $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(), Request::HEADER_FORWARDED); + + $this->testESIHeaderIsKeptInSubrequest(); + + Request::setTrustedProxies(array(), -1); + } + + public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest() + { + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + + $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); + } + + public function testFirstTrustedProxyIsSetAsRemote() + { + Request::setTrustedProxies(array('1.1.1.1'), -1); + + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1'); + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $strategy->render('/', $request); + + Request::setTrustedProxies(array(), -1); + } + + public function testIpAddressOfRangedTrustedProxyIsSetAsRemote() + { + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1'); + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); + + Request::setTrustedProxies(array('1.1.1.1/24'), -1); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); + + $request = Request::create('/'); + $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); + $strategy->render('/', $request); + + Request::setTrustedProxies(array(), -1); + } + + /** + * 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/vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php b/vendor/symfony/http-kernel/Tests/Fragment/RoutableFragmentRendererTest.php new file mode 100644 index 0000000..3a040de --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php b/vendor/symfony/http-kernel/Tests/Fragment/SsiFragmentRendererTest.php new file mode 100644 index 0000000..f725803 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer; +use Symfony\Component\HttpKernel\HttpCache\Ssi; +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/vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/EsiTest.php new file mode 100644 index 0000000..863ad76 --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Esi; + +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(' Keep this'." 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/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php new file mode 100644 index 0000000..a41d866 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTest.php @@ -0,0 +1,1525 @@ + + * + * 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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\Esi; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; +use Symfony\Component\HttpKernel\HttpCache\Store; +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->kernel->assert(function ($backendRequest) { + $this->assertSame('127.0.0.1', $backendRequest->server->get('REMOTE_ADDR')); + }); + } + + /** + * @dataProvider getTrustedProxyData + */ + public function testHttpCacheIsSetAsATrustedProxy(array $existing) + { + Request::setTrustedProxies($existing, Request::HEADER_X_FORWARDED_ALL); + + $this->setNextResponse(); + $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); + $this->assertSame($existing, Request::getTrustedProxies()); + + $existing = array_unique(array_merge($existing, array('127.0.0.1'))); + $this->kernel->assert(function ($backendRequest) use ($existing) { + $this->assertSame($existing, Request::getTrustedProxies()); + $this->assertsame('10.0.0.1', $backendRequest->getClientIp()); + }); + + Request::setTrustedProxies(array(), -1); + } + + public function getTrustedProxyData() + { + return array( + array(array()), + array(array('10.0.0.2')), + array(array('10.0.0.2', '127.0.0.1')), + ); + } + + /** + * @dataProvider getForwardedData + */ + public function testForwarderHeaderForForwardedRequests($forwarded, $expected) + { + $this->setNextResponse(); + $server = array('REMOTE_ADDR' => '10.0.0.1'); + if (null !== $forwarded) { + Request::setTrustedProxies($server, -1); + $server['HTTP_FORWARDED'] = $forwarded; + } + $this->request('GET', '/', $server); + + $this->kernel->assert(function ($backendRequest) use ($expected) { + $this->assertSame($expected, $backendRequest->headers->get('Forwarded')); + }); + + Request::setTrustedProxies(array(), -1); + } + + public function getForwardedData() + { + return array( + array(null, 'for="10.0.0.1";host="localhost";proto=http'), + array('for=10.0.0.2', 'for="10.0.0.2";host="localhost";proto=http, for="10.0.0.1"'), + array('for=10.0.0.2, for=10.0.0.3', 'for="10.0.0.2";host="localhost";proto=http, for="10.0.0.3", for="10.0.0.1"'), + ); + } + + 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()); + } + + public function testUsesOriginalRequestForSurrogate() + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $store = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpCache\StoreInterface')->getMock(); + + $kernel + ->expects($this->exactly(2)) + ->method('handle') + ->willReturnCallback(function (Request $request) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + + return new Response(); + }); + + $cache = new HttpCache($kernel, + $store, + new Esi() + ); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + + // Main request + $cache->handle($request, HttpKernelInterface::MASTER_REQUEST); + + // Main request was now modified by HttpCache + // The surrogate will ask for the request using $this->cache->getRequest() + // which MUST return the original request so the surrogate + // can actually behave like a reverse proxy like e.g. Varnish would. + $this->assertSame('10.0.0.1', $cache->getRequest()->getClientIp()); + $this->assertSame('10.0.0.1', $cache->getRequest()->server->get('REMOTE_ADDR')); + + // Surrogate request + $cache->handle($request, HttpKernelInterface::SUB_REQUEST); + } +} + +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/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php b/vendor/symfony/http-kernel/Tests/HttpCache/HttpCacheTestCase.php new file mode 100644 index 0000000..b3aa2be --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php new file mode 100644 index 0000000..6d67a17 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/HttpCache/ResponseCacheStrategyTest.php @@ -0,0 +1,240 @@ + + * + * 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')); + $this->assertFalse($masterResponse->headers->hasCacheControlDirective('public')); + } + + public function testEmbeddingPublicResponseDoesNotMakeMainResponsePublic() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setPrivate(); // this is the default, but let's be explicit + $masterResponse->setMaxAge(100); + + $embeddedResponse = new Response(); + $embeddedResponse->setPublic(); + $embeddedResponse->setSharedMaxAge(100); + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('private')); + $this->assertFalse($masterResponse->headers->hasCacheControlDirective('public')); + } + + 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/vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/SsiTest.php new file mode 100644 index 0000000..26ef6cb --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/StoreTest.php new file mode 100644 index 0000000..cef0191 --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/HttpCache/SubRequestHandlerTest.php b/vendor/symfony/http-kernel/Tests/HttpCache/SubRequestHandlerTest.php new file mode 100644 index 0000000..dfe0a73 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/HttpCache/SubRequestHandlerTest.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\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\SubRequestHandler; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class SubRequestHandlerTest extends TestCase +{ + private static $globalState; + + protected function setUp() + { + self::$globalState = $this->getGlobalState(); + } + + protected function tearDown() + { + Request::setTrustedProxies(self::$globalState[0], self::$globalState[1]); + } + + public function testTrustedHeadersAreKept() + { + Request::setTrustedProxies(array('10.0.0.1'), -1); + $globalState = $this->getGlobalState(); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('X-Forwarded-For', '10.0.0.2'); + $request->headers->set('X-Forwarded-Host', 'Good'); + $request->headers->set('X-Forwarded-Port', '1234'); + $request->headers->set('X-Forwarded-Proto', 'https'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.2', $request->getClientIp()); + $this->assertSame('Good', $request->headers->get('X-Forwarded-Host')); + $this->assertSame('1234', $request->headers->get('X-Forwarded-Port')); + $this->assertSame('https', $request->headers->get('X-Forwarded-Proto')); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame($globalState, $this->getGlobalState()); + } + + public function testUntrustedHeadersAreRemoved() + { + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('X-Forwarded-For', '10.0.0.2'); + $request->headers->set('X-Forwarded-Host', 'Evil'); + $request->headers->set('X-Forwarded-Port', '1234'); + $request->headers->set('X-Forwarded-Proto', 'http'); + $request->headers->set('Forwarded', 'Evil2'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.1', $request->getClientIp()); + $this->assertFalse($request->headers->has('X-Forwarded-Host')); + $this->assertFalse($request->headers->has('X-Forwarded-Port')); + $this->assertFalse($request->headers->has('X-Forwarded-Proto')); + $this->assertSame('for="10.0.0.1";host="localhost";proto=http', $request->headers->get('Forwarded')); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame(self::$globalState, $this->getGlobalState()); + } + + public function testTrustedForwardedHeader() + { + Request::setTrustedProxies(array('10.0.0.1'), -1); + $globalState = $this->getGlobalState(); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('Forwarded', 'for="10.0.0.2";host="foo.bar:1234";proto=https'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.2', $request->getClientIp()); + $this->assertSame('foo.bar:1234', $request->getHttpHost()); + $this->assertSame('https', $request->getScheme()); + $this->assertSame(1234, $request->getPort()); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame($globalState, $this->getGlobalState()); + } + + public function testTrustedXForwardedForHeader() + { + Request::setTrustedProxies(array('10.0.0.1'), -1); + $globalState = $this->getGlobalState(); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('X-Forwarded-For', '10.0.0.2'); + $request->headers->set('X-Forwarded-Host', 'foo.bar'); + $request->headers->set('X-Forwarded-Proto', 'https'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.2', $request->getClientIp()); + $this->assertSame('foo.bar', $request->getHttpHost()); + $this->assertSame('https', $request->getScheme()); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame($globalState, $this->getGlobalState()); + } + + private function getGlobalState() + { + return array( + Request::getTrustedProxies(), + Request::getTrustedHeaderSet(), + ); + } +} + +class TestSubRequestHandlerKernel implements HttpKernelInterface +{ + private $assertCallback; + + public function __construct(\Closure $assertCallback) + { + $this->assertCallback = $assertCallback; + } + + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + $assertCallback = $this->assertCallback; + $assertCallback($request, $type, $catch); + + return new Response(); + } +} diff --git a/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php b/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.php new file mode 100644 index 0000000..5dbf02c --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/HttpCache/TestHttpKernel.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\HttpCache; + +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +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 assert(\Closure $callback) + { + $trustedConfig = array(Request::getTrustedProxies(), Request::getTrustedHeaderSet()); + + list($trustedProxies, $trustedHeaderSet, $backendRequest) = $this->backendRequest; + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + + try { + $callback($backendRequest); + } finally { + list($trustedProxies, $trustedHeaderSet) = $trustedConfig; + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + } + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) + { + $this->catch = $catch; + $this->backendRequest = array(Request::getTrustedProxies(), Request::getTrustedHeaderSet(), $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/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php b/vendor/symfony/http-kernel/Tests/HttpCache/TestMultipleHttpKernel.php new file mode 100644 index 0000000..712132b --- /dev/null +++ b/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\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +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/vendor/symfony/http-kernel/Tests/HttpKernelTest.php b/vendor/symfony/http-kernel/Tests/HttpKernelTest.php new file mode 100644 index 0000000..c5326a3 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/HttpKernelTest.php @@ -0,0 +1,387 @@ + + * + * 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\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Response; +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\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; +use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\KernelEvents; + +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')); + } + + 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); + + Request::setTrustedProxies(array(), -1); + } + + 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/vendor/symfony/http-kernel/Tests/KernelTest.php b/vendor/symfony/http-kernel/Tests/KernelTest.php new file mode 100644 index 0000000..8926f20 --- /dev/null +++ b/vendor/symfony/http-kernel/Tests/KernelTest.php @@ -0,0 +1,763 @@ + + * + * 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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\HttpKernel\DependencyInjection\ResettableServicePass; +use Symfony\Component\HttpKernel\DependencyInjection\ServicesResetter; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForOverrideName; +use Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest; +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()); + } + + public function testClassCacheIsNotLoadedByDefault() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + + $kernel->boot(); + } + + 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($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($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) + ; + + $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt', $kernel->locateResource('@Bundle1Bundle/foo.txt')); + } + + public function testLocateResourceIgnoresDirOnNonResource() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue($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($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') + ); + } + + public function testLocateResourceOnDirectories() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue($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($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') + ); + } + + /** + * @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(); + } + + 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()); + } + + 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->assertInstanceOf($containerClass, $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->assertNotInstanceOf($containerClass, $kernel->getContainer()); + $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); + } + + /** + * @group time-sensitive + */ + public function testKernelStartTimeIsResetWhileBootingAlreadyBootedKernel() + { + $kernel = $this->getKernelForTest(array('initializeBundles'), true); + $kernel->boot(); + $preReBoot = $kernel->getStartTime(); + + sleep(3600); //Intentionally large value to detect if ClockMock ever breaks + $kernel->reboot(null); + + $this->assertGreaterThan($preReBoot, $kernel->getStartTime()); + } + + /** + * 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(), $debug = false) + { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest') + ->setConstructorArgs(array('test', $debug)) + ->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/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php b/vendor/symfony/http-kernel/Tests/Log/LoggerTest.php new file mode 100644 index 0000000..a5d070c --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Logger.php b/vendor/symfony/http-kernel/Tests/Logger.php new file mode 100644 index 0000000..63c70bf --- /dev/null +++ b/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/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php b/vendor/symfony/http-kernel/Tests/Profiler/FileProfilerStorageTest.php new file mode 100644 index 0000000..a1db1d9 --- /dev/null +++ b/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->assertNotFalse($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->assertNotFalse($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->assertNotFalse($this->storage->read('backslash'), '->write() accepts backslash in URL'); + + $profile = new Profile('comma'); + $profile->setUrl('http://foo.bar/,'); + $this->storage->write($profile); + $this->assertNotFalse($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->assertNotFalse($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->assertNotFalse($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/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php b/vendor/symfony/http-kernel/Tests/Profiler/ProfilerTest.php new file mode 100644 index 0000000..cff133c --- /dev/null +++ b/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\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface; +use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector; +use Symfony\Component\HttpKernel\Profiler\FileProfilerStorage; +use Symfony\Component\HttpKernel\Profiler\Profiler; + +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(array('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/vendor/symfony/http-kernel/Tests/TestHttpKernel.php b/vendor/symfony/http-kernel/Tests/TestHttpKernel.php new file mode 100644 index 0000000..fb95aa0 --- /dev/null +++ b/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\EventDispatcher\EventDispatcher; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; +use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\HttpKernel; + +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/vendor/symfony/http-kernel/Tests/UriSignerTest.php b/vendor/symfony/http-kernel/Tests/UriSignerTest.php new file mode 100644 index 0000000..84ec19f --- /dev/null +++ b/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/vendor/symfony/http-kernel/UriSigner.php b/vendor/symfony/http-kernel/UriSigner.php new file mode 100644 index 0000000..de73039 --- /dev/null +++ b/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(string $secret, string $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/vendor/symfony/http-kernel/composer.json b/vendor/symfony/http-kernel/composer.json new file mode 100644 index 0000000..372ec7e --- /dev/null +++ b/vendor/symfony/http-kernel/composer.json @@ -0,0 +1,71 @@ +{ + "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": "^7.1.3", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/http-foundation": "~3.4.12|~4.0.12|^4.1.1", + "symfony/debug": "~3.4|~4.0", + "symfony/polyfill-ctype": "~1.8", + "psr/log": "~1.0" + }, + "require-dev": { + "symfony/browser-kit": "~3.4|~4.0", + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/css-selector": "~3.4|~4.0", + "symfony/dependency-injection": "^3.4.10|^4.0.10", + "symfony/dom-crawler": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/finder": "~3.4|~4.0", + "symfony/process": "~3.4|~4.0", + "symfony/routing": "~3.4|~4.0", + "symfony/stopwatch": "~3.4|~4.0", + "symfony/templating": "~3.4|~4.0", + "symfony/translation": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0", + "psr/cache": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4.10|<4.0.10,>=4", + "symfony/var-dumper": "<3.4", + "twig/twig": "<1.34|<2.4,>=2" + }, + "suggest": { + "symfony/browser-kit": "", + "symfony/config": "", + "symfony/console": "", + "symfony/dependency-injection": "", + "symfony/var-dumper": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\HttpKernel\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + } +} diff --git a/vendor/symfony/http-kernel/phpunit.xml.dist b/vendor/symfony/http-kernel/phpunit.xml.dist new file mode 100644 index 0000000..e0de769 --- /dev/null +++ b/vendor/symfony/http-kernel/phpunit.xml.dist @@ -0,0 +1,40 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + + + + + + Symfony\Component\HttpFoundation + + + + + diff --git a/vendor/symfony/inflector/Inflector.php b/vendor/symfony/inflector/Inflector.php new file mode 100644 index 0000000..b670b22 --- /dev/null +++ b/vendor/symfony/inflector/Inflector.php @@ -0,0 +1,232 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Inflector; + +/** + * Converts words between singular and plural forms. + * + * @author Bernhard Schussek + * + * @internal + */ +final class Inflector +{ + /** + * Map English plural to singular suffixes. + * + * @var array + * + * @see http://english-zone.com/spelling/plurals.html + */ + private static $pluralMap = array( + // First entry: plural suffix, reversed + // Second entry: length of plural suffix + // Third entry: Whether the suffix may succeed a vocal + // Fourth entry: Whether the suffix may succeed a consonant + // Fifth entry: singular suffix, normal + + // bacteria (bacterium), criteria (criterion), phenomena (phenomenon) + array('a', 1, true, true, array('on', 'um')), + + // nebulae (nebula) + array('ea', 2, true, true, 'a'), + + // services (service) + array('secivres', 8, true, true, 'service'), + + // mice (mouse), lice (louse) + array('eci', 3, false, true, 'ouse'), + + // geese (goose) + array('esee', 4, false, true, 'oose'), + + // fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius) + array('i', 1, true, true, 'us'), + + // men (man), women (woman) + array('nem', 3, true, true, 'man'), + + // children (child) + array('nerdlihc', 8, true, true, 'child'), + + // oxen (ox) + array('nexo', 4, false, false, 'ox'), + + // indices (index), appendices (appendix), prices (price) + array('seci', 4, false, true, array('ex', 'ix', 'ice')), + + // selfies (selfie) + array('seifles', 7, true, true, 'selfie'), + + // movies (movie) + array('seivom', 6, true, true, 'movie'), + + // feet (foot) + array('teef', 4, true, true, 'foot'), + + // geese (goose) + array('eseeg', 5, true, true, 'goose'), + + // teeth (tooth) + array('hteet', 5, true, true, 'tooth'), + + // news (news) + array('swen', 4, true, true, 'news'), + + // series (series) + array('seires', 6, true, true, 'series'), + + // babies (baby) + array('sei', 3, false, true, 'y'), + + // accesses (access), addresses (address), kisses (kiss) + array('sess', 4, true, false, 'ss'), + + // analyses (analysis), ellipses (ellipsis), fungi (fungus), + // neuroses (neurosis), theses (thesis), emphases (emphasis), + // oases (oasis), crises (crisis), houses (house), bases (base), + // atlases (atlas) + array('ses', 3, true, true, array('s', 'se', 'sis')), + + // objectives (objective), alternative (alternatives) + array('sevit', 5, true, true, 'tive'), + + // drives (drive) + array('sevird', 6, false, true, 'drive'), + + // lives (life), wives (wife) + array('sevi', 4, false, true, 'ife'), + + // moves (move) + array('sevom', 5, true, true, 'move'), + + // hooves (hoof), dwarves (dwarf), elves (elf), leaves (leaf), caves (cave), staves (staff) + array('sev', 3, true, true, array('f', 've', 'ff')), + + // axes (axis), axes (ax), axes (axe) + array('sexa', 4, false, false, array('ax', 'axe', 'axis')), + + // indexes (index), matrixes (matrix) + array('sex', 3, true, false, 'x'), + + // quizzes (quiz) + array('sezz', 4, true, false, 'z'), + + // bureaus (bureau) + array('suae', 4, false, true, 'eau'), + + // roses (rose), garages (garage), cassettes (cassette), + // waltzes (waltz), heroes (hero), bushes (bush), arches (arch), + // shoes (shoe) + array('se', 2, true, true, array('', 'e')), + + // tags (tag) + array('s', 1, true, true, ''), + + // chateaux (chateau) + array('xuae', 4, false, true, 'eau'), + + // people (person) + array('elpoep', 6, true, true, 'person'), + ); + + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Returns the singular form of a word. + * + * If the method can't determine the form with certainty, an array of the + * possible singulars is returned. + * + * @param string $plural A word in plural form + * + * @return string|array The singular form or an array of possible singular + * forms + * + * @internal + */ + public static function singularize(string $plural) + { + $pluralRev = strrev($plural); + $lowerPluralRev = strtolower($pluralRev); + $pluralLength = \strlen($lowerPluralRev); + + // The outer loop iterates over the entries of the plural table + // The inner loop $j iterates over the characters of the plural suffix + // in the plural table to compare them with the characters of the actual + // given plural suffix + foreach (self::$pluralMap as $map) { + $suffix = $map[0]; + $suffixLength = $map[1]; + $j = 0; + + // Compare characters in the plural table and of the suffix of the + // given plural one by one + while ($suffix[$j] === $lowerPluralRev[$j]) { + // Let $j point to the next character + ++$j; + + // Successfully compared the last character + // Add an entry with the singular suffix to the singular array + if ($j === $suffixLength) { + // Is there any character preceding the suffix in the plural string? + if ($j < $pluralLength) { + $nextIsVocal = false !== strpos('aeiou', $lowerPluralRev[$j]); + + if (!$map[2] && $nextIsVocal) { + // suffix may not succeed a vocal but next char is one + break; + } + + if (!$map[3] && !$nextIsVocal) { + // suffix may not succeed a consonant but next char is one + break; + } + } + + $newBase = substr($plural, 0, $pluralLength - $suffixLength); + $newSuffix = $map[4]; + + // Check whether the first character in the plural suffix + // is uppercased. If yes, uppercase the first character in + // the singular suffix too + $firstUpper = ctype_upper($pluralRev[$j - 1]); + + if (\is_array($newSuffix)) { + $singulars = array(); + + foreach ($newSuffix as $newSuffixEntry) { + $singulars[] = $newBase.($firstUpper ? ucfirst($newSuffixEntry) : $newSuffixEntry); + } + + return $singulars; + } + + return $newBase.($firstUpper ? ucfirst($newSuffix) : $newSuffix); + } + + // Suffix is longer than word + if ($j === $pluralLength) { + break; + } + } + } + + // Assume that plural and singular is identical + return $plural; + } +} diff --git a/vendor/symfony/inflector/LICENSE b/vendor/symfony/inflector/LICENSE new file mode 100644 index 0000000..fbcca13 --- /dev/null +++ b/vendor/symfony/inflector/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012-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/vendor/symfony/inflector/README.md b/vendor/symfony/inflector/README.md new file mode 100644 index 0000000..8b81839 --- /dev/null +++ b/vendor/symfony/inflector/README.md @@ -0,0 +1,19 @@ +Inflector Component +=================== + +Inflector converts words between their singular and plural forms (English only). + +Disclaimer +---------- + +This component is currently marked as internal. Do not use it in your own code. +Breaking changes may be introduced in the next minor version of Symfony, or the +component itself might even be removed completely. + +Resources +--------- + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/inflector/Tests/InflectorTest.php b/vendor/symfony/inflector/Tests/InflectorTest.php new file mode 100644 index 0000000..8c40532 --- /dev/null +++ b/vendor/symfony/inflector/Tests/InflectorTest.php @@ -0,0 +1,172 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Inflector\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Inflector\Inflector; + +class InflectorTest extends TestCase +{ + public function singularizeProvider() + { + // see http://english-zone.com/spelling/plurals.html + // see http://www.scribd.com/doc/3271143/List-of-100-Irregular-Plural-Nouns-in-English + return array( + array('accesses', 'access'), + array('addresses', 'address'), + array('agendas', 'agenda'), + array('alumnae', 'alumna'), + array('alumni', 'alumnus'), + array('analyses', array('analys', 'analyse', 'analysis')), + array('antennae', 'antenna'), + array('antennas', 'antenna'), + array('appendices', array('appendex', 'appendix', 'appendice')), + array('arches', array('arch', 'arche')), + array('atlases', array('atlas', 'atlase', 'atlasis')), + array('axes', array('ax', 'axe', 'axis')), + array('babies', 'baby'), + array('bacteria', array('bacterion', 'bacterium')), + array('bases', array('bas', 'base', 'basis')), + array('batches', array('batch', 'batche')), + array('beaux', 'beau'), + array('bees', array('be', 'bee')), + array('boxes', 'box'), + array('boys', 'boy'), + array('bureaus', 'bureau'), + array('bureaux', 'bureau'), + array('buses', array('bus', 'buse', 'busis')), + array('bushes', array('bush', 'bushe')), + array('calves', array('calf', 'calve', 'calff')), + array('cars', 'car'), + array('cassettes', array('cassett', 'cassette')), + array('caves', array('caf', 'cave', 'caff')), + array('chateaux', 'chateau'), + array('cheeses', array('chees', 'cheese', 'cheesis')), + array('children', 'child'), + array('circuses', array('circus', 'circuse', 'circusis')), + array('cliffs', 'cliff'), + array('committee', 'committee'), + array('crises', array('cris', 'crise', 'crisis')), + array('criteria', array('criterion', 'criterium')), + array('cups', 'cup'), + array('data', array('daton', 'datum')), + array('days', 'day'), + array('discos', 'disco'), + array('devices', array('devex', 'devix', 'device')), + array('drives', 'drive'), + array('drivers', 'driver'), + array('dwarves', array('dwarf', 'dwarve', 'dwarff')), + array('echoes', array('echo', 'echoe')), + array('elves', array('elf', 'elve', 'elff')), + array('emphases', array('emphas', 'emphase', 'emphasis')), + array('faxes', 'fax'), + array('feet', 'foot'), + array('feedback', 'feedback'), + array('foci', 'focus'), + array('focuses', array('focus', 'focuse', 'focusis')), + array('formulae', 'formula'), + array('formulas', 'formula'), + array('fungi', 'fungus'), + array('funguses', array('fungus', 'funguse', 'fungusis')), + array('garages', array('garag', 'garage')), + array('geese', 'goose'), + array('halves', array('half', 'halve', 'halff')), + array('hats', 'hat'), + array('heroes', array('hero', 'heroe')), + array('hippopotamuses', array('hippopotamus', 'hippopotamuse', 'hippopotamusis')), //hippopotami + array('hoaxes', 'hoax'), + array('hooves', array('hoof', 'hoove', 'hooff')), + array('houses', array('hous', 'house', 'housis')), + array('indexes', 'index'), + array('indices', array('index', 'indix', 'indice')), + array('ions', 'ion'), + array('irises', array('iris', 'irise', 'irisis')), + array('kisses', 'kiss'), + array('knives', 'knife'), + array('lamps', 'lamp'), + array('leaves', array('leaf', 'leave', 'leaff')), + array('lice', 'louse'), + array('lives', 'life'), + array('matrices', array('matrex', 'matrix', 'matrice')), + array('matrixes', 'matrix'), + array('men', 'man'), + array('mice', 'mouse'), + array('moves', 'move'), + array('movies', 'movie'), + array('nebulae', 'nebula'), + array('neuroses', array('neuros', 'neurose', 'neurosis')), + array('news', 'news'), + array('oases', array('oas', 'oase', 'oasis')), + array('objectives', 'objective'), + array('oxen', 'ox'), + array('parties', 'party'), + array('people', 'person'), + array('persons', 'person'), + array('phenomena', array('phenomenon', 'phenomenum')), + array('photos', 'photo'), + array('pianos', 'piano'), + array('plateaux', 'plateau'), + array('poppies', 'poppy'), + array('prices', array('prex', 'prix', 'price')), + array('quizzes', 'quiz'), + array('radii', 'radius'), + array('roofs', 'roof'), + array('roses', array('ros', 'rose', 'rosis')), + array('sandwiches', array('sandwich', 'sandwiche')), + array('scarves', array('scarf', 'scarve', 'scarff')), + array('schemas', 'schema'), //schemata + array('selfies', 'selfie'), + array('series', 'series'), + array('services', 'service'), + array('sheriffs', 'sheriff'), + array('shoes', array('sho', 'shoe')), + array('spies', 'spy'), + array('staves', array('staf', 'stave', 'staff')), + array('stories', 'story'), + array('strata', array('straton', 'stratum')), + array('suitcases', array('suitcas', 'suitcase', 'suitcasis')), + array('syllabi', 'syllabus'), + array('tags', 'tag'), + array('teeth', 'tooth'), + array('theses', array('thes', 'these', 'thesis')), + array('thieves', array('thief', 'thieve', 'thieff')), + array('trees', array('tre', 'tree')), + array('waltzes', array('waltz', 'waltze')), + array('wives', 'wife'), + + // test casing: if the first letter was uppercase, it should remain so + array('Men', 'Man'), + array('GrandChildren', 'GrandChild'), + array('SubTrees', array('SubTre', 'SubTree')), + + // Known issues + //array('insignia', 'insigne'), + //array('insignias', 'insigne'), + //array('rattles', 'rattle'), + ); + } + + /** + * @dataProvider singularizeProvider + */ + public function testSingularize($plural, $singular) + { + $single = Inflector::singularize($plural); + if (\is_string($singular) && \is_array($single)) { + $this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single)); + } elseif (\is_array($singular) && \is_string($single)) { + $this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single); + } + + $this->assertEquals($singular, $single); + } +} diff --git a/vendor/symfony/inflector/composer.json b/vendor/symfony/inflector/composer.json new file mode 100644 index 0000000..36b9b77 --- /dev/null +++ b/vendor/symfony/inflector/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/inflector", + "type": "library", + "description": "Symfony Inflector Component", + "keywords": [ + "string", + "inflection", + "singularize", + "pluralize", + "words", + "symfony" + ], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Inflector\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/inflector/phpunit.xml.dist b/vendor/symfony/inflector/phpunit.xml.dist new file mode 100644 index 0000000..0e953fc --- /dev/null +++ b/vendor/symfony/inflector/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/monolog-bridge/.gitignore b/vendor/symfony/monolog-bridge/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/monolog-bridge/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/monolog-bridge/CHANGELOG.md b/vendor/symfony/monolog-bridge/CHANGELOG.md new file mode 100644 index 0000000..b56fc6d --- /dev/null +++ b/vendor/symfony/monolog-bridge/CHANGELOG.md @@ -0,0 +1,37 @@ +CHANGELOG +========= + +4.1.0 +----- + + * `WebProcessor` now implements `EventSubscriberInterface` in order to be easily autoconfigured + +4.0.0 +----- + + * the `$format`, `$dateFormat`, `$allowInlineLineBreaks`, and `$ignoreEmptyContextAndExtra` + constructor arguments of the `ConsoleFormatter` class have been removed, use + `$options` instead + * the `DebugHandler` class has been removed + +3.3.0 +----- + + * Improved the console handler output formatting by adding var-dumper support + +3.0.0 +----- + + * deprecated interface `Symfony\Component\HttpKernel\Log\LoggerInterface` has been removed + * deprecated methods `Logger::crit()`, `Logger::emerg()`, `Logger::err()` and `Logger::warn()` have been removed + +2.4.0 +----- + + * added ConsoleHandler and ConsoleFormatter which can be used to show log messages + in the console output depending on the verbosity settings + +2.1.0 +----- + + * added ChromePhpHandler diff --git a/vendor/symfony/monolog-bridge/Formatter/ConsoleFormatter.php b/vendor/symfony/monolog-bridge/Formatter/ConsoleFormatter.php new file mode 100644 index 0000000..feb735e --- /dev/null +++ b/vendor/symfony/monolog-bridge/Formatter/ConsoleFormatter.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\Bridge\Monolog\Formatter; + +use Monolog\Formatter\FormatterInterface; +use Monolog\Logger; +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Cloner\Stub; +use Symfony\Component\VarDumper\Cloner\VarCloner; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * Formats incoming records for console output by coloring them depending on log level. + * + * @author Tobias Schultze + * @author Grégoire Pineau + */ +class ConsoleFormatter implements FormatterInterface +{ + const SIMPLE_FORMAT = "%datetime% %start_tag%%level_name%%end_tag% [%channel%] %message%%context%%extra%\n"; + const SIMPLE_DATE = 'H:i:s'; + + private static $levelColorMap = array( + Logger::DEBUG => 'fg=white', + Logger::INFO => 'fg=green', + Logger::NOTICE => 'fg=blue', + Logger::WARNING => 'fg=cyan', + Logger::ERROR => 'fg=yellow', + Logger::CRITICAL => 'fg=red', + Logger::ALERT => 'fg=red', + Logger::EMERGENCY => 'fg=white;bg=red', + ); + + private $options; + private $cloner; + private $outputBuffer; + private $dumper; + + /** + * Available options: + * * format: The format of the outputted log string. The following placeholders are supported: %datetime%, %start_tag%, %level_name%, %end_tag%, %channel%, %message%, %context%, %extra%; + * * date_format: The format of the outputted date string; + * * colors: If true, the log string contains ANSI code to add color; + * * multiline: If false, "context" and "extra" are dumped on one line. + */ + public function __construct(array $options = array()) + { + $this->options = array_replace(array( + 'format' => self::SIMPLE_FORMAT, + 'date_format' => self::SIMPLE_DATE, + 'colors' => true, + 'multiline' => false, + 'level_name_format' => '%-9s', + 'ignore_empty_context_and_extra' => true, + ), $options); + + if (class_exists(VarCloner::class)) { + $this->cloner = new VarCloner(); + $this->cloner->addCasters(array( + '*' => array($this, 'castObject'), + )); + + $this->outputBuffer = fopen('php://memory', 'r+b'); + if ($this->options['multiline']) { + $output = $this->outputBuffer; + } else { + $output = array($this, 'echoLine'); + } + + $this->dumper = new CliDumper($output, null, CliDumper::DUMP_LIGHT_ARRAY | CliDumper::DUMP_COMMA_SEPARATOR); + } + } + + /** + * {@inheritdoc} + */ + public function formatBatch(array $records) + { + foreach ($records as $key => $record) { + $records[$key] = $this->format($record); + } + + return $records; + } + + /** + * {@inheritdoc} + */ + public function format(array $record) + { + $record = $this->replacePlaceHolder($record); + + $levelColor = self::$levelColorMap[$record['level']]; + + if (!$this->options['ignore_empty_context_and_extra'] || !empty($record['context'])) { + $context = ($this->options['multiline'] ? "\n" : ' ').$this->dumpData($record['context']); + } else { + $context = ''; + } + + if (!$this->options['ignore_empty_context_and_extra'] || !empty($record['extra'])) { + $extra = ($this->options['multiline'] ? "\n" : ' ').$this->dumpData($record['extra']); + } else { + $extra = ''; + } + + $formatted = strtr($this->options['format'], array( + '%datetime%' => $record['datetime']->format($this->options['date_format']), + '%start_tag%' => sprintf('<%s>', $levelColor), + '%level_name%' => sprintf($this->options['level_name_format'], $record['level_name']), + '%end_tag%' => '', + '%channel%' => $record['channel'], + '%message%' => $this->replacePlaceHolder($record)['message'], + '%context%' => $context, + '%extra%' => $extra, + )); + + return $formatted; + } + + /** + * @internal + */ + public function echoLine($line, $depth, $indentPad) + { + if (-1 !== $depth) { + fwrite($this->outputBuffer, $line); + } + } + + /** + * @internal + */ + public function castObject($v, array $a, Stub $s, $isNested) + { + if ($this->options['multiline']) { + return $a; + } + + if ($isNested && !$v instanceof \DateTimeInterface) { + $s->cut = -1; + $a = array(); + } + + return $a; + } + + private function replacePlaceHolder(array $record) + { + $message = $record['message']; + + if (false === strpos($message, '{')) { + return $record; + } + + $context = $record['context']; + + $replacements = array(); + foreach ($context as $k => $v) { + // Remove quotes added by the dumper around string. + $v = trim($this->dumpData($v, false), '"'); + $v = OutputFormatter::escape($v); + $replacements['{'.$k.'}'] = sprintf('%s', $v); + } + + $record['message'] = strtr($message, $replacements); + + return $record; + } + + private function dumpData($data, $colors = null) + { + if (null === $this->dumper) { + return ''; + } + + if (null === $colors) { + $this->dumper->setColors($this->options['colors']); + } else { + $this->dumper->setColors($colors); + } + + if (!$data instanceof Data) { + $data = $this->cloner->cloneVar($data); + } + $data = $data->withRefHandles(false); + $this->dumper->dump($data); + + $dump = stream_get_contents($this->outputBuffer, -1, 0); + rewind($this->outputBuffer); + ftruncate($this->outputBuffer, 0); + + return rtrim($dump); + } +} diff --git a/vendor/symfony/monolog-bridge/Formatter/VarDumperFormatter.php b/vendor/symfony/monolog-bridge/Formatter/VarDumperFormatter.php new file mode 100644 index 0000000..e96b510 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Formatter/VarDumperFormatter.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\Bridge\Monolog\Formatter; + +use Monolog\Formatter\FormatterInterface; +use Symfony\Component\VarDumper\Cloner\VarCloner; + +/** + * @author Grégoire Pineau + */ +class VarDumperFormatter implements FormatterInterface +{ + private $cloner; + + public function __construct(VarCloner $cloner = null) + { + $this->cloner = $cloner ?: new VarCloner(); + } + + public function format(array $record) + { + $record['context'] = $this->cloner->cloneVar($record['context']); + $record['extra'] = $this->cloner->cloneVar($record['extra']); + + return $record; + } + + public function formatBatch(array $records) + { + foreach ($records as $k => $record) { + $record[$k] = $this->format($record); + } + + return $records; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/ChromePhpHandler.php b/vendor/symfony/monolog-bridge/Handler/ChromePhpHandler.php new file mode 100644 index 0000000..65e99f6 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/ChromePhpHandler.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\ChromePHPHandler as BaseChromePhpHandler; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; + +/** + * ChromePhpHandler. + * + * @author Christophe Coevoet + */ +class ChromePhpHandler extends BaseChromePhpHandler +{ + private $headers = array(); + + /** + * @var Response + */ + private $response; + + /** + * Adds the headers to the response once it's created. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + if (!preg_match(static::USER_AGENT_REGEX, $event->getRequest()->headers->get('User-Agent'))) { + $this->sendHeaders = false; + $this->headers = array(); + + return; + } + + $this->response = $event->getResponse(); + foreach ($this->headers as $header => $content) { + $this->response->headers->set($header, $content); + } + $this->headers = array(); + } + + /** + * {@inheritdoc} + */ + protected function sendHeader($header, $content) + { + if (!$this->sendHeaders) { + return; + } + + if ($this->response) { + $this->response->headers->set($header, $content); + } else { + $this->headers[$header] = $content; + } + } + + /** + * Override default behavior since we check it in onKernelResponse. + */ + protected function headersAccepted() + { + return true; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/ConsoleHandler.php b/vendor/symfony/monolog-bridge/Handler/ConsoleHandler.php new file mode 100644 index 0000000..a23dc32 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/ConsoleHandler.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Formatter\LineFormatter; +use Monolog\Handler\AbstractProcessingHandler; +use Monolog\Logger; +use Symfony\Bridge\Monolog\Formatter\ConsoleFormatter; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\VarDumper\Dumper\CliDumper; + +/** + * Writes logs to the console output depending on its verbosity setting. + * + * It is disabled by default and gets activated as soon as a command is executed. + * Instead of listening to the console events, the output can also be set manually. + * + * The minimum logging level at which this handler will be triggered depends on the + * verbosity setting of the console output. The default mapping is: + * - OutputInterface::VERBOSITY_NORMAL will show all WARNING and higher logs + * - OutputInterface::VERBOSITY_VERBOSE (-v) will show all NOTICE and higher logs + * - OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) will show all INFO and higher logs + * - OutputInterface::VERBOSITY_DEBUG (-vvv) will show all DEBUG and higher logs, i.e. all logs + * + * This mapping can be customized with the $verbosityLevelMap constructor parameter. + * + * @author Tobias Schultze + */ +class ConsoleHandler extends AbstractProcessingHandler implements EventSubscriberInterface +{ + private $output; + private $verbosityLevelMap = array( + OutputInterface::VERBOSITY_QUIET => Logger::ERROR, + OutputInterface::VERBOSITY_NORMAL => Logger::WARNING, + OutputInterface::VERBOSITY_VERBOSE => Logger::NOTICE, + OutputInterface::VERBOSITY_VERY_VERBOSE => Logger::INFO, + OutputInterface::VERBOSITY_DEBUG => Logger::DEBUG, + ); + + /** + * @param OutputInterface|null $output The console output to use (the handler remains disabled when passing null + * until the output is set, e.g. by using console events) + * @param bool $bubble Whether the messages that are handled can bubble up the stack + * @param array $verbosityLevelMap Array that maps the OutputInterface verbosity to a minimum logging + * level (leave empty to use the default mapping) + */ + public function __construct(OutputInterface $output = null, bool $bubble = true, array $verbosityLevelMap = array()) + { + parent::__construct(Logger::DEBUG, $bubble); + $this->output = $output; + + if ($verbosityLevelMap) { + $this->verbosityLevelMap = $verbosityLevelMap; + } + } + + /** + * {@inheritdoc} + */ + public function isHandling(array $record) + { + return $this->updateLevel() && parent::isHandling($record); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + // we have to update the logging level each time because the verbosity of the + // console output might have changed in the meantime (it is not immutable) + return $this->updateLevel() && parent::handle($record); + } + + /** + * Sets the console output to use for printing logs. + */ + public function setOutput(OutputInterface $output) + { + $this->output = $output; + } + + /** + * Disables the output. + */ + public function close() + { + $this->output = null; + + parent::close(); + } + + /** + * Before a command is executed, the handler gets activated and the console output + * is set in order to know where to write the logs. + */ + public function onCommand(ConsoleCommandEvent $event) + { + $output = $event->getOutput(); + if ($output instanceof ConsoleOutputInterface) { + $output = $output->getErrorOutput(); + } + + $this->setOutput($output); + } + + /** + * After a command has been executed, it disables the output. + */ + public function onTerminate(ConsoleTerminateEvent $event) + { + $this->close(); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array( + ConsoleEvents::COMMAND => array('onCommand', 255), + ConsoleEvents::TERMINATE => array('onTerminate', -255), + ); + } + + /** + * {@inheritdoc} + */ + protected function write(array $record) + { + // at this point we've determined for sure that we want to output the record, so use the output's own verbosity + $this->output->write((string) $record['formatted'], false, $this->output->getVerbosity()); + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + if (!class_exists(CliDumper::class)) { + return new LineFormatter(); + } + if (!$this->output) { + return new ConsoleFormatter(); + } + + return new ConsoleFormatter(array( + 'colors' => $this->output->isDecorated(), + 'multiline' => OutputInterface::VERBOSITY_DEBUG <= $this->output->getVerbosity(), + )); + } + + /** + * Updates the logging level based on the verbosity setting of the console output. + * + * @return bool Whether the handler is enabled and verbosity is not set to quiet + */ + private function updateLevel() + { + if (null === $this->output) { + return false; + } + + $verbosity = $this->output->getVerbosity(); + if (isset($this->verbosityLevelMap[$verbosity])) { + $this->setLevel($this->verbosityLevelMap[$verbosity]); + } else { + $this->setLevel(Logger::DEBUG); + } + + return true; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/FingersCrossed/HttpCodeActivationStrategy.php b/vendor/symfony/monolog-bridge/Handler/FingersCrossed/HttpCodeActivationStrategy.php new file mode 100644 index 0000000..2a1ae70 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/FingersCrossed/HttpCodeActivationStrategy.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\Bridge\Monolog\Handler\FingersCrossed; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\HttpException; + +/** + * Activation strategy that ignores certain HTTP codes. + * + * @author Shaun Simmons + */ +class HttpCodeActivationStrategy extends ErrorLevelActivationStrategy +{ + private $exclusions; + private $requestStack; + + /** + * @param array $exclusions each exclusion must have a "code" and "urls" keys + */ + public function __construct(RequestStack $requestStack, array $exclusions, $actionLevel) + { + foreach ($exclusions as $exclusion) { + if (!array_key_exists('code', $exclusion)) { + throw new \LogicException(sprintf('An exclusion must have a "code" key')); + } + if (!array_key_exists('urls', $exclusion)) { + throw new \LogicException(sprintf('An exclusion must have a "urls" key')); + } + } + + parent::__construct($actionLevel); + + $this->requestStack = $requestStack; + $this->exclusions = $exclusions; + } + + public function isHandlerActivated(array $record) + { + $isActivated = parent::isHandlerActivated($record); + + if ( + $isActivated + && isset($record['context']['exception']) + && $record['context']['exception'] instanceof HttpException + && ($request = $this->requestStack->getMasterRequest()) + ) { + foreach ($this->exclusions as $exclusion) { + if ($record['context']['exception']->getStatusCode() !== $exclusion['code']) { + continue; + } + + $urlBlacklist = null; + if (\count($exclusion['urls'])) { + return !preg_match('{('.implode('|', $exclusion['urls']).')}i', $request->getPathInfo()); + } + + return false; + } + } + + return $isActivated; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/FingersCrossed/NotFoundActivationStrategy.php b/vendor/symfony/monolog-bridge/Handler/FingersCrossed/NotFoundActivationStrategy.php new file mode 100644 index 0000000..ed41929 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/FingersCrossed/NotFoundActivationStrategy.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\Bridge\Monolog\Handler\FingersCrossed; + +use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\HttpException; + +/** + * Activation strategy that ignores 404s for certain URLs. + * + * @author Jordi Boggiano + * @author Fabien Potencier + */ +class NotFoundActivationStrategy extends ErrorLevelActivationStrategy +{ + private $blacklist; + private $requestStack; + + public function __construct(RequestStack $requestStack, array $excludedUrls, $actionLevel) + { + parent::__construct($actionLevel); + + $this->requestStack = $requestStack; + $this->blacklist = '{('.implode('|', $excludedUrls).')}i'; + } + + public function isHandlerActivated(array $record) + { + $isActivated = parent::isHandlerActivated($record); + + if ( + $isActivated + && isset($record['context']['exception']) + && $record['context']['exception'] instanceof HttpException + && 404 == $record['context']['exception']->getStatusCode() + && ($request = $this->requestStack->getMasterRequest()) + ) { + return !preg_match($this->blacklist, $request->getPathInfo()); + } + + return $isActivated; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/FirePHPHandler.php b/vendor/symfony/monolog-bridge/Handler/FirePHPHandler.php new file mode 100644 index 0000000..9956eda --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/FirePHPHandler.php @@ -0,0 +1,80 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Handler; + +use Monolog\Handler\FirePHPHandler as BaseFirePHPHandler; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Event\FilterResponseEvent; + +/** + * FirePHPHandler. + * + * @author Jordi Boggiano + */ +class FirePHPHandler extends BaseFirePHPHandler +{ + private $headers = array(); + + /** + * @var Response + */ + private $response; + + /** + * Adds the headers to the response once it's created. + */ + public function onKernelResponse(FilterResponseEvent $event) + { + if (!$event->isMasterRequest()) { + return; + } + + $request = $event->getRequest(); + if (!preg_match('{\bFirePHP/\d+\.\d+\b}', $request->headers->get('User-Agent')) + && !$request->headers->has('X-FirePHP-Version')) { + self::$sendHeaders = false; + $this->headers = array(); + + return; + } + + $this->response = $event->getResponse(); + foreach ($this->headers as $header => $content) { + $this->response->headers->set($header, $content); + } + $this->headers = array(); + } + + /** + * {@inheritdoc} + */ + protected function sendHeader($header, $content) + { + if (!self::$sendHeaders) { + return; + } + + if ($this->response) { + $this->response->headers->set($header, $content); + } else { + $this->headers[$header] = $content; + } + } + + /** + * Override default behavior since we check the user agent in onKernelResponse. + */ + protected function headersAccepted() + { + return true; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/ServerLogHandler.php b/vendor/symfony/monolog-bridge/Handler/ServerLogHandler.php new file mode 100644 index 0000000..6c21a33 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/ServerLogHandler.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\Bridge\Monolog\Handler; + +use Monolog\Handler\AbstractHandler; +use Monolog\Logger; +use Symfony\Bridge\Monolog\Formatter\VarDumperFormatter; + +/** + * @author Grégoire Pineau + */ +class ServerLogHandler extends AbstractHandler +{ + private $host; + private $context; + private $socket; + + public function __construct(string $host, int $level = Logger::DEBUG, bool $bubble = true, array $context = array()) + { + parent::__construct($level, $bubble); + + if (false === strpos($host, '://')) { + $host = 'tcp://'.$host; + } + + $this->host = $host; + $this->context = stream_context_create($context); + } + + /** + * {@inheritdoc} + */ + public function handle(array $record) + { + if (!$this->isHandling($record)) { + return false; + } + + set_error_handler(self::class.'::nullErrorHandler'); + + try { + if (!$this->socket = $this->socket ?: $this->createSocket()) { + return false === $this->bubble; + } + } finally { + restore_error_handler(); + } + + $recordFormatted = $this->formatRecord($record); + + set_error_handler(self::class.'::nullErrorHandler'); + + try { + if (-1 === stream_socket_sendto($this->socket, $recordFormatted)) { + stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); + + // Let's retry: the persistent connection might just be stale + if ($this->socket = $this->createSocket()) { + stream_socket_sendto($this->socket, $recordFormatted); + } + } + } finally { + restore_error_handler(); + } + + return false === $this->bubble; + } + + /** + * {@inheritdoc} + */ + protected function getDefaultFormatter() + { + return new VarDumperFormatter(); + } + + private static function nullErrorHandler() + { + } + + private function createSocket() + { + $socket = stream_socket_client($this->host, $errno, $errstr, 0, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT | STREAM_CLIENT_PERSISTENT, $this->context); + + if ($socket) { + stream_set_blocking($socket, false); + } + + return $socket; + } + + private function formatRecord(array $record) + { + if ($this->processors) { + foreach ($this->processors as $processor) { + $record = \call_user_func($processor, $record); + } + } + + $recordFormatted = $this->getFormatter()->format($record); + + foreach (array('log_uuid', 'uuid', 'uid') as $key) { + if (isset($record['extra'][$key])) { + $recordFormatted['log_id'] = $record['extra'][$key]; + break; + } + } + + return base64_encode(serialize($recordFormatted))."\n"; + } +} diff --git a/vendor/symfony/monolog-bridge/Handler/SwiftMailerHandler.php b/vendor/symfony/monolog-bridge/Handler/SwiftMailerHandler.php new file mode 100644 index 0000000..fcbd98a --- /dev/null +++ b/vendor/symfony/monolog-bridge/Handler/SwiftMailerHandler.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\Bridge\Monolog\Handler; + +use Monolog\Handler\SwiftMailerHandler as BaseSwiftMailerHandler; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\HttpKernel\Event\PostResponseEvent; + +/** + * Extended SwiftMailerHandler that flushes mail queue if necessary. + * + * @author Philipp Kräutli + */ +class SwiftMailerHandler extends BaseSwiftMailerHandler +{ + protected $transport; + + protected $instantFlush = false; + + public function setTransport(\Swift_Transport $transport) + { + $this->transport = $transport; + } + + /** + * After the kernel has been terminated we will always flush messages. + */ + public function onKernelTerminate(PostResponseEvent $event) + { + $this->instantFlush = true; + } + + /** + * After the CLI application has been terminated we will always flush messages. + */ + public function onCliTerminate(ConsoleTerminateEvent $event) + { + $this->instantFlush = true; + } + + /** + * {@inheritdoc} + */ + protected function send($content, array $records) + { + parent::send($content, $records); + + if ($this->instantFlush) { + $this->flushMemorySpool(); + } + } + + /** + * Flushes the mail queue if a memory spool is used. + */ + private function flushMemorySpool() + { + $mailerTransport = $this->mailer->getTransport(); + if (!$mailerTransport instanceof \Swift_Transport_SpoolTransport) { + return; + } + + $spool = $mailerTransport->getSpool(); + if (!$spool instanceof \Swift_MemorySpool) { + return; + } + + if (null === $this->transport) { + throw new \Exception('No transport available to flush mail queue'); + } + + $spool->flushQueue($this->transport); + } +} diff --git a/vendor/symfony/monolog-bridge/LICENSE b/vendor/symfony/monolog-bridge/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/monolog-bridge/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/vendor/symfony/monolog-bridge/Logger.php b/vendor/symfony/monolog-bridge/Logger.php new file mode 100644 index 0000000..2f60299 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Logger.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog; + +use Monolog\Logger as BaseLogger; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +/** + * Logger. + * + * @author Fabien Potencier + */ +class Logger extends BaseLogger implements DebugLoggerInterface +{ + /** + * {@inheritdoc} + */ + public function getLogs(/* Request $request = null */) + { + if ($logger = $this->getDebugLogger()) { + return \call_user_func_array(array($logger, 'getLogs'), \func_get_args()); + } + + return array(); + } + + /** + * {@inheritdoc} + */ + public function countErrors(/* Request $request = null */) + { + if ($logger = $this->getDebugLogger()) { + return \call_user_func_array(array($logger, 'countErrors'), \func_get_args()); + } + + return 0; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + if ($logger = $this->getDebugLogger()) { + $logger->clear(); + } + } + + /** + * Returns a DebugLoggerInterface instance if one is registered with this logger. + * + * @return DebugLoggerInterface|null A DebugLoggerInterface instance or null if none is registered + */ + private function getDebugLogger() + { + foreach ($this->processors as $processor) { + if ($processor instanceof DebugLoggerInterface) { + return $processor; + } + } + + foreach ($this->handlers as $handler) { + if ($handler instanceof DebugLoggerInterface) { + return $handler; + } + } + } +} diff --git a/vendor/symfony/monolog-bridge/Processor/DebugProcessor.php b/vendor/symfony/monolog-bridge/Processor/DebugProcessor.php new file mode 100644 index 0000000..a699851 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Processor/DebugProcessor.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\Bridge\Monolog\Processor; + +use Monolog\Logger; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; + +class DebugProcessor implements DebugLoggerInterface +{ + private $records = array(); + private $errorCount = array(); + private $requestStack; + + public function __construct(RequestStack $requestStack = null) + { + $this->requestStack = $requestStack; + } + + public function __invoke(array $record) + { + $hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : ''; + + $this->records[$hash][] = array( + 'timestamp' => $record['datetime']->getTimestamp(), + 'message' => $record['message'], + 'priority' => $record['level'], + 'priorityName' => $record['level_name'], + 'context' => $record['context'], + 'channel' => isset($record['channel']) ? $record['channel'] : '', + ); + + if (!isset($this->errorCount[$hash])) { + $this->errorCount[$hash] = 0; + } + + switch ($record['level']) { + case Logger::ERROR: + case Logger::CRITICAL: + case Logger::ALERT: + case Logger::EMERGENCY: + ++$this->errorCount[$hash]; + } + + return $record; + } + + /** + * {@inheritdoc} + */ + public function getLogs(/* Request $request = null */) + { + if (1 <= \func_num_args() && null !== ($request = \func_get_arg(0)) && isset($this->records[$hash = spl_object_hash($request)])) { + return $this->records[$hash]; + } + + if (0 === \count($this->records)) { + return array(); + } + + return array_merge(...array_values($this->records)); + } + + /** + * {@inheritdoc} + */ + public function countErrors(/* Request $request = null */) + { + if (1 <= \func_num_args() && null !== ($request = \func_get_arg(0)) && isset($this->errorCount[$hash = spl_object_hash($request)])) { + return $this->errorCount[$hash]; + } + + return array_sum($this->errorCount); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->records = array(); + $this->errorCount = array(); + } +} diff --git a/vendor/symfony/monolog-bridge/Processor/TokenProcessor.php b/vendor/symfony/monolog-bridge/Processor/TokenProcessor.php new file mode 100644 index 0000000..11547be --- /dev/null +++ b/vendor/symfony/monolog-bridge/Processor/TokenProcessor.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\Bridge\Monolog\Processor; + +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + +/** + * Adds the current security token to the log entry. + * + * @author Dany Maillard + */ +class TokenProcessor +{ + private $tokenStorage; + + public function __construct(TokenStorageInterface $tokenStorage) + { + $this->tokenStorage = $tokenStorage; + } + + public function __invoke(array $records) + { + $records['extra']['token'] = null; + if (null !== $token = $this->tokenStorage->getToken()) { + $records['extra']['token'] = array( + 'username' => $token->getUsername(), + 'authenticated' => $token->isAuthenticated(), + 'roles' => array_map(function ($role) { return $role->getRole(); }, $token->getRoles()), + ); + } + + return $records; + } +} diff --git a/vendor/symfony/monolog-bridge/Processor/WebProcessor.php b/vendor/symfony/monolog-bridge/Processor/WebProcessor.php new file mode 100644 index 0000000..9c32e75 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Processor/WebProcessor.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\Bridge\Monolog\Processor; + +use Monolog\Processor\WebProcessor as BaseWebProcessor; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +/** + * WebProcessor override to read from the HttpFoundation's Request. + * + * @author Jordi Boggiano + */ +class WebProcessor extends BaseWebProcessor implements EventSubscriberInterface +{ + public function __construct(array $extraFields = null) + { + // Pass an empty array as the default null value would access $_SERVER + parent::__construct(array(), $extraFields); + } + + public function onKernelRequest(GetResponseEvent $event) + { + if ($event->isMasterRequest()) { + $this->serverData = $event->getRequest()->server->all(); + $this->serverData['REMOTE_ADDR'] = $event->getRequest()->getClientIp(); + } + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array('onKernelRequest', 4096), + ); + } +} diff --git a/vendor/symfony/monolog-bridge/README.md b/vendor/symfony/monolog-bridge/README.md new file mode 100644 index 0000000..2d19b3e --- /dev/null +++ b/vendor/symfony/monolog-bridge/README.md @@ -0,0 +1,12 @@ +Monolog Bridge +============== + +Provides integration for Monolog with various Symfony components. + +Resources +--------- + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/monolog-bridge/Tests/Handler/ConsoleHandlerTest.php b/vendor/symfony/monolog-bridge/Tests/Handler/ConsoleHandlerTest.php new file mode 100644 index 0000000..0764f89 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/Handler/ConsoleHandlerTest.php @@ -0,0 +1,208 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Monolog\Tests\Handler; + +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Handler\ConsoleHandler; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleCommandEvent; +use Symfony\Component\Console\Event\ConsoleTerminateEvent; +use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; + +/** + * Tests the ConsoleHandler and also the ConsoleFormatter. + * + * @author Tobias Schultze + */ +class ConsoleHandlerTest extends TestCase +{ + public function testConstructor() + { + $handler = new ConsoleHandler(null, false); + $this->assertFalse($handler->getBubble(), 'the bubble parameter gets propagated'); + } + + public function testIsHandling() + { + $handler = new ConsoleHandler(); + $this->assertFalse($handler->isHandling(array()), '->isHandling returns false when no output is set'); + } + + /** + * @dataProvider provideVerbosityMappingTests + */ + public function testVerbosityMapping($verbosity, $level, $isHandling, array $map = array()) + { + $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); + $output + ->expects($this->atLeastOnce()) + ->method('getVerbosity') + ->will($this->returnValue($verbosity)) + ; + $handler = new ConsoleHandler($output, true, $map); + $this->assertSame($isHandling, $handler->isHandling(array('level' => $level)), + '->isHandling returns correct value depending on console verbosity and log level' + ); + + // check that the handler actually outputs the record if it handles it + $levelName = Logger::getLevelName($level); + $levelName = sprintf('%-9s', $levelName); + + $realOutput = $this->getMockBuilder('Symfony\Component\Console\Output\Output')->setMethods(array('doWrite'))->getMock(); + $realOutput->setVerbosity($verbosity); + if ($realOutput->isDebug()) { + $log = "16:21:54 $levelName [app] My info message\n"; + } else { + $log = "16:21:54 $levelName [app] My info message\n"; + } + $realOutput + ->expects($isHandling ? $this->once() : $this->never()) + ->method('doWrite') + ->with($log, false); + $handler = new ConsoleHandler($realOutput, true, $map); + + $infoRecord = array( + 'message' => 'My info message', + 'context' => array(), + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'app', + 'datetime' => new \DateTime('2013-05-29 16:21:54'), + 'extra' => array(), + ); + $this->assertFalse($handler->handle($infoRecord), 'The handler finished handling the log.'); + } + + public function provideVerbosityMappingTests() + { + return array( + array(OutputInterface::VERBOSITY_QUIET, Logger::ERROR, true), + array(OutputInterface::VERBOSITY_QUIET, Logger::WARNING, false), + array(OutputInterface::VERBOSITY_NORMAL, Logger::WARNING, true), + array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, false), + array(OutputInterface::VERBOSITY_VERBOSE, Logger::NOTICE, true), + array(OutputInterface::VERBOSITY_VERBOSE, Logger::INFO, false), + array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::INFO, true), + array(OutputInterface::VERBOSITY_VERY_VERBOSE, Logger::DEBUG, false), + array(OutputInterface::VERBOSITY_DEBUG, Logger::DEBUG, true), + array(OutputInterface::VERBOSITY_DEBUG, Logger::EMERGENCY, true), + array(OutputInterface::VERBOSITY_NORMAL, Logger::NOTICE, true, array( + OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE, + )), + array(OutputInterface::VERBOSITY_DEBUG, Logger::NOTICE, true, array( + OutputInterface::VERBOSITY_NORMAL => Logger::NOTICE, + )), + ); + } + + public function testVerbosityChanged() + { + $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); + $output + ->expects($this->at(0)) + ->method('getVerbosity') + ->will($this->returnValue(OutputInterface::VERBOSITY_QUIET)) + ; + $output + ->expects($this->at(1)) + ->method('getVerbosity') + ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG)) + ; + $handler = new ConsoleHandler($output); + $this->assertFalse($handler->isHandling(array('level' => Logger::NOTICE)), + 'when verbosity is set to quiet, the handler does not handle the log' + ); + $this->assertTrue($handler->isHandling(array('level' => Logger::NOTICE)), + 'since the verbosity of the output increased externally, the handler is now handling the log' + ); + } + + public function testGetFormatter() + { + $handler = new ConsoleHandler(); + $this->assertInstanceOf('Symfony\Bridge\Monolog\Formatter\ConsoleFormatter', $handler->getFormatter(), + '-getFormatter returns ConsoleFormatter by default' + ); + } + + public function testWritingAndFormatting() + { + $output = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); + $output + ->expects($this->any()) + ->method('getVerbosity') + ->will($this->returnValue(OutputInterface::VERBOSITY_DEBUG)) + ; + $output + ->expects($this->once()) + ->method('write') + ->with("16:21:54 INFO [app] My info message\n") + ; + + $handler = new ConsoleHandler(null, false); + $handler->setOutput($output); + + $infoRecord = array( + 'message' => 'My info message', + 'context' => array(), + 'level' => Logger::INFO, + 'level_name' => Logger::getLevelName(Logger::INFO), + 'channel' => 'app', + 'datetime' => new \DateTime('2013-05-29 16:21:54'), + 'extra' => array(), + ); + + $this->assertTrue($handler->handle($infoRecord), 'The handler finished handling the log as bubble is false.'); + } + + public function testLogsFromListeners() + { + $output = new BufferedOutput(); + $output->setVerbosity(OutputInterface::VERBOSITY_DEBUG); + + $handler = new ConsoleHandler(null, false); + + $logger = new Logger('app'); + $logger->pushHandler($handler); + + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(ConsoleEvents::COMMAND, function () use ($logger) { + $logger->addInfo('Before command message.'); + }); + $dispatcher->addListener(ConsoleEvents::TERMINATE, function () use ($logger) { + $logger->addInfo('Before terminate message.'); + }); + + $dispatcher->addSubscriber($handler); + + $dispatcher->addListener(ConsoleEvents::COMMAND, function () use ($logger) { + $logger->addInfo('After command message.'); + }); + $dispatcher->addListener(ConsoleEvents::TERMINATE, function () use ($logger) { + $logger->addInfo('After terminate message.'); + }); + + $event = new ConsoleCommandEvent(new Command('foo'), $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(), $output); + $dispatcher->dispatch(ConsoleEvents::COMMAND, $event); + $this->assertContains('Before command message.', $out = $output->fetch()); + $this->assertContains('After command message.', $out); + + $event = new ConsoleTerminateEvent(new Command('foo'), $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(), $output, 0); + $dispatcher->dispatch(ConsoleEvents::TERMINATE, $event); + $this->assertContains('Before terminate message.', $out = $output->fetch()); + $this->assertContains('After terminate message.', $out); + } +} diff --git a/vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php b/vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.php new file mode 100644 index 0000000..9f0b0b3 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/HttpCodeActivationStrategyTest.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\Bridge\Monolog\Tests\Handler\FingersCrossed; + +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Handler\FingersCrossed\HttpCodeActivationStrategy; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\HttpException; + +class HttpCodeActivationStrategyTest extends TestCase +{ + /** + * @expectedException \LogicException + */ + public function testExclusionsWithoutCode() + { + new HttpCodeActivationStrategy(new RequestStack(), array(array('urls' => array())), Logger::WARNING); + } + + /** + * @expectedException \LogicException + */ + public function testExclusionsWithoutUrls() + { + new HttpCodeActivationStrategy(new RequestStack(), array(array('code' => 404)), Logger::WARNING); + } + + /** + * @dataProvider isActivatedProvider + */ + public function testIsActivated($url, $record, $expected) + { + $requestStack = new RequestStack(); + $requestStack->push(Request::create($url)); + + $strategy = new HttpCodeActivationStrategy( + $requestStack, + array( + array('code' => 403, 'urls' => array()), + array('code' => 404, 'urls' => array()), + array('code' => 405, 'urls' => array()), + array('code' => 400, 'urls' => array('^/400/a', '^/400/b')), + ), + Logger::WARNING + ); + + $this->assertEquals($expected, $strategy->isHandlerActivated($record)); + } + + public function isActivatedProvider() + { + return array( + array('/test', array('level' => Logger::ERROR), true), + array('/400', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), true), + array('/400/a', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), false), + array('/400/b', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), false), + array('/400/c', array('level' => Logger::ERROR, 'context' => $this->getContextException(400)), true), + array('/401', array('level' => Logger::ERROR, 'context' => $this->getContextException(401)), true), + array('/403', array('level' => Logger::ERROR, 'context' => $this->getContextException(403)), false), + array('/404', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), + array('/405', array('level' => Logger::ERROR, 'context' => $this->getContextException(405)), false), + array('/500', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), + ); + } + + protected function getContextException($code) + { + return array('exception' => new HttpException($code)); + } +} diff --git a/vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php b/vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.php new file mode 100644 index 0000000..3c34f06 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/Handler/FingersCrossed/NotFoundActivationStrategyTest.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\Bridge\Monolog\Tests\Handler\FingersCrossed; + +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Handler\FingersCrossed\NotFoundActivationStrategy; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpKernel\Exception\HttpException; + +class NotFoundActivationStrategyTest extends TestCase +{ + /** + * @dataProvider isActivatedProvider + */ + public function testIsActivated($url, $record, $expected) + { + $requestStack = new RequestStack(); + $requestStack->push(Request::create($url)); + + $strategy = new NotFoundActivationStrategy($requestStack, array('^/foo', 'bar'), Logger::WARNING); + + $this->assertEquals($expected, $strategy->isHandlerActivated($record)); + } + + public function isActivatedProvider() + { + return array( + array('/test', array('level' => Logger::DEBUG), false), + array('/foo', array('level' => Logger::DEBUG, 'context' => $this->getContextException(404)), false), + array('/baz/bar', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), + array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), false), + array('/foo', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), + + array('/test', array('level' => Logger::ERROR), true), + array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(404)), true), + array('/baz', array('level' => Logger::ERROR, 'context' => $this->getContextException(500)), true), + ); + } + + protected function getContextException($code) + { + return array('exception' => new HttpException($code)); + } +} diff --git a/vendor/symfony/monolog-bridge/Tests/LoggerTest.php b/vendor/symfony/monolog-bridge/Tests/LoggerTest.php new file mode 100644 index 0000000..193cde6 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/LoggerTest.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\Bridge\Monolog\Tests; + +use Monolog\Handler\TestHandler; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Logger; +use Symfony\Bridge\Monolog\Processor\DebugProcessor; +use Symfony\Component\HttpFoundation\Request; + +class LoggerTest extends TestCase +{ + public function testGetLogsWithoutDebugProcessor() + { + $handler = new TestHandler(); + $logger = new Logger(__METHOD__, array($handler)); + + $this->assertTrue($logger->error('error message')); + $this->assertSame(array(), $logger->getLogs()); + } + + public function testCountErrorsWithoutDebugProcessor() + { + $handler = new TestHandler(); + $logger = new Logger(__METHOD__, array($handler)); + + $this->assertTrue($logger->error('error message')); + $this->assertSame(0, $logger->countErrors()); + } + + public function testGetLogsWithDebugProcessor() + { + $handler = new TestHandler(); + $processor = new DebugProcessor(); + $logger = new Logger(__METHOD__, array($handler), array($processor)); + + $this->assertTrue($logger->error('error message')); + $this->assertCount(1, $logger->getLogs()); + } + + public function testCountErrorsWithDebugProcessor() + { + $handler = new TestHandler(); + $processor = new DebugProcessor(); + $logger = new Logger(__METHOD__, array($handler), array($processor)); + + $this->assertTrue($logger->debug('test message')); + $this->assertTrue($logger->info('test message')); + $this->assertTrue($logger->notice('test message')); + $this->assertTrue($logger->warning('test message')); + + $this->assertTrue($logger->error('test message')); + $this->assertTrue($logger->critical('test message')); + $this->assertTrue($logger->alert('test message')); + $this->assertTrue($logger->emergency('test message')); + + $this->assertSame(4, $logger->countErrors()); + } + + public function testGetLogsWithDebugProcessor2() + { + $handler = new TestHandler(); + $logger = new Logger('test', array($handler)); + $logger->pushProcessor(new DebugProcessor()); + + $logger->addInfo('test'); + $this->assertCount(1, $logger->getLogs()); + list($record) = $logger->getLogs(); + + $this->assertEquals('test', $record['message']); + $this->assertEquals(Logger::INFO, $record['priority']); + } + + public function testGetLogsWithDebugProcessor3() + { + $request = new Request(); + $processor = $this->getMockBuilder(DebugProcessor::class)->getMock(); + $processor->expects($this->once())->method('getLogs')->with($request); + $processor->expects($this->once())->method('countErrors')->with($request); + + $handler = new TestHandler(); + $logger = new Logger('test', array($handler)); + $logger->pushProcessor($processor); + + $logger->getLogs($request); + $logger->countErrors($request); + } + + public function testClear() + { + $handler = new TestHandler(); + $logger = new Logger('test', array($handler)); + $logger->pushProcessor(new DebugProcessor()); + + $logger->addInfo('test'); + $logger->clear(); + + $this->assertEmpty($logger->getLogs()); + $this->assertSame(0, $logger->countErrors()); + } +} diff --git a/vendor/symfony/monolog-bridge/Tests/Processor/DebugProcessorTest.php b/vendor/symfony/monolog-bridge/Tests/Processor/DebugProcessorTest.php new file mode 100644 index 0000000..9acaf60 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/Processor/DebugProcessorTest.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\Bridge\Monolog\Tests\Processor; + +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Processor\DebugProcessor; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; + +class DebugProcessorTest extends TestCase +{ + public function testDebugProcessor() + { + $processor = new DebugProcessor(); + $processor($this->getRecord()); + $processor($this->getRecord(Logger::ERROR)); + + $this->assertCount(2, $processor->getLogs()); + $this->assertSame(1, $processor->countErrors()); + } + + public function testDebugProcessorWithoutLogs() + { + $processor = new DebugProcessor(); + + $this->assertCount(0, $processor->getLogs()); + $this->assertSame(0, $processor->countErrors()); + } + + public function testWithRequestStack() + { + $stack = new RequestStack(); + $processor = new DebugProcessor($stack); + $processor($this->getRecord()); + $processor($this->getRecord(Logger::ERROR)); + + $this->assertCount(2, $processor->getLogs()); + $this->assertSame(1, $processor->countErrors()); + + $request = new Request(); + $stack->push($request); + + $processor($this->getRecord()); + $processor($this->getRecord(Logger::ERROR)); + + $this->assertCount(4, $processor->getLogs()); + $this->assertSame(2, $processor->countErrors()); + + $this->assertCount(2, $processor->getLogs($request)); + $this->assertSame(1, $processor->countErrors($request)); + } + + private function getRecord($level = Logger::WARNING, $message = 'test') + { + return array( + 'message' => $message, + 'context' => array(), + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + } +} diff --git a/vendor/symfony/monolog-bridge/Tests/Processor/TokenProcessorTest.php b/vendor/symfony/monolog-bridge/Tests/Processor/TokenProcessorTest.php new file mode 100644 index 0000000..e78acf4 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/Processor/TokenProcessorTest.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\Bridge\Monolog\Tests\Processor; + +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Processor\TokenProcessor; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; + +/** + * Tests the TokenProcessor. + * + * @author Dany Maillard + */ +class TokenProcessorTest extends TestCase +{ + public function testProcessor() + { + $token = new UsernamePasswordToken('user', 'password', 'provider', array('ROLE_USER')); + $tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); + $tokenStorage->method('getToken')->willReturn($token); + + $processor = new TokenProcessor($tokenStorage); + $record = array('extra' => array()); + $record = $processor($record); + + $this->assertArrayHasKey('token', $record['extra']); + $this->assertEquals($token->getUsername(), $record['extra']['token']['username']); + $this->assertEquals($token->isAuthenticated(), $record['extra']['token']['authenticated']); + $roles = array_map(function ($role) { return $role->getRole(); }, $token->getRoles()); + $this->assertEquals($roles, $record['extra']['token']['roles']); + } +} diff --git a/vendor/symfony/monolog-bridge/Tests/Processor/WebProcessorTest.php b/vendor/symfony/monolog-bridge/Tests/Processor/WebProcessorTest.php new file mode 100644 index 0000000..c18f857 --- /dev/null +++ b/vendor/symfony/monolog-bridge/Tests/Processor/WebProcessorTest.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\Bridge\Monolog\Tests\Processor; + +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\Monolog\Processor\WebProcessor; +use Symfony\Component\HttpFoundation\Request; + +class WebProcessorTest extends TestCase +{ + public function testUsesRequestServerData() + { + list($event, $server) = $this->createRequestEvent(); + + $processor = new WebProcessor(); + $processor->onKernelRequest($event); + $record = $processor($this->getRecord()); + + $this->assertCount(5, $record['extra']); + $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']); + $this->assertEquals($server['REMOTE_ADDR'], $record['extra']['ip']); + $this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']); + $this->assertEquals($server['SERVER_NAME'], $record['extra']['server']); + $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); + } + + public function testUseRequestClientIp() + { + Request::setTrustedProxies(array('192.168.0.1'), Request::HEADER_X_FORWARDED_ALL); + list($event, $server) = $this->createRequestEvent(array('X_FORWARDED_FOR' => '192.168.0.2')); + + $processor = new WebProcessor(); + $processor->onKernelRequest($event); + $record = $processor($this->getRecord()); + + $this->assertCount(5, $record['extra']); + $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']); + $this->assertEquals($server['X_FORWARDED_FOR'], $record['extra']['ip']); + $this->assertEquals($server['REQUEST_METHOD'], $record['extra']['http_method']); + $this->assertEquals($server['SERVER_NAME'], $record['extra']['server']); + $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); + + Request::setTrustedProxies(array(), -1); + } + + public function testCanBeConstructedWithExtraFields() + { + if (!$this->isExtraFieldsSupported()) { + $this->markTestSkipped('WebProcessor of the installed Monolog version does not support $extraFields parameter'); + } + + list($event, $server) = $this->createRequestEvent(); + + $processor = new WebProcessor(array('url', 'referrer')); + $processor->onKernelRequest($event); + $record = $processor($this->getRecord()); + + $this->assertCount(2, $record['extra']); + $this->assertEquals($server['REQUEST_URI'], $record['extra']['url']); + $this->assertEquals($server['HTTP_REFERER'], $record['extra']['referrer']); + } + + private function createRequestEvent($additionalServerParameters = array()): array + { + $server = array_merge( + array( + 'REQUEST_URI' => 'A', + 'REMOTE_ADDR' => '192.168.0.1', + 'REQUEST_METHOD' => 'C', + 'SERVER_NAME' => 'D', + 'HTTP_REFERER' => 'E', + ), + $additionalServerParameters + ); + + $request = new Request(); + $request->server->replace($server); + $request->headers->replace($server); + + $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseEvent') + ->disableOriginalConstructor() + ->getMock(); + $event->expects($this->any()) + ->method('isMasterRequest') + ->will($this->returnValue(true)); + $event->expects($this->any()) + ->method('getRequest') + ->will($this->returnValue($request)); + + return array($event, $server); + } + + private function getRecord(int $level = Logger::WARNING, string $message = 'test'): array + { + return array( + 'message' => $message, + 'context' => array(), + 'level' => $level, + 'level_name' => Logger::getLevelName($level), + 'channel' => 'test', + 'datetime' => new \DateTime(), + 'extra' => array(), + ); + } + + private function isExtraFieldsSupported() + { + $monologWebProcessorClass = new \ReflectionClass('Monolog\Processor\WebProcessor'); + + foreach ($monologWebProcessorClass->getConstructor()->getParameters() as $parameter) { + if ('extraFields' === $parameter->getName()) { + return true; + } + } + + return false; + } +} diff --git a/vendor/symfony/monolog-bridge/composer.json b/vendor/symfony/monolog-bridge/composer.json new file mode 100644 index 0000000..3a9be51 --- /dev/null +++ b/vendor/symfony/monolog-bridge/composer.json @@ -0,0 +1,51 @@ +{ + "name": "symfony/monolog-bridge", + "type": "symfony-bridge", + "description": "Symfony Monolog Bridge", + "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": "^7.1.3", + "monolog/monolog": "~1.19", + "symfony/http-kernel": "~3.4|~4.0" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", + "symfony/security-core": "~3.4|~4.0", + "symfony/var-dumper": "~3.4|~4.0" + }, + "conflict": { + "symfony/console": "<3.4", + "symfony/http-foundation": "<3.4" + }, + "suggest": { + "symfony/http-kernel": "For using the debugging handlers together with the response life cycle of the HTTP kernel.", + "symfony/console": "For the possibility to show log messages in console commands depending on verbosity settings.", + "symfony/event-dispatcher": "Needed when using log messages in console commands.", + "symfony/var-dumper": "For using the debugging handlers like the console handler or the log server handler." + }, + "autoload": { + "psr-4": { "Symfony\\Bridge\\Monolog\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/vendor/symfony/monolog-bridge/phpunit.xml.dist b/vendor/symfony/monolog-bridge/phpunit.xml.dist new file mode 100644 index 0000000..2890590 --- /dev/null +++ b/vendor/symfony/monolog-bridge/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/options-resolver/.gitignore b/vendor/symfony/options-resolver/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/options-resolver/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/options-resolver/CHANGELOG.md b/vendor/symfony/options-resolver/CHANGELOG.md new file mode 100644 index 0000000..6e9d49f --- /dev/null +++ b/vendor/symfony/options-resolver/CHANGELOG.md @@ -0,0 +1,52 @@ +CHANGELOG +========= + +3.4.0 +----- + + * added `OptionsResolverIntrospector` to inspect options definitions inside an `OptionsResolver` instance + * added array of types support in allowed types (e.g int[]) + +2.6.0 +----- + + * deprecated OptionsResolverInterface + * [BC BREAK] removed "array" type hint from OptionsResolverInterface methods + setRequired(), setAllowedValues(), addAllowedValues(), setAllowedTypes() and + addAllowedTypes() + * added OptionsResolver::setDefault() + * added OptionsResolver::hasDefault() + * added OptionsResolver::setNormalizer() + * added OptionsResolver::isRequired() + * added OptionsResolver::getRequiredOptions() + * added OptionsResolver::isMissing() + * added OptionsResolver::getMissingOptions() + * added OptionsResolver::setDefined() + * added OptionsResolver::isDefined() + * added OptionsResolver::getDefinedOptions() + * added OptionsResolver::remove() + * added OptionsResolver::clear() + * deprecated OptionsResolver::replaceDefaults() + * deprecated OptionsResolver::setOptional() in favor of setDefined() + * deprecated OptionsResolver::isKnown() in favor of isDefined() + * [BC BREAK] OptionsResolver::isRequired() returns true now if a required + option has a default value set + * [BC BREAK] merged Options into OptionsResolver and turned Options into an + interface + * deprecated Options::overload() (now in OptionsResolver) + * deprecated Options::set() (now in OptionsResolver) + * deprecated Options::get() (now in OptionsResolver) + * deprecated Options::has() (now in OptionsResolver) + * deprecated Options::replace() (now in OptionsResolver) + * [BC BREAK] Options::get() (now in OptionsResolver) can only be used within + lazy option/normalizer closures now + * [BC BREAK] removed Traversable interface from Options since using within + lazy option/normalizer closures resulted in exceptions + * [BC BREAK] removed Options::all() since using within lazy option/normalizer + closures resulted in exceptions + * [BC BREAK] OptionDefinitionException now extends LogicException instead of + RuntimeException + * [BC BREAK] normalizers are not executed anymore for unset options + * normalizers are executed after validating the options now + * [BC BREAK] an UndefinedOptionsException is now thrown instead of an + InvalidOptionsException when non-existing options are passed diff --git a/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php b/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.php new file mode 100644 index 0000000..146c18b --- /dev/null +++ b/vendor/symfony/options-resolver/Debug/OptionsResolverIntrospector.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\OptionsResolver\Debug; + +use Symfony\Component\OptionsResolver\Exception\NoConfigurationException; +use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; +use Symfony\Component\OptionsResolver\OptionsResolver; + +/** + * @author Maxime Steinhausser + * + * @final + */ +class OptionsResolverIntrospector +{ + private $get; + + public function __construct(OptionsResolver $optionsResolver) + { + $this->get = \Closure::bind(function ($property, $option, $message) { + /** @var OptionsResolver $this */ + if (!$this->isDefined($option)) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist.', $option)); + } + + if (!array_key_exists($option, $this->{$property})) { + throw new NoConfigurationException($message); + } + + return $this->{$property}[$option]; + }, $optionsResolver, $optionsResolver); + } + + /** + * @param string $option + * + * @return mixed + * + * @throws NoConfigurationException on no configured value + */ + public function getDefault($option) + { + return \call_user_func($this->get, 'defaults', $option, sprintf('No default value was set for the "%s" option.', $option)); + } + + /** + * @param string $option + * + * @return \Closure[] + * + * @throws NoConfigurationException on no configured closures + */ + public function getLazyClosures($option) + { + return \call_user_func($this->get, 'lazy', $option, sprintf('No lazy closures were set for the "%s" option.', $option)); + } + + /** + * @param string $option + * + * @return string[] + * + * @throws NoConfigurationException on no configured types + */ + public function getAllowedTypes($option) + { + return \call_user_func($this->get, 'allowedTypes', $option, sprintf('No allowed types were set for the "%s" option.', $option)); + } + + /** + * @param string $option + * + * @return mixed[] + * + * @throws NoConfigurationException on no configured values + */ + public function getAllowedValues($option) + { + return \call_user_func($this->get, 'allowedValues', $option, sprintf('No allowed values were set for the "%s" option.', $option)); + } + + /** + * @param string $option + * + * @return \Closure + * + * @throws NoConfigurationException on no configured normalizer + */ + public function getNormalizer($option) + { + return \call_user_func($this->get, 'normalizers', $option, sprintf('No normalizer was set for the "%s" option.', $option)); + } +} diff --git a/vendor/symfony/options-resolver/Exception/AccessException.php b/vendor/symfony/options-resolver/Exception/AccessException.php new file mode 100644 index 0000000..c12b680 --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/AccessException.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\OptionsResolver\Exception; + +/** + * Thrown when trying to read an option outside of or write it inside of + * {@link \Symfony\Component\OptionsResolver\Options::resolve()}. + * + * @author Bernhard Schussek + */ +class AccessException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Exception/ExceptionInterface.php b/vendor/symfony/options-resolver/Exception/ExceptionInterface.php new file mode 100644 index 0000000..b62bb51 --- /dev/null +++ b/vendor/symfony/options-resolver/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\OptionsResolver\Exception; + +/** + * Marker interface for all exceptions thrown by the OptionsResolver component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Exception/InvalidArgumentException.php b/vendor/symfony/options-resolver/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..6d421d6 --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/InvalidArgumentException.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\OptionsResolver\Exception; + +/** + * Thrown when an argument is invalid. + * + * @author Bernhard Schussek + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Exception/InvalidOptionsException.php b/vendor/symfony/options-resolver/Exception/InvalidOptionsException.php new file mode 100644 index 0000000..6fd4f12 --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/InvalidOptionsException.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\OptionsResolver\Exception; + +/** + * Thrown when the value of an option does not match its validation rules. + * + * You should make sure a valid value is passed to the option. + * + * @author Bernhard Schussek + */ +class InvalidOptionsException extends InvalidArgumentException +{ +} diff --git a/vendor/symfony/options-resolver/Exception/MissingOptionsException.php b/vendor/symfony/options-resolver/Exception/MissingOptionsException.php new file mode 100644 index 0000000..faa487f --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/MissingOptionsException.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\OptionsResolver\Exception; + +/** + * Exception thrown when a required option is missing. + * + * Add the option to the passed options array. + * + * @author Bernhard Schussek + */ +class MissingOptionsException extends InvalidArgumentException +{ +} diff --git a/vendor/symfony/options-resolver/Exception/NoConfigurationException.php b/vendor/symfony/options-resolver/Exception/NoConfigurationException.php new file mode 100644 index 0000000..6693ec1 --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/NoConfigurationException.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\OptionsResolver\Exception; + +use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector; + +/** + * Thrown when trying to introspect an option definition property + * for which no value was configured inside the OptionsResolver instance. + * + * @see OptionsResolverIntrospector + * + * @author Maxime Steinhausser + */ +class NoConfigurationException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Exception/NoSuchOptionException.php b/vendor/symfony/options-resolver/Exception/NoSuchOptionException.php new file mode 100644 index 0000000..4c3280f --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/NoSuchOptionException.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\OptionsResolver\Exception; + +/** + * Thrown when trying to read an option that has no value set. + * + * When accessing optional options from within a lazy option or normalizer you should first + * check whether the optional option is set. You can do this with `isset($options['optional'])`. + * In contrast to the {@link UndefinedOptionsException}, this is a runtime exception that can + * occur when evaluating lazy options. + * + * @author Tobias Schultze + */ +class NoSuchOptionException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Exception/OptionDefinitionException.php b/vendor/symfony/options-resolver/Exception/OptionDefinitionException.php new file mode 100644 index 0000000..e8e339d --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/OptionDefinitionException.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\OptionsResolver\Exception; + +/** + * Thrown when two lazy options have a cyclic dependency. + * + * @author Bernhard Schussek + */ +class OptionDefinitionException extends \LogicException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php b/vendor/symfony/options-resolver/Exception/UndefinedOptionsException.php new file mode 100644 index 0000000..6ca3fce --- /dev/null +++ b/vendor/symfony/options-resolver/Exception/UndefinedOptionsException.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\OptionsResolver\Exception; + +/** + * Exception thrown when an undefined option is passed. + * + * You should remove the options in question from your code or define them + * beforehand. + * + * @author Bernhard Schussek + */ +class UndefinedOptionsException extends InvalidArgumentException +{ +} diff --git a/vendor/symfony/options-resolver/LICENSE b/vendor/symfony/options-resolver/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/options-resolver/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/vendor/symfony/options-resolver/Options.php b/vendor/symfony/options-resolver/Options.php new file mode 100644 index 0000000..d444ec4 --- /dev/null +++ b/vendor/symfony/options-resolver/Options.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\OptionsResolver; + +/** + * Contains resolved option values. + * + * @author Bernhard Schussek + * @author Tobias Schultze + */ +interface Options extends \ArrayAccess, \Countable +{ +} diff --git a/vendor/symfony/options-resolver/OptionsResolver.php b/vendor/symfony/options-resolver/OptionsResolver.php new file mode 100644 index 0000000..83d8196 --- /dev/null +++ b/vendor/symfony/options-resolver/OptionsResolver.php @@ -0,0 +1,1065 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\OptionsResolver; + +use Symfony\Component\OptionsResolver\Exception\AccessException; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Exception\MissingOptionsException; +use Symfony\Component\OptionsResolver\Exception\NoSuchOptionException; +use Symfony\Component\OptionsResolver\Exception\OptionDefinitionException; +use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException; + +/** + * Validates options and merges them with default values. + * + * @author Bernhard Schussek + * @author Tobias Schultze + */ +class OptionsResolver implements Options +{ + /** + * The names of all defined options. + */ + private $defined = array(); + + /** + * The default option values. + */ + private $defaults = array(); + + /** + * The names of required options. + */ + private $required = array(); + + /** + * The resolved option values. + */ + private $resolved = array(); + + /** + * A list of normalizer closures. + * + * @var \Closure[] + */ + private $normalizers = array(); + + /** + * A list of accepted values for each option. + */ + private $allowedValues = array(); + + /** + * A list of accepted types for each option. + */ + private $allowedTypes = array(); + + /** + * A list of closures for evaluating lazy options. + */ + private $lazy = array(); + + /** + * A list of lazy options whose closure is currently being called. + * + * This list helps detecting circular dependencies between lazy options. + */ + private $calling = array(); + + /** + * Whether the instance is locked for reading. + * + * Once locked, the options cannot be changed anymore. This is + * necessary in order to avoid inconsistencies during the resolving + * process. If any option is changed after being read, all evaluated + * lazy options that depend on this option would become invalid. + */ + private $locked = false; + + private static $typeAliases = array( + 'boolean' => 'bool', + 'integer' => 'int', + 'double' => 'float', + ); + + /** + * Sets the default value of a given option. + * + * If the default value should be set based on other options, you can pass + * a closure with the following signature: + * + * function (Options $options) { + * // ... + * } + * + * The closure will be evaluated when {@link resolve()} is called. The + * closure has access to the resolved values of other options through the + * passed {@link Options} instance: + * + * function (Options $options) { + * if (isset($options['port'])) { + * // ... + * } + * } + * + * If you want to access the previously set default value, add a second + * argument to the closure's signature: + * + * $options->setDefault('name', 'Default Name'); + * + * $options->setDefault('name', function (Options $options, $previousValue) { + * // 'Default Name' === $previousValue + * }); + * + * This is mostly useful if the configuration of the {@link Options} object + * is spread across different locations of your code, such as base and + * sub-classes. + * + * @param string $option The name of the option + * @param mixed $value The default value of the option + * + * @return $this + * + * @throws AccessException If called from a lazy option or normalizer + */ + public function setDefault($option, $value) + { + // Setting is not possible once resolving starts, because then lazy + // options could manipulate the state of the object, leading to + // inconsistent results. + if ($this->locked) { + throw new AccessException('Default values cannot be set from a lazy option or normalizer.'); + } + + // If an option is a closure that should be evaluated lazily, store it + // in the "lazy" property. + if ($value instanceof \Closure) { + $reflClosure = new \ReflectionFunction($value); + $params = $reflClosure->getParameters(); + + if (isset($params[0]) && null !== ($class = $params[0]->getClass()) && Options::class === $class->name) { + // Initialize the option if no previous value exists + if (!isset($this->defaults[$option])) { + $this->defaults[$option] = null; + } + + // Ignore previous lazy options if the closure has no second parameter + if (!isset($this->lazy[$option]) || !isset($params[1])) { + $this->lazy[$option] = array(); + } + + // Store closure for later evaluation + $this->lazy[$option][] = $value; + $this->defined[$option] = true; + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + } + + // This option is not lazy anymore + unset($this->lazy[$option]); + + // Yet undefined options can be marked as resolved, because we only need + // to resolve options with lazy closures, normalizers or validation + // rules, none of which can exist for undefined options + // If the option was resolved before, update the resolved value + if (!isset($this->defined[$option]) || array_key_exists($option, $this->resolved)) { + $this->resolved[$option] = $value; + } + + $this->defaults[$option] = $value; + $this->defined[$option] = true; + + return $this; + } + + /** + * Sets a list of default values. + * + * @param array $defaults The default values to set + * + * @return $this + * + * @throws AccessException If called from a lazy option or normalizer + */ + public function setDefaults(array $defaults) + { + foreach ($defaults as $option => $value) { + $this->setDefault($option, $value); + } + + return $this; + } + + /** + * Returns whether a default value is set for an option. + * + * Returns true if {@link setDefault()} was called for this option. + * An option is also considered set if it was set to null. + * + * @param string $option The option name + * + * @return bool Whether a default value is set + */ + public function hasDefault($option) + { + return array_key_exists($option, $this->defaults); + } + + /** + * Marks one or more options as required. + * + * @param string|string[] $optionNames One or more option names + * + * @return $this + * + * @throws AccessException If called from a lazy option or normalizer + */ + public function setRequired($optionNames) + { + if ($this->locked) { + throw new AccessException('Options cannot be made required from a lazy option or normalizer.'); + } + + foreach ((array) $optionNames as $option) { + $this->defined[$option] = true; + $this->required[$option] = true; + } + + return $this; + } + + /** + * Returns whether an option is required. + * + * An option is required if it was passed to {@link setRequired()}. + * + * @param string $option The name of the option + * + * @return bool Whether the option is required + */ + public function isRequired($option) + { + return isset($this->required[$option]); + } + + /** + * Returns the names of all required options. + * + * @return string[] The names of the required options + * + * @see isRequired() + */ + public function getRequiredOptions() + { + return array_keys($this->required); + } + + /** + * Returns whether an option is missing a default value. + * + * An option is missing if it was passed to {@link setRequired()}, but not + * to {@link setDefault()}. This option must be passed explicitly to + * {@link resolve()}, otherwise an exception will be thrown. + * + * @param string $option The name of the option + * + * @return bool Whether the option is missing + */ + public function isMissing($option) + { + return isset($this->required[$option]) && !array_key_exists($option, $this->defaults); + } + + /** + * Returns the names of all options missing a default value. + * + * @return string[] The names of the missing options + * + * @see isMissing() + */ + public function getMissingOptions() + { + return array_keys(array_diff_key($this->required, $this->defaults)); + } + + /** + * Defines a valid option name. + * + * Defines an option name without setting a default value. The option will + * be accepted when passed to {@link resolve()}. When not passed, the + * option will not be included in the resolved options. + * + * @param string|string[] $optionNames One or more option names + * + * @return $this + * + * @throws AccessException If called from a lazy option or normalizer + */ + public function setDefined($optionNames) + { + if ($this->locked) { + throw new AccessException('Options cannot be defined from a lazy option or normalizer.'); + } + + foreach ((array) $optionNames as $option) { + $this->defined[$option] = true; + } + + return $this; + } + + /** + * Returns whether an option is defined. + * + * Returns true for any option passed to {@link setDefault()}, + * {@link setRequired()} or {@link setDefined()}. + * + * @param string $option The option name + * + * @return bool Whether the option is defined + */ + public function isDefined($option) + { + return isset($this->defined[$option]); + } + + /** + * Returns the names of all defined options. + * + * @return string[] The names of the defined options + * + * @see isDefined() + */ + public function getDefinedOptions() + { + return array_keys($this->defined); + } + + /** + * Sets the normalizer for an option. + * + * The normalizer should be a closure with the following signature: + * + * function (Options $options, $value) { + * // ... + * } + * + * The closure is invoked when {@link resolve()} is called. The closure + * has access to the resolved values of other options through the passed + * {@link Options} instance. + * + * The second parameter passed to the closure is the value of + * the option. + * + * The resolved option value is set to the return value of the closure. + * + * @param string $option The option name + * @param \Closure $normalizer The normalizer + * + * @return $this + * + * @throws UndefinedOptionsException If the option is undefined + * @throws AccessException If called from a lazy option or normalizer + */ + public function setNormalizer($option, \Closure $normalizer) + { + if ($this->locked) { + throw new AccessException('Normalizers cannot be set from a lazy option or normalizer.'); + } + + if (!isset($this->defined[$option])) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + $this->normalizers[$option] = $normalizer; + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + + /** + * Sets allowed values for an option. + * + * Instead of passing values, you may also pass a closures with the + * following signature: + * + * function ($value) { + * // return true or false + * } + * + * The closure receives the value as argument and should return true to + * accept the value and false to reject the value. + * + * @param string $option The option name + * @param mixed $allowedValues One or more acceptable values/closures + * + * @return $this + * + * @throws UndefinedOptionsException If the option is undefined + * @throws AccessException If called from a lazy option or normalizer + */ + public function setAllowedValues($option, $allowedValues) + { + if ($this->locked) { + throw new AccessException('Allowed values cannot be set from a lazy option or normalizer.'); + } + + if (!isset($this->defined[$option])) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + $this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues); + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + + /** + * Adds allowed values for an option. + * + * The values are merged with the allowed values defined previously. + * + * Instead of passing values, you may also pass a closures with the + * following signature: + * + * function ($value) { + * // return true or false + * } + * + * The closure receives the value as argument and should return true to + * accept the value and false to reject the value. + * + * @param string $option The option name + * @param mixed $allowedValues One or more acceptable values/closures + * + * @return $this + * + * @throws UndefinedOptionsException If the option is undefined + * @throws AccessException If called from a lazy option or normalizer + */ + public function addAllowedValues($option, $allowedValues) + { + if ($this->locked) { + throw new AccessException('Allowed values cannot be added from a lazy option or normalizer.'); + } + + if (!isset($this->defined[$option])) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + if (!\is_array($allowedValues)) { + $allowedValues = array($allowedValues); + } + + if (!isset($this->allowedValues[$option])) { + $this->allowedValues[$option] = $allowedValues; + } else { + $this->allowedValues[$option] = array_merge($this->allowedValues[$option], $allowedValues); + } + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + + /** + * Sets allowed types for an option. + * + * Any type for which a corresponding is_() function exists is + * acceptable. Additionally, fully-qualified class or interface names may + * be passed. + * + * @param string $option The option name + * @param string|string[] $allowedTypes One or more accepted types + * + * @return $this + * + * @throws UndefinedOptionsException If the option is undefined + * @throws AccessException If called from a lazy option or normalizer + */ + public function setAllowedTypes($option, $allowedTypes) + { + if ($this->locked) { + throw new AccessException('Allowed types cannot be set from a lazy option or normalizer.'); + } + + if (!isset($this->defined[$option])) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + $this->allowedTypes[$option] = (array) $allowedTypes; + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + + /** + * Adds allowed types for an option. + * + * The types are merged with the allowed types defined previously. + * + * Any type for which a corresponding is_() function exists is + * acceptable. Additionally, fully-qualified class or interface names may + * be passed. + * + * @param string $option The option name + * @param string|string[] $allowedTypes One or more accepted types + * + * @return $this + * + * @throws UndefinedOptionsException If the option is undefined + * @throws AccessException If called from a lazy option or normalizer + */ + public function addAllowedTypes($option, $allowedTypes) + { + if ($this->locked) { + throw new AccessException('Allowed types cannot be added from a lazy option or normalizer.'); + } + + if (!isset($this->defined[$option])) { + throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + if (!isset($this->allowedTypes[$option])) { + $this->allowedTypes[$option] = (array) $allowedTypes; + } else { + $this->allowedTypes[$option] = array_merge($this->allowedTypes[$option], (array) $allowedTypes); + } + + // Make sure the option is processed + unset($this->resolved[$option]); + + return $this; + } + + /** + * Removes the option with the given name. + * + * Undefined options are ignored. + * + * @param string|string[] $optionNames One or more option names + * + * @return $this + * + * @throws AccessException If called from a lazy option or normalizer + */ + public function remove($optionNames) + { + if ($this->locked) { + throw new AccessException('Options cannot be removed from a lazy option or normalizer.'); + } + + foreach ((array) $optionNames as $option) { + unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]); + unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option]); + } + + return $this; + } + + /** + * Removes all options. + * + * @return $this + * + * @throws AccessException If called from a lazy option or normalizer + */ + public function clear() + { + if ($this->locked) { + throw new AccessException('Options cannot be cleared from a lazy option or normalizer.'); + } + + $this->defined = array(); + $this->defaults = array(); + $this->required = array(); + $this->resolved = array(); + $this->lazy = array(); + $this->normalizers = array(); + $this->allowedTypes = array(); + $this->allowedValues = array(); + + return $this; + } + + /** + * Merges options with the default values stored in the container and + * validates them. + * + * Exceptions are thrown if: + * + * - Undefined options are passed; + * - Required options are missing; + * - Options have invalid types; + * - Options have invalid values. + * + * @param array $options A map of option names to values + * + * @return array The merged and validated options + * + * @throws UndefinedOptionsException If an option name is undefined + * @throws InvalidOptionsException If an option doesn't fulfill the + * specified validation rules + * @throws MissingOptionsException If a required option is missing + * @throws OptionDefinitionException If there is a cyclic dependency between + * lazy options and/or normalizers + * @throws NoSuchOptionException If a lazy option reads an unavailable option + * @throws AccessException If called from a lazy option or normalizer + */ + public function resolve(array $options = array()) + { + if ($this->locked) { + throw new AccessException('Options cannot be resolved from a lazy option or normalizer.'); + } + + // Allow this method to be called multiple times + $clone = clone $this; + + // Make sure that no unknown options are passed + $diff = array_diff_key($options, $clone->defined); + + if (\count($diff) > 0) { + ksort($clone->defined); + ksort($diff); + + throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', implode('", "', array_keys($diff)), implode('", "', array_keys($clone->defined)))); + } + + // Override options set by the user + foreach ($options as $option => $value) { + $clone->defaults[$option] = $value; + unset($clone->resolved[$option], $clone->lazy[$option]); + } + + // Check whether any required option is missing + $diff = array_diff_key($clone->required, $clone->defaults); + + if (\count($diff) > 0) { + ksort($diff); + + throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', implode('", "', array_keys($diff)))); + } + + // Lock the container + $clone->locked = true; + + // Now process the individual options. Use offsetGet(), which resolves + // the option itself and any options that the option depends on + foreach ($clone->defaults as $option => $_) { + $clone->offsetGet($option); + } + + return $clone->resolved; + } + + /** + * Returns the resolved value of an option. + * + * @param string $option The option name + * + * @return mixed The option value + * + * @throws AccessException If accessing this method outside of + * {@link resolve()} + * @throws NoSuchOptionException If the option is not set + * @throws InvalidOptionsException If the option doesn't fulfill the + * specified validation rules + * @throws OptionDefinitionException If there is a cyclic dependency between + * lazy options and/or normalizers + */ + public function offsetGet($option) + { + if (!$this->locked) { + throw new AccessException('Array access is only supported within closures of lazy options and normalizers.'); + } + + // Shortcut for resolved options + if (array_key_exists($option, $this->resolved)) { + return $this->resolved[$option]; + } + + // Check whether the option is set at all + if (!array_key_exists($option, $this->defaults)) { + if (!isset($this->defined[$option])) { + throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined)))); + } + + throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $option)); + } + + $value = $this->defaults[$option]; + + // Resolve the option if the default value is lazily evaluated + if (isset($this->lazy[$option])) { + // If the closure is already being called, we have a cyclic + // dependency + if (isset($this->calling[$option])) { + throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling)))); + } + + // The following section must be protected from cyclic + // calls. Set $calling for the current $option to detect a cyclic + // dependency + // BEGIN + $this->calling[$option] = true; + try { + foreach ($this->lazy[$option] as $closure) { + $value = $closure($this, $value); + } + } finally { + unset($this->calling[$option]); + } + // END + } + + // Validate the type of the resolved option + if (isset($this->allowedTypes[$option])) { + $valid = false; + $invalidTypes = array(); + + foreach ($this->allowedTypes[$option] as $type) { + $type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type; + + if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) { + break; + } + } + + if (!$valid) { + $keys = array_keys($invalidTypes); + + if (1 === \count($keys) && '[]' === substr($keys[0], -2)) { + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0])); + } + + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes)))); + } + } + + // Validate the value of the resolved option + if (isset($this->allowedValues[$option])) { + $success = false; + $printableAllowedValues = array(); + + foreach ($this->allowedValues[$option] as $allowedValue) { + if ($allowedValue instanceof \Closure) { + if ($allowedValue($value)) { + $success = true; + break; + } + + // Don't include closures in the exception message + continue; + } + + if ($value === $allowedValue) { + $success = true; + break; + } + + $printableAllowedValues[] = $allowedValue; + } + + if (!$success) { + $message = sprintf( + 'The option "%s" with value %s is invalid.', + $option, + $this->formatValue($value) + ); + + if (\count($printableAllowedValues) > 0) { + $message .= sprintf( + ' Accepted values are: %s.', + $this->formatValues($printableAllowedValues) + ); + } + + throw new InvalidOptionsException($message); + } + } + + // Normalize the validated option + if (isset($this->normalizers[$option])) { + // If the closure is already being called, we have a cyclic + // dependency + if (isset($this->calling[$option])) { + throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling)))); + } + + $normalizer = $this->normalizers[$option]; + + // The following section must be protected from cyclic + // calls. Set $calling for the current $option to detect a cyclic + // dependency + // BEGIN + $this->calling[$option] = true; + try { + $value = $normalizer($this, $value); + } finally { + unset($this->calling[$option]); + } + // END + } + + // Mark as resolved + $this->resolved[$option] = $value; + + return $value; + } + + /** + * @param string $type + * @param mixed $value + * @param array &$invalidTypes + * + * @return bool + */ + private function verifyTypes($type, $value, array &$invalidTypes) + { + if (\is_array($value) && '[]' === substr($type, -2)) { + return $this->verifyArrayType($type, $value, $invalidTypes); + } + + if (self::isValueValidType($type, $value)) { + return true; + } + + if (!$invalidTypes) { + $invalidTypes[$this->formatTypeOf($value, null)] = true; + } + + return false; + } + + /** + * @return bool + */ + private function verifyArrayType($type, array $value, array &$invalidTypes, $level = 0) + { + $type = substr($type, 0, -2); + + $suffix = '[]'; + while (\strlen($suffix) <= $level * 2) { + $suffix .= '[]'; + } + + if ('[]' === substr($type, -2)) { + $success = true; + foreach ($value as $item) { + if (!\is_array($item)) { + $invalidTypes[$this->formatTypeOf($item, null).$suffix] = true; + + return false; + } + + if (!$this->verifyArrayType($type, $item, $invalidTypes, $level + 1)) { + $success = false; + } + } + + return $success; + } + + foreach ($value as $item) { + if (!self::isValueValidType($type, $item)) { + $invalidTypes[$this->formatTypeOf($item, $type).$suffix] = $value; + + return false; + } + } + + return true; + } + + /** + * Returns whether a resolved option with the given name exists. + * + * @param string $option The option name + * + * @return bool Whether the option is set + * + * @throws AccessException If accessing this method outside of {@link resolve()} + * + * @see \ArrayAccess::offsetExists() + */ + public function offsetExists($option) + { + if (!$this->locked) { + throw new AccessException('Array access is only supported within closures of lazy options and normalizers.'); + } + + return array_key_exists($option, $this->defaults); + } + + /** + * Not supported. + * + * @throws AccessException + */ + public function offsetSet($option, $value) + { + throw new AccessException('Setting options via array access is not supported. Use setDefault() instead.'); + } + + /** + * Not supported. + * + * @throws AccessException + */ + public function offsetUnset($option) + { + throw new AccessException('Removing options via array access is not supported. Use remove() instead.'); + } + + /** + * Returns the number of set options. + * + * This may be only a subset of the defined options. + * + * @return int Number of options + * + * @throws AccessException If accessing this method outside of {@link resolve()} + * + * @see \Countable::count() + */ + public function count() + { + if (!$this->locked) { + throw new AccessException('Counting is only supported within closures of lazy options and normalizers.'); + } + + return \count($this->defaults); + } + + /** + * Returns a string representation of the type of the value. + * + * This method should be used if you pass the type of a value as + * message parameter to a constraint violation. Note that such + * parameters should usually not be included in messages aimed at + * non-technical people. + * + * @param mixed $value The value to return the type of + * @param string $type + * + * @return string The type of the value + */ + private function formatTypeOf($value, $type) + { + $suffix = ''; + + if ('[]' === substr($type, -2)) { + $suffix = '[]'; + $type = substr($type, 0, -2); + while ('[]' === substr($type, -2)) { + $type = substr($type, 0, -2); + $value = array_shift($value); + if (!\is_array($value)) { + break; + } + $suffix .= '[]'; + } + + if (\is_array($value)) { + $subTypes = array(); + foreach ($value as $val) { + $subTypes[$this->formatTypeOf($val, null)] = true; + } + + return implode('|', array_keys($subTypes)).$suffix; + } + } + + return (\is_object($value) ? \get_class($value) : \gettype($value)).$suffix; + } + + /** + * Returns a string representation of the value. + * + * This method returns the equivalent PHP tokens for most scalar types + * (i.e. "false" for false, "1" for 1 etc.). Strings are always wrapped + * in double quotes ("). + * + * @param mixed $value The value to format as string + * + * @return string The string representation of the passed value + */ + private function formatValue($value) + { + if (\is_object($value)) { + return \get_class($value); + } + + if (\is_array($value)) { + return 'array'; + } + + if (\is_string($value)) { + return '"'.$value.'"'; + } + + if (\is_resource($value)) { + return 'resource'; + } + + if (null === $value) { + return 'null'; + } + + if (false === $value) { + return 'false'; + } + + if (true === $value) { + return 'true'; + } + + return (string) $value; + } + + /** + * Returns a string representation of a list of values. + * + * Each of the values is converted to a string using + * {@link formatValue()}. The values are then concatenated with commas. + * + * @param array $values A list of values + * + * @return string The string representation of the value list + * + * @see formatValue() + */ + private function formatValues(array $values) + { + foreach ($values as $key => $value) { + $values[$key] = $this->formatValue($value); + } + + return implode(', ', $values); + } + + private static function isValueValidType($type, $value) + { + return (\function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type; + } +} diff --git a/vendor/symfony/options-resolver/README.md b/vendor/symfony/options-resolver/README.md new file mode 100644 index 0000000..245e69b --- /dev/null +++ b/vendor/symfony/options-resolver/README.md @@ -0,0 +1,15 @@ +OptionsResolver Component +========================= + +The OptionsResolver component is `array_replace` on steroids. It allows you to +create an options system with required options, defaults, validation (type, +value), normalization and more. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/options_resolver.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php b/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php new file mode 100644 index 0000000..7c4753a --- /dev/null +++ b/vendor/symfony/options-resolver/Tests/Debug/OptionsResolverIntrospectorTest.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\OptionsResolver\Tests\Debug; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class OptionsResolverIntrospectorTest extends TestCase +{ + public function testGetDefault() + { + $resolver = new OptionsResolver(); + $resolver->setDefault($option = 'foo', 'bar'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getDefault($option)); + } + + public function testGetDefaultNull() + { + $resolver = new OptionsResolver(); + $resolver->setDefault($option = 'foo', null); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertNull($debug->getDefault($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException + * @expectedExceptionMessage No default value was set for the "foo" option. + */ + public function testGetDefaultThrowsOnNoConfiguredValue() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getDefault($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. + */ + public function testGetDefaultThrowsOnNotDefinedOption() + { + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getDefault('foo')); + } + + public function testGetLazyClosures() + { + $resolver = new OptionsResolver(); + $closures = array(); + $resolver->setDefault($option = 'foo', $closures[] = function (Options $options) {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($closures, $debug->getLazyClosures($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException + * @expectedExceptionMessage No lazy closures were set for the "foo" option. + */ + public function testGetLazyClosuresThrowsOnNoConfiguredValue() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getLazyClosures($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. + */ + public function testGetLazyClosuresThrowsOnNotDefinedOption() + { + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getLazyClosures('foo')); + } + + public function testGetAllowedTypes() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + $resolver->setAllowedTypes($option = 'foo', $allowedTypes = array('string', 'bool')); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($allowedTypes, $debug->getAllowedTypes($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException + * @expectedExceptionMessage No allowed types were set for the "foo" option. + */ + public function testGetAllowedTypesThrowsOnNoConfiguredValue() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedTypes($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. + */ + public function testGetAllowedTypesThrowsOnNotDefinedOption() + { + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedTypes('foo')); + } + + public function testGetAllowedValues() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + $resolver->setAllowedValues($option = 'foo', $allowedValues = array('bar', 'baz')); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($allowedValues, $debug->getAllowedValues($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException + * @expectedExceptionMessage No allowed values were set for the "foo" option. + */ + public function testGetAllowedValuesThrowsOnNoConfiguredValue() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedValues($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. + */ + public function testGetAllowedValuesThrowsOnNotDefinedOption() + { + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getAllowedValues('foo')); + } + + public function testGetNormalizer() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + $resolver->setNormalizer($option = 'foo', $normalizer = function () {}); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame($normalizer, $debug->getNormalizer($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoConfigurationException + * @expectedExceptionMessage No normalizer was set for the "foo" option. + */ + public function testGetNormalizerThrowsOnNoConfiguredValue() + { + $resolver = new OptionsResolver(); + $resolver->setDefined($option = 'foo'); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getNormalizer($option)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. + */ + public function testGetNormalizerThrowsOnNotDefinedOption() + { + $resolver = new OptionsResolver(); + + $debug = new OptionsResolverIntrospector($resolver); + $this->assertSame('bar', $debug->getNormalizer('foo')); + } +} diff --git a/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php new file mode 100644 index 0000000..449a53f --- /dev/null +++ b/vendor/symfony/options-resolver/Tests/OptionsResolverTest.php @@ -0,0 +1,1736 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\OptionsResolver\Tests; + +use PHPUnit\Framework\Assert; +use PHPUnit\Framework\TestCase; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Options; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class OptionsResolverTest extends TestCase +{ + /** + * @var OptionsResolver + */ + private $resolver; + + protected function setUp() + { + $this->resolver = new OptionsResolver(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The option "foo" does not exist. Defined options are: "a", "z". + */ + public function testResolveFailsIfNonExistingOption() + { + $this->resolver->setDefault('z', '1'); + $this->resolver->setDefault('a', '2'); + + $this->resolver->resolve(array('foo' => 'bar')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + * @expectedExceptionMessage The options "baz", "foo", "ping" do not exist. Defined options are: "a", "z". + */ + public function testResolveFailsIfMultipleNonExistingOptions() + { + $this->resolver->setDefault('z', '1'); + $this->resolver->setDefault('a', '2'); + + $this->resolver->resolve(array('ping' => 'pong', 'foo' => 'bar', 'baz' => 'bam')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testResolveFailsFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->resolve(array()); + }); + + $this->resolver->resolve(); + } + + public function testSetDefaultReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setDefault('foo', 'bar')); + } + + public function testSetDefault() + { + $this->resolver->setDefault('one', '1'); + $this->resolver->setDefault('two', '20'); + + $this->assertEquals(array( + 'one' => '1', + 'two' => '20', + ), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetDefaultFromLazyOption() + { + $this->resolver->setDefault('lazy', function (Options $options) { + $options->setDefault('default', 42); + }); + + $this->resolver->resolve(); + } + + public function testHasDefault() + { + $this->assertFalse($this->resolver->hasDefault('foo')); + $this->resolver->setDefault('foo', 42); + $this->assertTrue($this->resolver->hasDefault('foo')); + } + + public function testHasDefaultWithNullValue() + { + $this->assertFalse($this->resolver->hasDefault('foo')); + $this->resolver->setDefault('foo', null); + $this->assertTrue($this->resolver->hasDefault('foo')); + } + + public function testSetLazyReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setDefault('foo', function (Options $options) {})); + } + + public function testSetLazyClosure() + { + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + + $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve()); + } + + public function testClosureWithoutTypeHintNotInvoked() + { + $closure = function ($options) { + Assert::fail('Should not be called'); + }; + + $this->resolver->setDefault('foo', $closure); + + $this->assertSame(array('foo' => $closure), $this->resolver->resolve()); + } + + public function testClosureWithoutParametersNotInvoked() + { + $closure = function () { + Assert::fail('Should not be called'); + }; + + $this->resolver->setDefault('foo', $closure); + + $this->assertSame(array('foo' => $closure), $this->resolver->resolve()); + } + + public function testAccessPreviousDefaultValue() + { + // defined by superclass + $this->resolver->setDefault('foo', 'bar'); + + // defined by subclass + $this->resolver->setDefault('foo', function (Options $options, $previousValue) { + Assert::assertEquals('bar', $previousValue); + + return 'lazy'; + }); + + $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve()); + } + + public function testAccessPreviousLazyDefaultValue() + { + // defined by superclass + $this->resolver->setDefault('foo', function (Options $options) { + return 'bar'; + }); + + // defined by subclass + $this->resolver->setDefault('foo', function (Options $options, $previousValue) { + Assert::assertEquals('bar', $previousValue); + + return 'lazy'; + }); + + $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve()); + } + + public function testPreviousValueIsNotEvaluatedIfNoSecondArgument() + { + // defined by superclass + $this->resolver->setDefault('foo', function () { + Assert::fail('Should not be called'); + }); + + // defined by subclass, no $previousValue argument defined! + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + + $this->assertEquals(array('foo' => 'lazy'), $this->resolver->resolve()); + } + + public function testOverwrittenLazyOptionNotEvaluated() + { + $this->resolver->setDefault('foo', function (Options $options) { + Assert::fail('Should not be called'); + }); + + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testInvokeEachLazyOptionOnlyOnce() + { + $calls = 0; + + $this->resolver->setDefault('lazy1', function (Options $options) use (&$calls) { + Assert::assertSame(1, ++$calls); + + $options['lazy2']; + }); + + $this->resolver->setDefault('lazy2', function (Options $options) use (&$calls) { + Assert::assertSame(2, ++$calls); + }); + + $this->resolver->resolve(); + + $this->assertSame(2, $calls); + } + + public function testSetRequiredReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setRequired('foo')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetRequiredFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->setRequired('bar'); + }); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException + */ + public function testResolveFailsIfRequiredOptionMissing() + { + $this->resolver->setRequired('foo'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfRequiredOptionSet() + { + $this->resolver->setRequired('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveSucceedsIfRequiredOptionPassed() + { + $this->resolver->setRequired('foo'); + + $this->assertNotEmpty($this->resolver->resolve(array('foo' => 'bar'))); + } + + public function testIsRequired() + { + $this->assertFalse($this->resolver->isRequired('foo')); + $this->resolver->setRequired('foo'); + $this->assertTrue($this->resolver->isRequired('foo')); + } + + public function testRequiredIfSetBefore() + { + $this->assertFalse($this->resolver->isRequired('foo')); + + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setRequired('foo'); + + $this->assertTrue($this->resolver->isRequired('foo')); + } + + public function testStillRequiredAfterSet() + { + $this->assertFalse($this->resolver->isRequired('foo')); + + $this->resolver->setRequired('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertTrue($this->resolver->isRequired('foo')); + } + + public function testIsNotRequiredAfterRemove() + { + $this->assertFalse($this->resolver->isRequired('foo')); + $this->resolver->setRequired('foo'); + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isRequired('foo')); + } + + public function testIsNotRequiredAfterClear() + { + $this->assertFalse($this->resolver->isRequired('foo')); + $this->resolver->setRequired('foo'); + $this->resolver->clear(); + $this->assertFalse($this->resolver->isRequired('foo')); + } + + public function testGetRequiredOptions() + { + $this->resolver->setRequired(array('foo', 'bar')); + $this->resolver->setDefault('bam', 'baz'); + $this->resolver->setDefault('foo', 'boo'); + + $this->assertSame(array('foo', 'bar'), $this->resolver->getRequiredOptions()); + } + + public function testIsMissingIfNotSet() + { + $this->assertFalse($this->resolver->isMissing('foo')); + $this->resolver->setRequired('foo'); + $this->assertTrue($this->resolver->isMissing('foo')); + } + + public function testIsNotMissingIfSet() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->assertFalse($this->resolver->isMissing('foo')); + $this->resolver->setRequired('foo'); + $this->assertFalse($this->resolver->isMissing('foo')); + } + + public function testIsNotMissingAfterRemove() + { + $this->resolver->setRequired('foo'); + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isMissing('foo')); + } + + public function testIsNotMissingAfterClear() + { + $this->resolver->setRequired('foo'); + $this->resolver->clear(); + $this->assertFalse($this->resolver->isRequired('foo')); + } + + public function testGetMissingOptions() + { + $this->resolver->setRequired(array('foo', 'bar')); + $this->resolver->setDefault('bam', 'baz'); + $this->resolver->setDefault('foo', 'boo'); + + $this->assertSame(array('bar'), $this->resolver->getMissingOptions()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetDefinedFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->setDefined('bar'); + }); + + $this->resolver->resolve(); + } + + public function testDefinedOptionsNotIncludedInResolvedOptions() + { + $this->resolver->setDefined('foo'); + + $this->assertSame(array(), $this->resolver->resolve()); + } + + public function testDefinedOptionsIncludedIfDefaultSetBefore() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setDefined('foo'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testDefinedOptionsIncludedIfDefaultSetAfter() + { + $this->resolver->setDefined('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testDefinedOptionsIncludedIfPassedToResolve() + { + $this->resolver->setDefined('foo'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve(array('foo' => 'bar'))); + } + + public function testIsDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefined('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testLazyOptionsAreDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefault('foo', function (Options $options) {}); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testRequiredOptionsAreDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setRequired('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testSetOptionsAreDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefault('foo', 'bar'); + $this->assertTrue($this->resolver->isDefined('foo')); + } + + public function testGetDefinedOptions() + { + $this->resolver->setDefined(array('foo', 'bar')); + $this->resolver->setDefault('baz', 'bam'); + $this->resolver->setRequired('boo'); + + $this->assertSame(array('foo', 'bar', 'baz', 'boo'), $this->resolver->getDefinedOptions()); + } + + public function testRemovedOptionsAreNotDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefined('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + $this->resolver->remove('foo'); + $this->assertFalse($this->resolver->isDefined('foo')); + } + + public function testClearedOptionsAreNotDefined() + { + $this->assertFalse($this->resolver->isDefined('foo')); + $this->resolver->setDefined('foo'); + $this->assertTrue($this->resolver->isDefined('foo')); + $this->resolver->clear(); + $this->assertFalse($this->resolver->isDefined('foo')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + */ + public function testSetAllowedTypesFailsIfUnknownOption() + { + $this->resolver->setAllowedTypes('foo', 'string'); + } + + public function testResolveTypedArray() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $options = $this->resolver->resolve(array('foo' => array('bar', 'baz'))); + + $this->assertSame(array('foo' => array('bar', 'baz')), $options); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetAllowedTypesFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->setAllowedTypes('bar', 'string'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]". + */ + public function testResolveFailsIfInvalidTypedArray() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + + $this->resolver->resolve(array('foo' => array(new \DateTime()))); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value "bar" is expected to be of type "int[]", but is of type "string". + */ + public function testResolveFailsWithNonArray() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + + $this->resolver->resolve(array('foo' => 'bar')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]". + */ + public function testResolveFailsIfTypedArrayContainsInvalidTypes() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + $values = range(1, 5); + $values[] = new \stdClass(); + $values[] = array(); + $values[] = new \DateTime(); + $values[] = 123; + + $this->resolver->resolve(array('foo' => $values)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]". + */ + public function testResolveFailsWithCorrectLevelsButWrongScalar() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array(1.2), + ), + ) + ); + } + + /** + * @dataProvider provideInvalidTypes + */ + public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage) + { + $this->resolver->setDefined('option'); + $this->resolver->setAllowedTypes('option', $allowedType); + + if (method_exists($this, 'expectException')) { + $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); + $this->expectExceptionMessage($exceptionMessage); + } else { + $this->setExpectedException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException', $exceptionMessage); + } + + $this->resolver->resolve(array('option' => $actualType)); + } + + public function provideInvalidTypes() + { + return array( + array(true, 'string', 'The option "option" with value true is expected to be of type "string", but is of type "boolean".'), + array(false, 'string', 'The option "option" with value false is expected to be of type "string", but is of type "boolean".'), + array(fopen(__FILE__, 'r'), 'string', 'The option "option" with value resource is expected to be of type "string", but is of type "resource".'), + array(array(), 'string', 'The option "option" with value array is expected to be of type "string", but is of type "array".'), + array(new OptionsResolver(), 'string', 'The option "option" with value Symfony\Component\OptionsResolver\OptionsResolver is expected to be of type "string", but is of type "Symfony\Component\OptionsResolver\OptionsResolver".'), + array(42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'), + array(null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'), + array('bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'), + ); + } + + public function testResolveSucceedsIfValidType() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'string'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string" or "bool", but is of type "integer". + */ + public function testResolveFailsIfInvalidTypeMultiple() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedTypes('foo', array('string', 'bool')); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidTypeMultiple() + { + $this->resolver->setDefault('foo', true); + $this->resolver->setAllowedTypes('foo', array('string', 'bool')); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveSucceedsIfInstanceOfClass() + { + $this->resolver->setDefault('foo', new \stdClass()); + $this->resolver->setAllowedTypes('foo', '\stdClass'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testResolveSucceedsIfTypedArray() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedTypes('foo', array('null', 'DateTime[]')); + + $data = array( + 'foo' => array( + new \DateTime(), + new \DateTime(), + ), + ); + $result = $this->resolver->resolve($data); + $this->assertEquals($data, $result); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfNotInstanceOfClass() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', '\stdClass'); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + */ + public function testAddAllowedTypesFailsIfUnknownOption() + { + $this->resolver->addAllowedTypes('foo', 'string'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfAddAllowedTypesFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->addAllowedTypes('bar', 'string'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfInvalidAddedType() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedTypes('foo', 'string'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedType() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedTypes('foo', 'string'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfInvalidAddedTypeMultiple() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedTypes('foo', array('string', 'bool')); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedTypeMultiple() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedTypes('foo', array('string', 'bool')); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testAddAllowedTypesDoesNotOverwrite() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'string'); + $this->resolver->addAllowedTypes('foo', 'bool'); + + $this->resolver->setDefault('foo', 'bar'); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + public function testAddAllowedTypesDoesNotOverwrite2() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'string'); + $this->resolver->addAllowedTypes('foo', 'bool'); + + $this->resolver->setDefault('foo', false); + + $this->assertNotEmpty($this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + */ + public function testSetAllowedValuesFailsIfUnknownOption() + { + $this->resolver->setAllowedValues('foo', 'bar'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetAllowedValuesFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->setAllowedValues('bar', 'baz'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar". + */ + public function testResolveFailsIfInvalidValue() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedValues('foo', 'bar'); + + $this->resolver->resolve(array('foo' => 42)); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value null is invalid. Accepted values are: "bar". + */ + public function testResolveFailsIfInvalidValueIsNull() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedValues('foo', 'bar'); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfInvalidValueStrict() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', '42'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidValue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', 'bar'); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testResolveSucceedsIfValidValueIsNull() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedValues('foo', null); + + $this->assertEquals(array('foo' => null), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value 42 is invalid. Accepted values are: "bar", false, null. + */ + public function testResolveFailsIfInvalidValueMultiple() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', array('bar', false, null)); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidValueMultiple() + { + $this->resolver->setDefault('foo', 'baz'); + $this->resolver->setAllowedValues('foo', array('bar', 'baz')); + + $this->assertEquals(array('foo' => 'baz'), $this->resolver->resolve()); + } + + public function testResolveFailsIfClosureReturnsFalse() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) { + $passedValue = $value; + + return false; + }); + + try { + $this->resolver->resolve(); + $this->fail('Should fail'); + } catch (InvalidOptionsException $e) { + } + + $this->assertSame(42, $passedValue); + } + + public function testResolveSucceedsIfClosureReturnsTrue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', function ($value) use (&$passedValue) { + $passedValue = $value; + + return true; + }); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + $this->assertSame('bar', $passedValue); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfAllClosuresReturnFalse() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', array( + function () { return false; }, + function () { return false; }, + function () { return false; }, + )); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfAnyClosureReturnsTrue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', array( + function () { return false; }, + function () { return true; }, + function () { return false; }, + )); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + */ + public function testAddAllowedValuesFailsIfUnknownOption() + { + $this->resolver->addAllowedValues('foo', 'bar'); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfAddAllowedValuesFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->addAllowedValues('bar', 'baz'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfInvalidAddedValue() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedValues('foo', 'bar'); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedValue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedValues('foo', 'bar'); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testResolveSucceedsIfValidAddedValueIsNull() + { + $this->resolver->setDefault('foo', null); + $this->resolver->addAllowedValues('foo', null); + + $this->assertEquals(array('foo' => null), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfInvalidAddedValueMultiple() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->addAllowedValues('foo', array('bar', 'baz')); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfValidAddedValueMultiple() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->addAllowedValues('foo', array('bar', 'baz')); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testAddAllowedValuesDoesNotOverwrite() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', 'bar'); + $this->resolver->addAllowedValues('foo', 'baz'); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testAddAllowedValuesDoesNotOverwrite2() + { + $this->resolver->setDefault('foo', 'baz'); + $this->resolver->setAllowedValues('foo', 'bar'); + $this->resolver->addAllowedValues('foo', 'baz'); + + $this->assertEquals(array('foo' => 'baz'), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfAllAddedClosuresReturnFalse() + { + $this->resolver->setDefault('foo', 42); + $this->resolver->setAllowedValues('foo', function () { return false; }); + $this->resolver->addAllowedValues('foo', function () { return false; }); + + $this->resolver->resolve(); + } + + public function testResolveSucceedsIfAnyAddedClosureReturnsTrue() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', function () { return false; }); + $this->resolver->addAllowedValues('foo', function () { return true; }); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testResolveSucceedsIfAnyAddedClosureReturnsTrue2() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', function () { return true; }); + $this->resolver->addAllowedValues('foo', function () { return false; }); + + $this->assertEquals(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testSetNormalizerReturnsThis() + { + $this->resolver->setDefault('foo', 'bar'); + $this->assertSame($this->resolver, $this->resolver->setNormalizer('foo', function () {})); + } + + public function testSetNormalizerClosure() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function () { + return 'normalized'; + }); + + $this->assertEquals(array('foo' => 'normalized'), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException + */ + public function testSetNormalizerFailsIfUnknownOption() + { + $this->resolver->setNormalizer('foo', function () {}); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetNormalizerFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->setNormalizer('foo', function () {}); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testNormalizerReceivesSetOption() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized['.$value.']'; + }); + + $this->assertEquals(array('foo' => 'normalized[bar]'), $this->resolver->resolve()); + } + + public function testNormalizerReceivesPassedOption() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized['.$value.']'; + }); + + $resolved = $this->resolver->resolve(array('foo' => 'baz')); + + $this->assertEquals(array('foo' => 'normalized[baz]'), $resolved); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testValidateTypeBeforeNormalization() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setAllowedTypes('foo', 'int'); + + $this->resolver->setNormalizer('foo', function () { + Assert::fail('Should not be called.'); + }); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testValidateValueBeforeNormalization() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setAllowedValues('foo', 'baz'); + + $this->resolver->setNormalizer('foo', function () { + Assert::fail('Should not be called.'); + }); + + $this->resolver->resolve(); + } + + public function testNormalizerCanAccessOtherOptions() + { + $this->resolver->setDefault('default', 'bar'); + $this->resolver->setDefault('norm', 'baz'); + + $this->resolver->setNormalizer('norm', function (Options $options) { + /* @var TestCase $test */ + Assert::assertSame('bar', $options['default']); + + return 'normalized'; + }); + + $this->assertEquals(array( + 'default' => 'bar', + 'norm' => 'normalized', + ), $this->resolver->resolve()); + } + + public function testNormalizerCanAccessLazyOptions() + { + $this->resolver->setDefault('lazy', function (Options $options) { + return 'bar'; + }); + $this->resolver->setDefault('norm', 'baz'); + + $this->resolver->setNormalizer('norm', function (Options $options) { + /* @var TestCase $test */ + Assert::assertEquals('bar', $options['lazy']); + + return 'normalized'; + }); + + $this->assertEquals(array( + 'lazy' => 'bar', + 'norm' => 'normalized', + ), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException + */ + public function testFailIfCyclicDependencyBetweenNormalizers() + { + $this->resolver->setDefault('norm1', 'bar'); + $this->resolver->setDefault('norm2', 'baz'); + + $this->resolver->setNormalizer('norm1', function (Options $options) { + $options['norm2']; + }); + + $this->resolver->setNormalizer('norm2', function (Options $options) { + $options['norm1']; + }); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException + */ + public function testFailIfCyclicDependencyBetweenNormalizerAndLazyOption() + { + $this->resolver->setDefault('lazy', function (Options $options) { + $options['norm']; + }); + + $this->resolver->setDefault('norm', 'baz'); + + $this->resolver->setNormalizer('norm', function (Options $options) { + $options['lazy']; + }); + + $this->resolver->resolve(); + } + + public function testCaughtExceptionFromNormalizerDoesNotCrashOptionResolver() + { + $throw = true; + + $this->resolver->setDefaults(array('catcher' => null, 'thrower' => null)); + + $this->resolver->setNormalizer('catcher', function (Options $options) { + try { + return $options['thrower']; + } catch (\Exception $e) { + return false; + } + }); + + $this->resolver->setNormalizer('thrower', function () use (&$throw) { + if ($throw) { + $throw = false; + throw new \UnexpectedValueException('throwing'); + } + + return true; + }); + + $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve()); + } + + public function testCaughtExceptionFromLazyDoesNotCrashOptionResolver() + { + $throw = true; + + $this->resolver->setDefault('catcher', function (Options $options) { + try { + return $options['thrower']; + } catch (\Exception $e) { + return false; + } + }); + + $this->resolver->setDefault('thrower', function (Options $options) use (&$throw) { + if ($throw) { + $throw = false; + throw new \UnexpectedValueException('throwing'); + } + + return true; + }); + + $this->assertSame(array('catcher' => false, 'thrower' => true), $this->resolver->resolve()); + } + + public function testInvokeEachNormalizerOnlyOnce() + { + $calls = 0; + + $this->resolver->setDefault('norm1', 'bar'); + $this->resolver->setDefault('norm2', 'baz'); + + $this->resolver->setNormalizer('norm1', function ($options) use (&$calls) { + Assert::assertSame(1, ++$calls); + + $options['norm2']; + }); + $this->resolver->setNormalizer('norm2', function () use (&$calls) { + Assert::assertSame(2, ++$calls); + }); + + $this->resolver->resolve(); + + $this->assertSame(2, $calls); + } + + public function testNormalizerNotCalledForUnsetOptions() + { + $this->resolver->setDefined('norm'); + + $this->resolver->setNormalizer('norm', function () { + Assert::fail('Should not be called.'); + }); + + $this->assertEmpty($this->resolver->resolve()); + } + + public function testSetDefaultsReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->setDefaults(array('foo', 'bar'))); + } + + public function testSetDefaults() + { + $this->resolver->setDefault('one', '1'); + $this->resolver->setDefault('two', 'bar'); + + $this->resolver->setDefaults(array( + 'two' => '2', + 'three' => '3', + )); + + $this->assertEquals(array( + 'one' => '1', + 'two' => '2', + 'three' => '3', + ), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfSetDefaultsFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->setDefaults(array('two' => '2')); + }); + + $this->resolver->resolve(); + } + + public function testRemoveReturnsThis() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame($this->resolver, $this->resolver->remove('foo')); + } + + public function testRemoveSingleOption() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setDefault('baz', 'boo'); + $this->resolver->remove('foo'); + + $this->assertSame(array('baz' => 'boo'), $this->resolver->resolve()); + } + + public function testRemoveMultipleOptions() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setDefault('baz', 'boo'); + $this->resolver->setDefault('doo', 'dam'); + + $this->resolver->remove(array('foo', 'doo')); + + $this->assertSame(array('baz' => 'boo'), $this->resolver->resolve()); + } + + public function testRemoveLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + $this->resolver->remove('foo'); + + $this->assertSame(array(), $this->resolver->resolve()); + } + + public function testRemoveNormalizer() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized'; + }); + $this->resolver->remove('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testRemoveAllowedTypes() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'int'); + $this->resolver->remove('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testRemoveAllowedValues() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', array('baz', 'boo')); + $this->resolver->remove('foo'); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfRemoveFromLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->remove('bar'); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testRemoveUnknownOptionIgnored() + { + $this->assertNotNull($this->resolver->remove('foo')); + } + + public function testClearReturnsThis() + { + $this->assertSame($this->resolver, $this->resolver->clear()); + } + + public function testClearRemovesAllOptions() + { + $this->resolver->setDefault('one', 1); + $this->resolver->setDefault('two', 2); + + $this->resolver->clear(); + + $this->assertEmpty($this->resolver->resolve()); + } + + public function testClearLazyOption() + { + $this->resolver->setDefault('foo', function (Options $options) { + return 'lazy'; + }); + $this->resolver->clear(); + + $this->assertSame(array(), $this->resolver->resolve()); + } + + public function testClearNormalizer() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setNormalizer('foo', function (Options $options, $value) { + return 'normalized'; + }); + $this->resolver->clear(); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testClearAllowedTypes() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', 'int'); + $this->resolver->clear(); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + public function testClearAllowedValues() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedValues('foo', 'baz'); + $this->resolver->clear(); + $this->resolver->setDefault('foo', 'bar'); + + $this->assertSame(array('foo' => 'bar'), $this->resolver->resolve()); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testFailIfClearFromLazyption() + { + $this->resolver->setDefault('foo', function (Options $options) { + $options->clear(); + }); + + $this->resolver->setDefault('bar', 'baz'); + + $this->resolver->resolve(); + } + + public function testClearOptionAndNormalizer() + { + $this->resolver->setDefault('foo1', 'bar'); + $this->resolver->setNormalizer('foo1', function (Options $options) { + return ''; + }); + $this->resolver->setDefault('foo2', 'bar'); + $this->resolver->setNormalizer('foo2', function (Options $options) { + return ''; + }); + + $this->resolver->clear(); + $this->assertEmpty($this->resolver->resolve()); + } + + public function testArrayAccess() + { + $this->resolver->setDefault('default1', 0); + $this->resolver->setDefault('default2', 1); + $this->resolver->setRequired('required'); + $this->resolver->setDefined('defined'); + $this->resolver->setDefault('lazy1', function (Options $options) { + return 'lazy'; + }); + + $this->resolver->setDefault('lazy2', function (Options $options) { + Assert::assertArrayHasKey('default1', $options); + Assert::assertArrayHasKey('default2', $options); + Assert::assertArrayHasKey('required', $options); + Assert::assertArrayHasKey('lazy1', $options); + Assert::assertArrayHasKey('lazy2', $options); + Assert::assertArrayNotHasKey('defined', $options); + + Assert::assertSame(0, $options['default1']); + Assert::assertSame(42, $options['default2']); + Assert::assertSame('value', $options['required']); + Assert::assertSame('lazy', $options['lazy1']); + + // Obviously $options['lazy'] and $options['defined'] cannot be + // accessed + }); + + $this->resolver->resolve(array('default2' => 42, 'required' => 'value')); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testArrayAccessGetFailsOutsideResolve() + { + $this->resolver->setDefault('default', 0); + + $this->resolver['default']; + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testArrayAccessExistsFailsOutsideResolve() + { + $this->resolver->setDefault('default', 0); + + isset($this->resolver['default']); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testArrayAccessSetNotSupported() + { + $this->resolver['default'] = 0; + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testArrayAccessUnsetNotSupported() + { + $this->resolver->setDefault('default', 0); + + unset($this->resolver['default']); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException + * @expectedExceptionMessage The option "undefined" does not exist. Defined options are: "foo", "lazy". + */ + public function testFailIfGetNonExisting() + { + $this->resolver->setDefault('foo', 'bar'); + + $this->resolver->setDefault('lazy', function (Options $options) { + $options['undefined']; + }); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\NoSuchOptionException + * @expectedExceptionMessage The optional option "defined" has no value set. You should make sure it is set with "isset" before reading it. + */ + public function testFailIfGetDefinedButUnset() + { + $this->resolver->setDefined('defined'); + + $this->resolver->setDefault('lazy', function (Options $options) { + $options['defined']; + }); + + $this->resolver->resolve(); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\OptionDefinitionException + */ + public function testFailIfCyclicDependency() + { + $this->resolver->setDefault('lazy1', function (Options $options) { + $options['lazy2']; + }); + + $this->resolver->setDefault('lazy2', function (Options $options) { + $options['lazy1']; + }); + + $this->resolver->resolve(); + } + + public function testCount() + { + $this->resolver->setDefault('default', 0); + $this->resolver->setRequired('required'); + $this->resolver->setDefined('defined'); + $this->resolver->setDefault('lazy1', function () {}); + + $this->resolver->setDefault('lazy2', function (Options $options) { + Assert::assertCount(4, $options); + }); + + $this->assertCount(4, $this->resolver->resolve(array('required' => 'value'))); + } + + /** + * In resolve() we count the options that are actually set (which may be + * only a subset of the defined options). Outside of resolve(), it's not + * clear what is counted. + * + * @expectedException \Symfony\Component\OptionsResolver\Exception\AccessException + */ + public function testCountFailsOutsideResolve() + { + $this->resolver->setDefault('foo', 0); + $this->resolver->setRequired('bar'); + $this->resolver->setDefined('bar'); + $this->resolver->setDefault('lazy1', function () {}); + + \count($this->resolver); + } + + public function testNestedArrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + 1, 2, + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array(1, 2), + ), + ) + )); + } + + public function testNested2Arrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][][][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + array( + array( + 1, 2, + ), + ), + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]". + */ + public function testNestedArraysException() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'float[][][][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + ); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException1() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(1, true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]". + */ + public function testNestedArrayException2() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + $this->resolver->resolve(array( + 'foo' => array( + array(true, 'str', array(2, 3)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]". + */ + public function testNestedArrayException3() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array('str', array(1, 2)), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]". + */ + public function testNestedArrayException4() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[][][]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]". + */ + public function testNestedArrayException5() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'string[]'); + $this->resolver->resolve(array( + 'foo' => array( + array( + array('str'), array(1, 2), ), + ), + )); + } +} diff --git a/vendor/symfony/options-resolver/composer.json b/vendor/symfony/options-resolver/composer.json new file mode 100644 index 0000000..895847e --- /dev/null +++ b/vendor/symfony/options-resolver/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/options-resolver", + "type": "library", + "description": "Symfony OptionsResolver Component", + "keywords": ["options", "config", "configuration"], + "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" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\OptionsResolver\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/vendor/symfony/options-resolver/phpunit.xml.dist b/vendor/symfony/options-resolver/phpunit.xml.dist new file mode 100644 index 0000000..7e04e60 --- /dev/null +++ b/vendor/symfony/options-resolver/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/polyfill-ctype/Ctype.php b/vendor/symfony/polyfill-ctype/Ctype.php new file mode 100644 index 0000000..58414dc --- /dev/null +++ b/vendor/symfony/polyfill-ctype/Ctype.php @@ -0,0 +1,227 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Polyfill\Ctype; + +/** + * Ctype implementation through regex. + * + * @internal + * + * @author Gert de Pagter + */ +final class Ctype +{ + /** + * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise. + * + * @see https://php.net/ctype-alnum + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_alnum($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is a letter, FALSE otherwise. + * + * @see https://php.net/ctype-alpha + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_alpha($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text); + } + + /** + * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise. + * + * @see https://php.net/ctype-cntrl + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_cntrl($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text); + } + + /** + * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise. + * + * @see https://php.net/ctype-digit + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_digit($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text); + } + + /** + * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise. + * + * @see https://php.net/ctype-graph + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_graph($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text); + } + + /** + * Returns TRUE if every character in text is a lowercase letter. + * + * @see https://php.net/ctype-lower + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_lower($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text); + } + + /** + * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all. + * + * @see https://php.net/ctype-print + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_print($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text); + } + + /** + * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise. + * + * @see https://php.net/ctype-punct + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_punct($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text); + } + + /** + * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters. + * + * @see https://php.net/ctype-space + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_space($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text); + } + + /** + * Returns TRUE if every character in text is an uppercase letter. + * + * @see https://php.net/ctype-upper + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_upper($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text); + } + + /** + * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise. + * + * @see https://php.net/ctype-xdigit + * + * @param string|int $text + * + * @return bool + */ + public static function ctype_xdigit($text) + { + $text = self::convert_int_to_char_for_ctype($text); + + return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text); + } + + /** + * Converts integers to their char versions according to normal ctype behaviour, if needed. + * + * If an integer between -128 and 255 inclusive is provided, + * it is interpreted as the ASCII value of a single character + * (negative values have 256 added in order to allow characters in the Extended ASCII range). + * Any other integer is interpreted as a string containing the decimal digits of the integer. + * + * @param string|int $int + * + * @return mixed + */ + private static function convert_int_to_char_for_ctype($int) + { + if (!\is_int($int)) { + return $int; + } + + if ($int < -128 || $int > 255) { + return (string) $int; + } + + if ($int < 0) { + $int += 256; + } + + return \chr($int); + } +} diff --git a/vendor/symfony/polyfill-ctype/LICENSE b/vendor/symfony/polyfill-ctype/LICENSE new file mode 100644 index 0000000..ad399a7 --- /dev/null +++ b/vendor/symfony/polyfill-ctype/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 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/vendor/symfony/polyfill-ctype/README.md b/vendor/symfony/polyfill-ctype/README.md new file mode 100644 index 0000000..8add1ab --- /dev/null +++ b/vendor/symfony/polyfill-ctype/README.md @@ -0,0 +1,12 @@ +Symfony Polyfill / Ctype +======================== + +This component provides `ctype_*` functions to users who run php versions without the ctype extension. + +More information can be found in the +[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). + +License +======= + +This library is released under the [MIT license](LICENSE). diff --git a/vendor/symfony/polyfill-ctype/bootstrap.php b/vendor/symfony/polyfill-ctype/bootstrap.php new file mode 100644 index 0000000..14d1d0f --- /dev/null +++ b/vendor/symfony/polyfill-ctype/bootstrap.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Ctype as p; + +if (!function_exists('ctype_alnum')) { + function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); } + function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); } + function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); } + function ctype_digit($text) { return p\Ctype::ctype_digit($text); } + function ctype_graph($text) { return p\Ctype::ctype_graph($text); } + function ctype_lower($text) { return p\Ctype::ctype_lower($text); } + function ctype_print($text) { return p\Ctype::ctype_print($text); } + function ctype_punct($text) { return p\Ctype::ctype_punct($text); } + function ctype_space($text) { return p\Ctype::ctype_space($text); } + function ctype_upper($text) { return p\Ctype::ctype_upper($text); } + function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); } +} diff --git a/vendor/symfony/polyfill-ctype/composer.json b/vendor/symfony/polyfill-ctype/composer.json new file mode 100644 index 0000000..094f8d8 --- /dev/null +++ b/vendor/symfony/polyfill-ctype/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/polyfill-ctype", + "type": "library", + "description": "Symfony polyfill for ctype functions", + "keywords": ["polyfill", "compatibility", "portable", "ctype"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" }, + "files": [ "bootstrap.php" ] + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + } +} diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE new file mode 100644 index 0000000..24fa32c --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-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/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php new file mode 100644 index 0000000..1f568b4 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -0,0 +1,789 @@ + + * + * 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_decode_numericentity - Decode HTML numeric string reference to character + * - mb_encode_numericentity - Encode character to HTML numeric string reference + * - mb_convert_case - Perform case folding on a string + * - mb_detect_encoding - Detect character encoding + * - 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 another + * - 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_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_decode_numericentity($s, $convmap, $encoding = null) + { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING); + return null; + } + + if (!\is_array($convmap) || !$convmap) { + return false; + } + + if (null !== $encoding && !\is_scalar($encoding)) { + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING); + return ''; // Instead of null (cf. mb_encode_numericentity). + } + + $s = (string) $s; + 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); + } + + $cnt = floor(\count($convmap) / 4) * 4; + + for ($i = 0; $i < $cnt; $i += 4) { + // collector_decode_htmlnumericentity ignores $convmap[$i + 3] + $convmap[$i] += $convmap[$i + 2]; + $convmap[$i + 1] += $convmap[$i + 2]; + } + + $s = preg_replace_callback('/&#(?:0*([0-9]+)|x0*([0-9a-fA-F]+))(?!&);?/', function (array $m) use ($cnt, $convmap) { + $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; + for ($i = 0; $i < $cnt; $i += 4) { + if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { + return Mbstring::mb_chr($c - $convmap[$i + 2]); + } + } + return $m[0]; + }, $s); + + if (null === $encoding) { + return $s; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $s); + } + + public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) + { + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.gettype($s).' given', E_USER_WARNING); + return null; + } + + if (!\is_array($convmap) || !$convmap) { + return false; + } + + if (null !== $encoding && !\is_scalar($encoding)) { + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.gettype($s).' given', E_USER_WARNING); + return null; // Instead of '' (cf. mb_decode_numericentity). + } + + if (null !== $is_hex && !\is_scalar($is_hex)) { + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.gettype($s).' given', E_USER_WARNING); + return null; + } + + $s = (string) $s; + 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); + } + + static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + + $cnt = floor(\count($convmap) / 4) * 4; + $i = 0; + $len = \strlen($s); + $result = ''; + + while ($i < $len) { + $ulen = $s[$i] < "\x80" ? 1 : $ulenMask[$s[$i] & "\xF0"]; + $uchr = substr($s, $i, $ulen); + $i += $ulen; + $c = self::mb_ord($uchr); + + for ($j = 0; $j < $cnt; $j += 4) { + if ($c >= $convmap[$j] && $c <= $convmap[$j + 1]) { + $cOffset = ($c + $convmap[$j + 2]) & $convmap[$j + 3]; + $result .= $is_hex ? sprintf('&#x%X;', $cOffset) : '&#'.$cOffset.';'; + continue 2; + } + } + $result .= $uchr; + } + + if (null === $encoding) { + return $result; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $result); + } + + public static function mb_convert_case($s, $mode, $encoding = null) + { + $s = (string) $s; + 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) { + static $titleRegexp = null; + if (null === $titleRegexp) { + $titleRegexp = self::getData('titleCaseRegexp'); + } + $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $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); + } + + $needle = (string) $needle; + 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 (string) 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(array $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(array $s) + { + return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, '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/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md new file mode 100644 index 0000000..342e828 --- /dev/null +++ b/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/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php new file mode 100644 index 0000000..3ca1641 --- /dev/null +++ b/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/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php new file mode 100644 index 0000000..2a8f6e7 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php @@ -0,0 +1,5 @@ + '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/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php new file mode 100644 index 0000000..2fdcc5a --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -0,0 +1,58 @@ + + * + * 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_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); } + function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); } + 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/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json new file mode 100644 index 0000000..50ea12f --- /dev/null +++ b/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.9-dev" + } + } +} diff --git a/vendor/symfony/polyfill-php70/LICENSE b/vendor/symfony/polyfill-php70/LICENSE new file mode 100644 index 0000000..24fa32c --- /dev/null +++ b/vendor/symfony/polyfill-php70/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015-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/vendor/symfony/polyfill-php70/Php70.php b/vendor/symfony/polyfill-php70/Php70.php new file mode 100644 index 0000000..39e6645 --- /dev/null +++ b/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 = (string) $subject; + if (0 === $limit = self::intArg($limit, __FUNCTION__, 3)) { + return $result; + } + + foreach ($patterns as $pattern => $callback) { + $result = preg_replace_callback($pattern, $callback, $result, $limit, $c); + $count += $c; + } + + return $result; + } + + public static function error_clear_last() + { + static $handler; + if (!$handler) { + $handler = function() { return false; }; + } + set_error_handler($handler); + @trigger_error(''); + restore_error_handler(); + } + + private static function intArg($value, $caller, $pos) + { + if (\is_int($value)) { + return $value; + } + if (!\is_numeric($value) || PHP_INT_MAX <= ($value += 0) || ~PHP_INT_MAX >= $value) { + throw new \TypeError(sprintf('%s() expects parameter %d to be integer, %s given', $caller, $pos, gettype($value))); + } + + return (int) $value; + } +} diff --git a/vendor/symfony/polyfill-php70/README.md b/vendor/symfony/polyfill-php70/README.md new file mode 100644 index 0000000..04988c6 --- /dev/null +++ b/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/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php b/vendor/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php new file mode 100644 index 0000000..6819124 --- /dev/null +++ b/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/vendor/symfony/polyfill-php70/composer.json b/vendor/symfony/polyfill-php70/composer.json new file mode 100644 index 0000000..13dcf94 --- /dev/null +++ b/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|~9.99" + }, + "autoload": { + "psr-4": { "Symfony\\Polyfill\\Php70\\": "" }, + "files": [ "bootstrap.php" ], + "classmap": [ "Resources/stubs" ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "1.9-dev" + } + } +} diff --git a/vendor/symfony/property-access/.gitignore b/vendor/symfony/property-access/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/property-access/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/property-access/CHANGELOG.md b/vendor/symfony/property-access/CHANGELOG.md new file mode 100644 index 0000000..416287e --- /dev/null +++ b/vendor/symfony/property-access/CHANGELOG.md @@ -0,0 +1,35 @@ +CHANGELOG +========= + +3.1.0 +----- + + * deprecated the `StringUtil` class, use `Symfony\Component\Inflector\Inflector` + instead + +2.7.0 +------ + + * `UnexpectedTypeException` now expects three constructor arguments: The invalid property value, + the `PropertyPathInterface` object and the current index of the property path. + +2.5.0 +------ + + * allowed non alpha numeric characters in second level and deeper object properties names + * [BC BREAK] when accessing an index on an object that does not implement + ArrayAccess, a NoSuchIndexException is now thrown instead of the + semantically wrong NoSuchPropertyException + * [BC BREAK] added isReadable() and isWritable() to PropertyAccessorInterface + +2.3.0 +------ + + * added PropertyAccessorBuilder, to enable or disable the support of "__call" + * added support for "__call" in the PropertyAccessor (disabled by default) + * [BC BREAK] changed PropertyAccessor to continue its search for a property or + method even if a non-public match was found. Before, a PropertyAccessDeniedException + was thrown in this case. Class PropertyAccessDeniedException was removed + now. + * deprecated PropertyAccess::getPropertyAccessor + * added PropertyAccess::createPropertyAccessor and PropertyAccess::createPropertyAccessorBuilder diff --git a/vendor/symfony/property-access/Exception/AccessException.php b/vendor/symfony/property-access/Exception/AccessException.php new file mode 100644 index 0000000..b3a8546 --- /dev/null +++ b/vendor/symfony/property-access/Exception/AccessException.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\PropertyAccess\Exception; + +/** + * Thrown when a property path is not available. + * + * @author Stéphane Escandell + */ +class AccessException extends RuntimeException +{ +} diff --git a/vendor/symfony/property-access/Exception/ExceptionInterface.php b/vendor/symfony/property-access/Exception/ExceptionInterface.php new file mode 100644 index 0000000..d1fcdac --- /dev/null +++ b/vendor/symfony/property-access/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\PropertyAccess\Exception; + +/** + * Marker interface for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +interface ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Exception/InvalidArgumentException.php b/vendor/symfony/property-access/Exception/InvalidArgumentException.php new file mode 100644 index 0000000..47bc7e1 --- /dev/null +++ b/vendor/symfony/property-access/Exception/InvalidArgumentException.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\PropertyAccess\Exception; + +/** + * Base InvalidArgumentException for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Exception/InvalidPropertyPathException.php b/vendor/symfony/property-access/Exception/InvalidPropertyPathException.php new file mode 100644 index 0000000..69de31c --- /dev/null +++ b/vendor/symfony/property-access/Exception/InvalidPropertyPathException.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\PropertyAccess\Exception; + +/** + * Thrown when a property path is malformed. + * + * @author Bernhard Schussek + */ +class InvalidPropertyPathException extends RuntimeException +{ +} diff --git a/vendor/symfony/property-access/Exception/NoSuchIndexException.php b/vendor/symfony/property-access/Exception/NoSuchIndexException.php new file mode 100644 index 0000000..597b990 --- /dev/null +++ b/vendor/symfony/property-access/Exception/NoSuchIndexException.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\PropertyAccess\Exception; + +/** + * Thrown when an index cannot be found. + * + * @author Stéphane Escandell + */ +class NoSuchIndexException extends AccessException +{ +} diff --git a/vendor/symfony/property-access/Exception/NoSuchPropertyException.php b/vendor/symfony/property-access/Exception/NoSuchPropertyException.php new file mode 100644 index 0000000..1c7eda5 --- /dev/null +++ b/vendor/symfony/property-access/Exception/NoSuchPropertyException.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\PropertyAccess\Exception; + +/** + * Thrown when a property cannot be found. + * + * @author Bernhard Schussek + */ +class NoSuchPropertyException extends AccessException +{ +} diff --git a/vendor/symfony/property-access/Exception/OutOfBoundsException.php b/vendor/symfony/property-access/Exception/OutOfBoundsException.php new file mode 100644 index 0000000..a3c4559 --- /dev/null +++ b/vendor/symfony/property-access/Exception/OutOfBoundsException.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\PropertyAccess\Exception; + +/** + * Base OutOfBoundsException for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +class OutOfBoundsException extends \OutOfBoundsException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Exception/RuntimeException.php b/vendor/symfony/property-access/Exception/RuntimeException.php new file mode 100644 index 0000000..9fe843e --- /dev/null +++ b/vendor/symfony/property-access/Exception/RuntimeException.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\PropertyAccess\Exception; + +/** + * Base RuntimeException for the PropertyAccess component. + * + * @author Bernhard Schussek + */ +class RuntimeException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/vendor/symfony/property-access/Exception/UnexpectedTypeException.php b/vendor/symfony/property-access/Exception/UnexpectedTypeException.php new file mode 100644 index 0000000..8d55721 --- /dev/null +++ b/vendor/symfony/property-access/Exception/UnexpectedTypeException.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\PropertyAccess\Exception; + +use Symfony\Component\PropertyAccess\PropertyPathInterface; + +/** + * Thrown when a value does not match an expected type. + * + * @author Bernhard Schussek + */ +class UnexpectedTypeException extends RuntimeException +{ + /** + * @param mixed $value The unexpected value found while traversing property path + * @param PropertyPathInterface $path The property path + * @param int $pathIndex The property path index when the unexpected value was found + */ + public function __construct($value, PropertyPathInterface $path, $pathIndex) + { + $message = sprintf( + 'PropertyAccessor requires a graph of objects or arrays to operate on, '. + 'but it found type "%s" while trying to traverse path "%s" at property "%s".', + \gettype($value), + (string) $path, + $path->getElement($pathIndex) + ); + + parent::__construct($message); + } +} diff --git a/vendor/symfony/property-access/LICENSE b/vendor/symfony/property-access/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/vendor/symfony/property-access/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/vendor/symfony/property-access/PropertyAccess.php b/vendor/symfony/property-access/PropertyAccess.php new file mode 100644 index 0000000..3c8dc1c --- /dev/null +++ b/vendor/symfony/property-access/PropertyAccess.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\PropertyAccess; + +/** + * Entry point of the PropertyAccess component. + * + * @author Bernhard Schussek + */ +final class PropertyAccess +{ + /** + * Creates a property accessor with the default configuration. + * + * @return PropertyAccessor + */ + public static function createPropertyAccessor() + { + return self::createPropertyAccessorBuilder()->getPropertyAccessor(); + } + + /** + * Creates a property accessor builder. + * + * @return PropertyAccessorBuilder + */ + public static function createPropertyAccessorBuilder() + { + return new PropertyAccessorBuilder(); + } + + /** + * This class cannot be instantiated. + */ + private function __construct() + { + } +} diff --git a/vendor/symfony/property-access/PropertyAccessor.php b/vendor/symfony/property-access/PropertyAccessor.php new file mode 100644 index 0000000..33763a7 --- /dev/null +++ b/vendor/symfony/property-access/PropertyAccessor.php @@ -0,0 +1,921 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess; + +use Psr\Cache\CacheItemPoolInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\Cache\Adapter\AdapterInterface; +use Symfony\Component\Cache\Adapter\ApcuAdapter; +use Symfony\Component\Cache\Adapter\NullAdapter; +use Symfony\Component\Inflector\Inflector; +use Symfony\Component\PropertyAccess\Exception\AccessException; +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; + +/** + * Default implementation of {@link PropertyAccessorInterface}. + * + * @author Bernhard Schussek + * @author Kévin Dunglas + * @author Nicolas Grekas + */ +class PropertyAccessor implements PropertyAccessorInterface +{ + /** + * @internal + */ + const VALUE = 0; + + /** + * @internal + */ + const REF = 1; + + /** + * @internal + */ + const IS_REF_CHAINED = 2; + + /** + * @internal + */ + const ACCESS_HAS_PROPERTY = 0; + + /** + * @internal + */ + const ACCESS_TYPE = 1; + + /** + * @internal + */ + const ACCESS_NAME = 2; + + /** + * @internal + */ + const ACCESS_REF = 3; + + /** + * @internal + */ + const ACCESS_ADDER = 4; + + /** + * @internal + */ + const ACCESS_REMOVER = 5; + + /** + * @internal + */ + const ACCESS_TYPE_METHOD = 0; + + /** + * @internal + */ + const ACCESS_TYPE_PROPERTY = 1; + + /** + * @internal + */ + const ACCESS_TYPE_MAGIC = 2; + + /** + * @internal + */ + const ACCESS_TYPE_ADDER_AND_REMOVER = 3; + + /** + * @internal + */ + const ACCESS_TYPE_NOT_FOUND = 4; + + /** + * @internal + */ + const CACHE_PREFIX_READ = 'r'; + + /** + * @internal + */ + const CACHE_PREFIX_WRITE = 'w'; + + /** + * @internal + */ + const CACHE_PREFIX_PROPERTY_PATH = 'p'; + + /** + * @var bool + */ + private $magicCall; + private $ignoreInvalidIndices; + + /** + * @var CacheItemPoolInterface + */ + private $cacheItemPool; + + private $readPropertyCache = array(); + private $writePropertyCache = array(); + private $propertyPathCache = array(); + + private static $previousErrorHandler = false; + private static $errorHandler = array(__CLASS__, 'handleError'); + private static $resultProto = array(self::VALUE => null); + + /** + * Should not be used by application code. Use + * {@link PropertyAccess::createPropertyAccessor()} instead. + * + * @param bool $magicCall + * @param bool $throwExceptionOnInvalidIndex + * @param CacheItemPoolInterface $cacheItemPool + */ + public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null) + { + $this->magicCall = $magicCall; + $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; + $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value + } + + /** + * {@inheritdoc} + */ + public function getValue($objectOrArray, $propertyPath) + { + $propertyPath = $this->getPropertyPath($propertyPath); + + $zval = array( + self::VALUE => $objectOrArray, + ); + $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices); + + return $propertyValues[\count($propertyValues) - 1][self::VALUE]; + } + + /** + * {@inheritdoc} + */ + public function setValue(&$objectOrArray, $propertyPath, $value) + { + $propertyPath = $this->getPropertyPath($propertyPath); + + $zval = array( + self::VALUE => $objectOrArray, + self::REF => &$objectOrArray, + ); + $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1); + $overwrite = true; + + try { + if (\PHP_VERSION_ID < 70000 && false === self::$previousErrorHandler) { + self::$previousErrorHandler = set_error_handler(self::$errorHandler); + } + + for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) { + $zval = $propertyValues[$i]; + unset($propertyValues[$i]); + + // You only need set value for current element if: + // 1. it's the parent of the last index element + // OR + // 2. its child is not passed by reference + // + // This may avoid uncessary value setting process for array elements. + // For example: + // '[a][b][c]' => 'old-value' + // If you want to change its value to 'new-value', + // you only need set value for '[a][b][c]' and it's safe to ignore '[a][b]' and '[a]' + if ($overwrite) { + $property = $propertyPath->getElement($i); + + if ($propertyPath->isIndex($i)) { + if ($overwrite = !isset($zval[self::REF])) { + $ref = &$zval[self::REF]; + $ref = $zval[self::VALUE]; + } + $this->writeIndex($zval, $property, $value); + if ($overwrite) { + $zval[self::VALUE] = $zval[self::REF]; + } + } else { + $this->writeProperty($zval, $property, $value); + } + + // if current element is an object + // OR + // if current element's reference chain is not broken - current element + // as well as all its ancients in the property path are all passed by reference, + // then there is no need to continue the value setting process + if (\is_object($zval[self::VALUE]) || isset($zval[self::IS_REF_CHAINED])) { + break; + } + } + + $value = $zval[self::VALUE]; + } + } catch (\TypeError $e) { + self::throwInvalidArgumentException($e->getMessage(), $e->getTrace(), 0); + + // It wasn't thrown in this class so rethrow it + throw $e; + } finally { + if (\PHP_VERSION_ID < 70000 && false !== self::$previousErrorHandler) { + restore_error_handler(); + self::$previousErrorHandler = false; + } + } + } + + /** + * @internal + */ + public static function handleError($type, $message, $file, $line, $context = array()) + { + if (E_RECOVERABLE_ERROR === $type) { + self::throwInvalidArgumentException($message, debug_backtrace(false), 1); + } + + return null !== self::$previousErrorHandler && false !== \call_user_func(self::$previousErrorHandler, $type, $message, $file, $line, $context); + } + + private static function throwInvalidArgumentException($message, $trace, $i) + { + // the type mismatch is not caused by invalid arguments (but e.g. by an incompatible return type hint of the writer method) + if (0 !== strpos($message, 'Argument ')) { + return; + } + + if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && array_key_exists(0, $trace[$i]['args'])) { + $pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface ')); + $pos += \strlen($delim); + $type = $trace[$i]['args'][0]; + $type = \is_object($type) ? \get_class($type) : \gettype($type); + + throw new InvalidArgumentException(sprintf('Expected argument of type "%s", "%s" given', substr($message, $pos, strpos($message, ',', $pos) - $pos), $type)); + } + } + + /** + * {@inheritdoc} + */ + public function isReadable($objectOrArray, $propertyPath) + { + if (!$propertyPath instanceof PropertyPathInterface) { + $propertyPath = new PropertyPath($propertyPath); + } + + try { + $zval = array( + self::VALUE => $objectOrArray, + ); + $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices); + + return true; + } catch (AccessException $e) { + return false; + } catch (UnexpectedTypeException $e) { + return false; + } + } + + /** + * {@inheritdoc} + */ + public function isWritable($objectOrArray, $propertyPath) + { + $propertyPath = $this->getPropertyPath($propertyPath); + + try { + $zval = array( + self::VALUE => $objectOrArray, + ); + $propertyValues = $this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength() - 1); + + for ($i = \count($propertyValues) - 1; 0 <= $i; --$i) { + $zval = $propertyValues[$i]; + unset($propertyValues[$i]); + + if ($propertyPath->isIndex($i)) { + if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) { + return false; + } + } else { + if (!$this->isPropertyWritable($zval[self::VALUE], $propertyPath->getElement($i))) { + return false; + } + } + + if (\is_object($zval[self::VALUE])) { + return true; + } + } + + return true; + } catch (AccessException $e) { + return false; + } catch (UnexpectedTypeException $e) { + return false; + } + } + + /** + * Reads the path from an object up to a given path index. + * + * @param array $zval The array containing the object or array to read from + * @param PropertyPathInterface $propertyPath The property path to read + * @param int $lastIndex The index up to which should be read + * @param bool $ignoreInvalidIndices Whether to ignore invalid indices or throw an exception + * + * @return array The values read in the path + * + * @throws UnexpectedTypeException if a value within the path is neither object nor array + * @throws NoSuchIndexException If a non-existing index is accessed + */ + private function readPropertiesUntil($zval, PropertyPathInterface $propertyPath, $lastIndex, $ignoreInvalidIndices = true) + { + if (!\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) { + throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, 0); + } + + // Add the root object to the list + $propertyValues = array($zval); + + for ($i = 0; $i < $lastIndex; ++$i) { + $property = $propertyPath->getElement($i); + $isIndex = $propertyPath->isIndex($i); + + if ($isIndex) { + // Create missing nested arrays on demand + if (($zval[self::VALUE] instanceof \ArrayAccess && !$zval[self::VALUE]->offsetExists($property)) || + (\is_array($zval[self::VALUE]) && !isset($zval[self::VALUE][$property]) && !array_key_exists($property, $zval[self::VALUE])) + ) { + if (!$ignoreInvalidIndices) { + if (!\is_array($zval[self::VALUE])) { + if (!$zval[self::VALUE] instanceof \Traversable) { + throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s".', $property, (string) $propertyPath)); + } + + $zval[self::VALUE] = iterator_to_array($zval[self::VALUE]); + } + + throw new NoSuchIndexException(sprintf('Cannot read index "%s" while trying to traverse path "%s". Available indices are "%s".', $property, (string) $propertyPath, print_r(array_keys($zval[self::VALUE]), true))); + } + + if ($i + 1 < $propertyPath->getLength()) { + if (isset($zval[self::REF])) { + $zval[self::VALUE][$property] = array(); + $zval[self::REF] = $zval[self::VALUE]; + } else { + $zval[self::VALUE] = array($property => array()); + } + } + } + + $zval = $this->readIndex($zval, $property); + } else { + $zval = $this->readProperty($zval, $property); + } + + // the final value of the path must not be validated + if ($i + 1 < $propertyPath->getLength() && !\is_object($zval[self::VALUE]) && !\is_array($zval[self::VALUE])) { + throw new UnexpectedTypeException($zval[self::VALUE], $propertyPath, $i + 1); + } + + if (isset($zval[self::REF]) && (0 === $i || isset($propertyValues[$i - 1][self::IS_REF_CHAINED]))) { + // Set the IS_REF_CHAINED flag to true if: + // current property is passed by reference and + // it is the first element in the property path or + // the IS_REF_CHAINED flag of its parent element is true + // Basically, this flag is true only when the reference chain from the top element to current element is not broken + $zval[self::IS_REF_CHAINED] = true; + } + + $propertyValues[] = $zval; + } + + return $propertyValues; + } + + /** + * Reads a key from an array-like structure. + * + * @param array $zval The array containing the array or \ArrayAccess object to read from + * @param string|int $index The key to read + * + * @return array The array containing the value of the key + * + * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array + */ + private function readIndex($zval, $index) + { + if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) { + throw new NoSuchIndexException(sprintf('Cannot read index "%s" from object of type "%s" because it doesn\'t implement \ArrayAccess.', $index, \get_class($zval[self::VALUE]))); + } + + $result = self::$resultProto; + + if (isset($zval[self::VALUE][$index])) { + $result[self::VALUE] = $zval[self::VALUE][$index]; + + if (!isset($zval[self::REF])) { + // Save creating references when doing read-only lookups + } elseif (\is_array($zval[self::VALUE])) { + $result[self::REF] = &$zval[self::REF][$index]; + } elseif (\is_object($result[self::VALUE])) { + $result[self::REF] = $result[self::VALUE]; + } + } + + return $result; + } + + /** + * Reads the a property from an object. + * + * @param array $zval The array containing the object to read from + * @param string $property The property to read + * + * @return array The array containing the value of the property + * + * @throws NoSuchPropertyException if the property does not exist or is not public + */ + private function readProperty($zval, $property) + { + if (!\is_object($zval[self::VALUE])) { + throw new NoSuchPropertyException(sprintf('Cannot read property "%s" from an array. Maybe you intended to write the property path as "[%1$s]" instead.', $property)); + } + + $result = self::$resultProto; + $object = $zval[self::VALUE]; + $access = $this->getReadAccessInfo(\get_class($object), $property); + + if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) { + $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}(); + } elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) { + $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}; + + if ($access[self::ACCESS_REF] && isset($zval[self::REF])) { + $result[self::REF] = &$object->{$access[self::ACCESS_NAME]}; + } + } elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) { + // Needed to support \stdClass instances. We need to explicitly + // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if + // a *protected* property was found on the class, property_exists() + // returns true, consequently the following line will result in a + // fatal error. + + $result[self::VALUE] = $object->$property; + if (isset($zval[self::REF])) { + $result[self::REF] = &$object->$property; + } + } elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) { + // we call the getter and hope the __call do the job + $result[self::VALUE] = $object->{$access[self::ACCESS_NAME]}(); + } else { + throw new NoSuchPropertyException($access[self::ACCESS_NAME]); + } + + // Objects are always passed around by reference + if (isset($zval[self::REF]) && \is_object($result[self::VALUE])) { + $result[self::REF] = $result[self::VALUE]; + } + + return $result; + } + + /** + * Guesses how to read the property value. + * + * @param string $class + * @param string $property + * + * @return array + */ + private function getReadAccessInfo($class, $property) + { + $key = (false !== strpos($class, '@') ? rawurlencode($class) : $class).'..'.$property; + + if (isset($this->readPropertyCache[$key])) { + return $this->readPropertyCache[$key]; + } + + if ($this->cacheItemPool) { + $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_READ.str_replace('\\', '.', $key)); + if ($item->isHit()) { + return $this->readPropertyCache[$key] = $item->get(); + } + } + + $access = array(); + + $reflClass = new \ReflectionClass($class); + $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property); + $camelProp = $this->camelize($property); + $getter = 'get'.$camelProp; + $getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item) + $isser = 'is'.$camelProp; + $hasser = 'has'.$camelProp; + + if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $getter; + } elseif ($reflClass->hasMethod($getsetter) && $reflClass->getMethod($getsetter)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $getsetter; + } elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $isser; + } elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $hasser; + } elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; + $access[self::ACCESS_NAME] = $property; + $access[self::ACCESS_REF] = false; + } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; + $access[self::ACCESS_NAME] = $property; + $access[self::ACCESS_REF] = true; + } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) { + // we call the getter and hope the __call do the job + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; + $access[self::ACCESS_NAME] = $getter; + } else { + $methods = array($getter, $getsetter, $isser, $hasser, '__get'); + if ($this->magicCall) { + $methods[] = '__call'; + } + + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; + $access[self::ACCESS_NAME] = sprintf( + 'Neither the property "%s" nor one of the methods "%s()" '. + 'exist and have public access in class "%s".', + $property, + implode('()", "', $methods), + $reflClass->name + ); + } + + if (isset($item)) { + $this->cacheItemPool->save($item->set($access)); + } + + return $this->readPropertyCache[$key] = $access; + } + + /** + * Sets the value of an index in a given array-accessible value. + * + * @param array $zval The array containing the array or \ArrayAccess object to write to + * @param string|int $index The index to write at + * @param mixed $value The value to write + * + * @throws NoSuchIndexException If the array does not implement \ArrayAccess or it is not an array + */ + private function writeIndex($zval, $index, $value) + { + if (!$zval[self::VALUE] instanceof \ArrayAccess && !\is_array($zval[self::VALUE])) { + throw new NoSuchIndexException(sprintf('Cannot modify index "%s" in object of type "%s" because it doesn\'t implement \ArrayAccess', $index, \get_class($zval[self::VALUE]))); + } + + $zval[self::REF][$index] = $value; + } + + /** + * Sets the value of a property in the given object. + * + * @param array $zval The array containing the object to write to + * @param string $property The property to write + * @param mixed $value The value to write + * + * @throws NoSuchPropertyException if the property does not exist or is not public + */ + private function writeProperty($zval, $property, $value) + { + if (!\is_object($zval[self::VALUE])) { + throw new NoSuchPropertyException(sprintf('Cannot write property "%s" to an array. Maybe you should write the property path as "[%1$s]" instead?', $property)); + } + + $object = $zval[self::VALUE]; + $access = $this->getWriteAccessInfo(\get_class($object), $property, $value); + + if (self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE]) { + $object->{$access[self::ACCESS_NAME]}($value); + } elseif (self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE]) { + $object->{$access[self::ACCESS_NAME]} = $value; + } elseif (self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE]) { + $this->writeCollection($zval, $property, $value, $access[self::ACCESS_ADDER], $access[self::ACCESS_REMOVER]); + } elseif (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) { + // Needed to support \stdClass instances. We need to explicitly + // exclude $access[self::ACCESS_HAS_PROPERTY], otherwise if + // a *protected* property was found on the class, property_exists() + // returns true, consequently the following line will result in a + // fatal error. + + $object->$property = $value; + } elseif (self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]) { + $object->{$access[self::ACCESS_NAME]}($value); + } elseif (self::ACCESS_TYPE_NOT_FOUND === $access[self::ACCESS_TYPE]) { + throw new NoSuchPropertyException(sprintf('Could not determine access type for property "%s" in class "%s".', $property, \get_class($object))); + } else { + throw new NoSuchPropertyException($access[self::ACCESS_NAME]); + } + } + + /** + * Adjusts a collection-valued property by calling add*() and remove*() methods. + * + * @param array $zval The array containing the object to write to + * @param string $property The property to write + * @param iterable $collection The collection to write + * @param string $addMethod The add*() method + * @param string $removeMethod The remove*() method + */ + private function writeCollection($zval, $property, $collection, $addMethod, $removeMethod) + { + // At this point the add and remove methods have been found + $previousValue = $this->readProperty($zval, $property); + $previousValue = $previousValue[self::VALUE]; + + if ($previousValue instanceof \Traversable) { + $previousValue = iterator_to_array($previousValue); + } + if ($previousValue && \is_array($previousValue)) { + if (\is_object($collection)) { + $collection = iterator_to_array($collection); + } + foreach ($previousValue as $key => $item) { + if (!\in_array($item, $collection, true)) { + unset($previousValue[$key]); + $zval[self::VALUE]->{$removeMethod}($item); + } + } + } else { + $previousValue = false; + } + + foreach ($collection as $item) { + if (!$previousValue || !\in_array($item, $previousValue, true)) { + $zval[self::VALUE]->{$addMethod}($item); + } + } + } + + /** + * Guesses how to write the property value. + * + * @param string $class + * @param string $property + * @param mixed $value + * + * @return array + */ + private function getWriteAccessInfo($class, $property, $value) + { + $key = (false !== strpos($class, '@') ? rawurlencode($class) : $class).'..'.$property; + + if (isset($this->writePropertyCache[$key])) { + return $this->writePropertyCache[$key]; + } + + if ($this->cacheItemPool) { + $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_WRITE.str_replace('\\', '.', $key)); + if ($item->isHit()) { + return $this->writePropertyCache[$key] = $item->get(); + } + } + + $access = array(); + + $reflClass = new \ReflectionClass($class); + $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property); + $camelized = $this->camelize($property); + $singulars = (array) Inflector::singularize($camelized); + + if (\is_array($value) || $value instanceof \Traversable) { + $methods = $this->findAdderAndRemover($reflClass, $singulars); + + if (null !== $methods) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; + $access[self::ACCESS_ADDER] = $methods[0]; + $access[self::ACCESS_REMOVER] = $methods[1]; + } + } + + if (!isset($access[self::ACCESS_TYPE])) { + $setter = 'set'.$camelized; + $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) + + if ($this->isMethodAccessible($reflClass, $setter, 1)) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $setter; + } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $getsetter; + } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; + $access[self::ACCESS_NAME] = $property; + } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; + $access[self::ACCESS_NAME] = $property; + } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) { + // we call the getter and hope the __call do the job + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; + $access[self::ACCESS_NAME] = $setter; + } elseif (null !== $methods = $this->findAdderAndRemover($reflClass, $singulars)) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; + $access[self::ACCESS_NAME] = sprintf( + 'The property "%s" in class "%s" can be defined with the methods "%s()" but '. + 'the new value must be an array or an instance of \Traversable, '. + '"%s" given.', + $property, + $reflClass->name, + implode('()", "', $methods), + \is_object($value) ? \get_class($value) : \gettype($value) + ); + } else { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; + $access[self::ACCESS_NAME] = sprintf( + 'Neither the property "%s" nor one of the methods %s"%s()", "%s()", '. + '"__set()" or "__call()" exist and have public access in class "%s".', + $property, + implode('', array_map(function ($singular) { + return '"add'.$singular.'()"/"remove'.$singular.'()", '; + }, $singulars)), + $setter, + $getsetter, + $reflClass->name + ); + } + } + + if (isset($item)) { + $this->cacheItemPool->save($item->set($access)); + } + + return $this->writePropertyCache[$key] = $access; + } + + /** + * Returns whether a property is writable in the given object. + * + * @param object $object The object to write to + * @param string $property The property to write + * + * @return bool Whether the property is writable + */ + private function isPropertyWritable($object, $property) + { + if (!\is_object($object)) { + return false; + } + + $access = $this->getWriteAccessInfo(\get_class($object), $property, array()); + + return self::ACCESS_TYPE_METHOD === $access[self::ACCESS_TYPE] + || self::ACCESS_TYPE_PROPERTY === $access[self::ACCESS_TYPE] + || self::ACCESS_TYPE_ADDER_AND_REMOVER === $access[self::ACCESS_TYPE] + || (!$access[self::ACCESS_HAS_PROPERTY] && property_exists($object, $property)) + || self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]; + } + + /** + * Camelizes a given string. + * + * @param string $string Some string + * + * @return string The camelized version of the string + */ + private function camelize($string) + { + return str_replace(' ', '', ucwords(str_replace('_', ' ', $string))); + } + + /** + * Searches for add and remove methods. + * + * @param \ReflectionClass $reflClass The reflection class for the given object + * @param array $singulars The singular form of the property name or null + * + * @return array|null An array containing the adder and remover when found, null otherwise + */ + private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars) + { + foreach ($singulars as $singular) { + $addMethod = 'add'.$singular; + $removeMethod = 'remove'.$singular; + + $addMethodFound = $this->isMethodAccessible($reflClass, $addMethod, 1); + $removeMethodFound = $this->isMethodAccessible($reflClass, $removeMethod, 1); + + if ($addMethodFound && $removeMethodFound) { + return array($addMethod, $removeMethod); + } + } + } + + /** + * Returns whether a method is public and has the number of required parameters. + * + * @param \ReflectionClass $class The class of the method + * @param string $methodName The method name + * @param int $parameters The number of parameters + * + * @return bool Whether the method is public and has $parameters required parameters + */ + private function isMethodAccessible(\ReflectionClass $class, $methodName, $parameters) + { + if ($class->hasMethod($methodName)) { + $method = $class->getMethod($methodName); + + if ($method->isPublic() + && $method->getNumberOfRequiredParameters() <= $parameters + && $method->getNumberOfParameters() >= $parameters) { + return true; + } + } + + return false; + } + + /** + * Gets a PropertyPath instance and caches it. + * + * @param string|PropertyPath $propertyPath + * + * @return PropertyPath + */ + private function getPropertyPath($propertyPath) + { + if ($propertyPath instanceof PropertyPathInterface) { + // Don't call the copy constructor has it is not needed here + return $propertyPath; + } + + if (isset($this->propertyPathCache[$propertyPath])) { + return $this->propertyPathCache[$propertyPath]; + } + + if ($this->cacheItemPool) { + $item = $this->cacheItemPool->getItem(self::CACHE_PREFIX_PROPERTY_PATH.$propertyPath); + if ($item->isHit()) { + return $this->propertyPathCache[$propertyPath] = $item->get(); + } + } + + $propertyPathInstance = new PropertyPath($propertyPath); + if (isset($item)) { + $item->set($propertyPathInstance); + $this->cacheItemPool->save($item); + } + + return $this->propertyPathCache[$propertyPath] = $propertyPathInstance; + } + + /** + * Creates the APCu adapter if applicable. + * + * @param string $namespace + * @param int $defaultLifetime + * @param string $version + * @param LoggerInterface|null $logger + * + * @return AdapterInterface + * + * @throws RuntimeException When the Cache Component isn't available + */ + public static function createCache($namespace, $defaultLifetime, $version, LoggerInterface $logger = null) + { + if (!class_exists('Symfony\Component\Cache\Adapter\ApcuAdapter')) { + throw new \RuntimeException(sprintf('The Symfony Cache component must be installed to use %s().', __METHOD__)); + } + + if (!ApcuAdapter::isSupported()) { + return new NullAdapter(); + } + + $apcu = new ApcuAdapter($namespace, $defaultLifetime / 5, $version); + if ('cli' === \PHP_SAPI && !ini_get('apc.enable_cli')) { + $apcu->setLogger(new NullLogger()); + } elseif (null !== $logger) { + $apcu->setLogger($logger); + } + + return $apcu; + } +} diff --git a/vendor/symfony/property-access/PropertyAccessorBuilder.php b/vendor/symfony/property-access/PropertyAccessorBuilder.php new file mode 100644 index 0000000..1db6a1d --- /dev/null +++ b/vendor/symfony/property-access/PropertyAccessorBuilder.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\PropertyAccess; + +use Psr\Cache\CacheItemPoolInterface; + +/** + * A configurable builder to create a PropertyAccessor. + * + * @author Jérémie Augustin + */ +class PropertyAccessorBuilder +{ + private $magicCall = false; + private $throwExceptionOnInvalidIndex = false; + + /** + * @var CacheItemPoolInterface|null + */ + private $cacheItemPool; + + /** + * Enables the use of "__call" by the PropertyAccessor. + * + * @return $this + */ + public function enableMagicCall() + { + $this->magicCall = true; + + return $this; + } + + /** + * Disables the use of "__call" by the PropertyAccessor. + * + * @return $this + */ + public function disableMagicCall() + { + $this->magicCall = false; + + return $this; + } + + /** + * @return bool whether the use of "__call" by the PropertyAccessor is enabled + */ + public function isMagicCallEnabled() + { + return $this->magicCall; + } + + /** + * Enables exceptions when reading a non-existing index. + * + * This has no influence on writing non-existing indices with PropertyAccessorInterface::setValue() + * which are always created on-the-fly. + * + * @return $this + */ + public function enableExceptionOnInvalidIndex() + { + $this->throwExceptionOnInvalidIndex = true; + + return $this; + } + + /** + * Disables exceptions when reading a non-existing index. + * + * Instead, null is returned when calling PropertyAccessorInterface::getValue() on a non-existing index. + * + * @return $this + */ + public function disableExceptionOnInvalidIndex() + { + $this->throwExceptionOnInvalidIndex = false; + + return $this; + } + + /** + * @return bool whether an exception is thrown or null is returned when reading a non-existing index + */ + public function isExceptionOnInvalidIndexEnabled() + { + return $this->throwExceptionOnInvalidIndex; + } + + /** + * Sets a cache system. + * + * @param CacheItemPoolInterface|null $cacheItemPool + * + * @return PropertyAccessorBuilder The builder object + */ + public function setCacheItemPool(CacheItemPoolInterface $cacheItemPool = null) + { + $this->cacheItemPool = $cacheItemPool; + + return $this; + } + + /** + * Gets the used cache system. + * + * @return CacheItemPoolInterface|null + */ + public function getCacheItemPool() + { + return $this->cacheItemPool; + } + + /** + * Builds and returns a new PropertyAccessor object. + * + * @return PropertyAccessorInterface The built PropertyAccessor + */ + public function getPropertyAccessor() + { + return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool); + } +} diff --git a/vendor/symfony/property-access/PropertyAccessorInterface.php b/vendor/symfony/property-access/PropertyAccessorInterface.php new file mode 100644 index 0000000..51fa0cc --- /dev/null +++ b/vendor/symfony/property-access/PropertyAccessorInterface.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\PropertyAccess; + +/** + * Writes and reads values to/from an object/array graph. + * + * @author Bernhard Schussek + */ +interface PropertyAccessorInterface +{ + /** + * Sets the value at the end of the property path of the object graph. + * + * Example: + * + * use Symfony\Component\PropertyAccess\PropertyAccess; + * + * $propertyAccessor = PropertyAccess::createPropertyAccessor(); + * + * echo $propertyAccessor->setValue($object, 'child.name', 'Fabien'); + * // equals echo $object->getChild()->setName('Fabien'); + * + * This method first tries to find a public setter for each property in the + * path. The name of the setter must be the camel-cased property name + * prefixed with "set". + * + * If the setter does not exist, this method tries to find a public + * property. The value of the property is then changed. + * + * If neither is found, an exception is thrown. + * + * @param object|array $objectOrArray The object or array to modify + * @param string|PropertyPathInterface $propertyPath The property path to modify + * @param mixed $value The value to set at the end of the property path + * + * @throws Exception\InvalidArgumentException If the property path is invalid + * @throws Exception\AccessException If a property/index does not exist or is not public + * @throws Exception\UnexpectedTypeException If a value within the path is neither object nor array + */ + public function setValue(&$objectOrArray, $propertyPath, $value); + + /** + * Returns the value at the end of the property path of the object graph. + * + * Example: + * + * use Symfony\Component\PropertyAccess\PropertyAccess; + * + * $propertyAccessor = PropertyAccess::createPropertyAccessor(); + * + * echo $propertyAccessor->getValue($object, 'child.name); + * // equals echo $object->getChild()->getName(); + * + * This method first tries to find a public getter for each property in the + * path. The name of the getter must be the camel-cased property name + * prefixed with "get", "is", or "has". + * + * If the getter does not exist, this method tries to find a public + * property. The value of the property is then returned. + * + * If none of them are found, an exception is thrown. + * + * @param object|array $objectOrArray The object or array to traverse + * @param string|PropertyPathInterface $propertyPath The property path to read + * + * @return mixed The value at the end of the property path + * + * @throws Exception\InvalidArgumentException If the property path is invalid + * @throws Exception\AccessException If a property/index does not exist or is not public + * @throws Exception\UnexpectedTypeException If a value within the path is neither object + * nor array + */ + public function getValue($objectOrArray, $propertyPath); + + /** + * Returns whether a value can be written at a given property path. + * + * Whenever this method returns true, {@link setValue()} is guaranteed not + * to throw an exception when called with the same arguments. + * + * @param object|array $objectOrArray The object or array to check + * @param string|PropertyPathInterface $propertyPath The property path to check + * + * @return bool Whether the value can be set + * + * @throws Exception\InvalidArgumentException If the property path is invalid + */ + public function isWritable($objectOrArray, $propertyPath); + + /** + * Returns whether a property path can be read from an object graph. + * + * Whenever this method returns true, {@link getValue()} is guaranteed not + * to throw an exception when called with the same arguments. + * + * @param object|array $objectOrArray The object or array to check + * @param string|PropertyPathInterface $propertyPath The property path to check + * + * @return bool Whether the property path can be read + * + * @throws Exception\InvalidArgumentException If the property path is invalid + */ + public function isReadable($objectOrArray, $propertyPath); +} diff --git a/vendor/symfony/property-access/PropertyPath.php b/vendor/symfony/property-access/PropertyPath.php new file mode 100644 index 0000000..2897a4a --- /dev/null +++ b/vendor/symfony/property-access/PropertyPath.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\PropertyAccess; + +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException; +use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException; + +/** + * Default implementation of {@link PropertyPathInterface}. + * + * @author Bernhard Schussek + */ +class PropertyPath implements \IteratorAggregate, PropertyPathInterface +{ + /** + * Character used for separating between plural and singular of an element. + */ + const SINGULAR_SEPARATOR = '|'; + + /** + * The elements of the property path. + * + * @var array + */ + private $elements = array(); + + /** + * The number of elements in the property path. + * + * @var int + */ + private $length; + + /** + * Contains a Boolean for each property in $elements denoting whether this + * element is an index. It is a property otherwise. + * + * @var array + */ + private $isIndex = array(); + + /** + * String representation of the path. + * + * @var string + */ + private $pathAsString; + + /** + * Constructs a property path from a string. + * + * @param PropertyPath|string $propertyPath The property path as string or instance + * + * @throws InvalidArgumentException If the given path is not a string + * @throws InvalidPropertyPathException If the syntax of the property path is not valid + */ + public function __construct($propertyPath) + { + // Can be used as copy constructor + if ($propertyPath instanceof self) { + /* @var PropertyPath $propertyPath */ + $this->elements = $propertyPath->elements; + $this->length = $propertyPath->length; + $this->isIndex = $propertyPath->isIndex; + $this->pathAsString = $propertyPath->pathAsString; + + return; + } + if (!\is_string($propertyPath)) { + throw new InvalidArgumentException(sprintf('The property path constructor needs a string or an instance of "Symfony\Component\PropertyAccess\PropertyPath". Got: "%s"', \is_object($propertyPath) ? \get_class($propertyPath) : \gettype($propertyPath))); + } + + if ('' === $propertyPath) { + throw new InvalidPropertyPathException('The property path should not be empty.'); + } + + $this->pathAsString = $propertyPath; + $position = 0; + $remaining = $propertyPath; + + // first element is evaluated differently - no leading dot for properties + $pattern = '/^(([^\.\[]++)|\[([^\]]++)\])(.*)/'; + + while (preg_match($pattern, $remaining, $matches)) { + if ('' !== $matches[2]) { + $element = $matches[2]; + $this->isIndex[] = false; + } else { + $element = $matches[3]; + $this->isIndex[] = true; + } + + $this->elements[] = $element; + + $position += \strlen($matches[1]); + $remaining = $matches[4]; + $pattern = '/^(\.([^\.|\[]++)|\[([^\]]++)\])(.*)/'; + } + + if ('' !== $remaining) { + throw new InvalidPropertyPathException(sprintf('Could not parse property path "%s". Unexpected token "%s" at position %d', $propertyPath, $remaining[0], $position)); + } + + $this->length = \count($this->elements); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return $this->pathAsString; + } + + /** + * {@inheritdoc} + */ + public function getLength() + { + return $this->length; + } + + /** + * {@inheritdoc} + */ + public function getParent() + { + if ($this->length <= 1) { + return; + } + + $parent = clone $this; + + --$parent->length; + $parent->pathAsString = substr($parent->pathAsString, 0, max(strrpos($parent->pathAsString, '.'), strrpos($parent->pathAsString, '['))); + array_pop($parent->elements); + array_pop($parent->isIndex); + + return $parent; + } + + /** + * Returns a new iterator for this path. + * + * @return PropertyPathIteratorInterface + */ + public function getIterator() + { + return new PropertyPathIterator($this); + } + + /** + * {@inheritdoc} + */ + public function getElements() + { + return $this->elements; + } + + /** + * {@inheritdoc} + */ + public function getElement($index) + { + if (!isset($this->elements[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index)); + } + + return $this->elements[$index]; + } + + /** + * {@inheritdoc} + */ + public function isProperty($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index)); + } + + return !$this->isIndex[$index]; + } + + /** + * {@inheritdoc} + */ + public function isIndex($index) + { + if (!isset($this->isIndex[$index])) { + throw new OutOfBoundsException(sprintf('The index %s is not within the property path', $index)); + } + + return $this->isIndex[$index]; + } +} diff --git a/vendor/symfony/property-access/PropertyPathBuilder.php b/vendor/symfony/property-access/PropertyPathBuilder.php new file mode 100644 index 0000000..e17c53c --- /dev/null +++ b/vendor/symfony/property-access/PropertyPathBuilder.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\PropertyAccess; + +use Symfony\Component\PropertyAccess\Exception\OutOfBoundsException; + +/** + * @author Bernhard Schussek + */ +class PropertyPathBuilder +{ + private $elements = array(); + private $isIndex = array(); + + /** + * Creates a new property path builder. + * + * @param PropertyPathInterface|string|null $path The path to initially store + * in the builder. Optional. + */ + public function __construct($path = null) + { + if (null !== $path) { + $this->append($path); + } + } + + /** + * Appends a (sub-) path to the current path. + * + * @param PropertyPathInterface|string $path The path to append + * @param int $offset The offset where the appended + * piece starts in $path + * @param int $length The length of the appended piece + * If 0, the full path is appended + */ + public function append($path, $offset = 0, $length = 0) + { + if (\is_string($path)) { + $path = new PropertyPath($path); + } + + if (0 === $length) { + $end = $path->getLength(); + } else { + $end = $offset + $length; + } + + for (; $offset < $end; ++$offset) { + $this->elements[] = $path->getElement($offset); + $this->isIndex[] = $path->isIndex($offset); + } + } + + /** + * Appends an index element to the current path. + * + * @param string $name The name of the appended index + */ + public function appendIndex($name) + { + $this->elements[] = $name; + $this->isIndex[] = true; + } + + /** + * Appends a property element to the current path. + * + * @param string $name The name of the appended property + */ + public function appendProperty($name) + { + $this->elements[] = $name; + $this->isIndex[] = false; + } + + /** + * Removes elements from the current path. + * + * @param int $offset The offset at which to remove + * @param int $length The length of the removed piece + * + * @throws OutOfBoundsException if offset is invalid + */ + public function remove($offset, $length = 1) + { + if (!isset($this->elements[$offset])) { + throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset)); + } + + $this->resize($offset, $length, 0); + } + + /** + * Replaces a sub-path by a different (sub-) path. + * + * @param int $offset The offset at which to replace + * @param int $length The length of the piece to replace + * @param PropertyPathInterface|string $path The path to insert + * @param int $pathOffset The offset where the inserted piece + * starts in $path + * @param int $pathLength The length of the inserted piece + * If 0, the full path is inserted + * + * @throws OutOfBoundsException If the offset is invalid + */ + public function replace($offset, $length, $path, $pathOffset = 0, $pathLength = 0) + { + if (\is_string($path)) { + $path = new PropertyPath($path); + } + + if ($offset < 0 && abs($offset) <= $this->getLength()) { + $offset = $this->getLength() + $offset; + } elseif (!isset($this->elements[$offset])) { + throw new OutOfBoundsException('The offset '.$offset.' is not within the property path'); + } + + if (0 === $pathLength) { + $pathLength = $path->getLength() - $pathOffset; + } + + $this->resize($offset, $length, $pathLength); + + for ($i = 0; $i < $pathLength; ++$i) { + $this->elements[$offset + $i] = $path->getElement($pathOffset + $i); + $this->isIndex[$offset + $i] = $path->isIndex($pathOffset + $i); + } + ksort($this->elements); + } + + /** + * Replaces a property element by an index element. + * + * @param int $offset The offset at which to replace + * @param string $name The new name of the element. Optional + * + * @throws OutOfBoundsException If the offset is invalid + */ + public function replaceByIndex($offset, $name = null) + { + if (!isset($this->elements[$offset])) { + throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset)); + } + + if (null !== $name) { + $this->elements[$offset] = $name; + } + + $this->isIndex[$offset] = true; + } + + /** + * Replaces an index element by a property element. + * + * @param int $offset The offset at which to replace + * @param string $name The new name of the element. Optional + * + * @throws OutOfBoundsException If the offset is invalid + */ + public function replaceByProperty($offset, $name = null) + { + if (!isset($this->elements[$offset])) { + throw new OutOfBoundsException(sprintf('The offset %s is not within the property path', $offset)); + } + + if (null !== $name) { + $this->elements[$offset] = $name; + } + + $this->isIndex[$offset] = false; + } + + /** + * Returns the length of the current path. + * + * @return int The path length + */ + public function getLength() + { + return \count($this->elements); + } + + /** + * Returns the current property path. + * + * @return PropertyPathInterface The constructed property path + */ + public function getPropertyPath() + { + $pathAsString = $this->__toString(); + + return '' !== $pathAsString ? new PropertyPath($pathAsString) : null; + } + + /** + * Returns the current property path as string. + * + * @return string The property path as string + */ + public function __toString() + { + $string = ''; + + foreach ($this->elements as $offset => $element) { + if ($this->isIndex[$offset]) { + $element = '['.$element.']'; + } elseif ('' !== $string) { + $string .= '.'; + } + + $string .= $element; + } + + return $string; + } + + /** + * Resizes the path so that a chunk of length $cutLength is + * removed at $offset and another chunk of length $insertionLength + * can be inserted. + * + * @param int $offset The offset where the removed chunk starts + * @param int $cutLength The length of the removed chunk + * @param int $insertionLength The length of the inserted chunk + */ + private function resize($offset, $cutLength, $insertionLength) + { + // Nothing else to do in this case + if ($insertionLength === $cutLength) { + return; + } + + $length = \count($this->elements); + + if ($cutLength > $insertionLength) { + // More elements should be removed than inserted + $diff = $cutLength - $insertionLength; + $newLength = $length - $diff; + + // Shift elements to the left (left-to-right until the new end) + // Max allowed offset to be shifted is such that + // $offset + $diff < $length (otherwise invalid index access) + // i.e. $offset < $length - $diff = $newLength + for ($i = $offset; $i < $newLength; ++$i) { + $this->elements[$i] = $this->elements[$i + $diff]; + $this->isIndex[$i] = $this->isIndex[$i + $diff]; + } + + // All remaining elements should be removed + for (; $i < $length; ++$i) { + unset($this->elements[$i], $this->isIndex[$i]); + } + } else { + $diff = $insertionLength - $cutLength; + + $newLength = $length + $diff; + $indexAfterInsertion = $offset + $insertionLength; + + // $diff <= $insertionLength + // $indexAfterInsertion >= $insertionLength + // => $diff <= $indexAfterInsertion + + // In each of the following loops, $i >= $diff must hold, + // otherwise ($i - $diff) becomes negative. + + // Shift old elements to the right to make up space for the + // inserted elements. This needs to be done left-to-right in + // order to preserve an ascending array index order + // Since $i = max($length, $indexAfterInsertion) and $indexAfterInsertion >= $diff, + // $i >= $diff is guaranteed. + for ($i = max($length, $indexAfterInsertion); $i < $newLength; ++$i) { + $this->elements[$i] = $this->elements[$i - $diff]; + $this->isIndex[$i] = $this->isIndex[$i - $diff]; + } + + // Shift remaining elements to the right. Do this right-to-left + // so we don't overwrite elements before copying them + // The last written index is the immediate index after the inserted + // string, because the indices before that will be overwritten + // anyway. + // Since $i >= $indexAfterInsertion and $indexAfterInsertion >= $diff, + // $i >= $diff is guaranteed. + for ($i = $length - 1; $i >= $indexAfterInsertion; --$i) { + $this->elements[$i] = $this->elements[$i - $diff]; + $this->isIndex[$i] = $this->isIndex[$i - $diff]; + } + } + } +} diff --git a/vendor/symfony/property-access/PropertyPathInterface.php b/vendor/symfony/property-access/PropertyPathInterface.php new file mode 100644 index 0000000..b627ebc --- /dev/null +++ b/vendor/symfony/property-access/PropertyPathInterface.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\PropertyAccess; + +/** + * A sequence of property names or array indices. + * + * @author Bernhard Schussek + */ +interface PropertyPathInterface extends \Traversable +{ + /** + * Returns the string representation of the property path. + * + * @return string The path as string + */ + public function __toString(); + + /** + * Returns the length of the property path, i.e. the number of elements. + * + * @return int The path length + */ + public function getLength(); + + /** + * Returns the parent property path. + * + * The parent property path is the one that contains the same items as + * this one except for the last one. + * + * If this property path only contains one item, null is returned. + * + * @return PropertyPath The parent path or null + */ + public function getParent(); + + /** + * Returns the elements of the property path as array. + * + * @return array An array of property/index names + */ + public function getElements(); + + /** + * Returns the element at the given index in the property path. + * + * @param int $index The index key + * + * @return string A property or index name + * + * @throws Exception\OutOfBoundsException If the offset is invalid + */ + public function getElement($index); + + /** + * Returns whether the element at the given index is a property. + * + * @param int $index The index in the property path + * + * @return bool Whether the element at this index is a property + * + * @throws Exception\OutOfBoundsException If the offset is invalid + */ + public function isProperty($index); + + /** + * Returns whether the element at the given index is an array index. + * + * @param int $index The index in the property path + * + * @return bool Whether the element at this index is an array index + * + * @throws Exception\OutOfBoundsException If the offset is invalid + */ + public function isIndex($index); +} diff --git a/vendor/symfony/property-access/PropertyPathIterator.php b/vendor/symfony/property-access/PropertyPathIterator.php new file mode 100644 index 0000000..02fa26e --- /dev/null +++ b/vendor/symfony/property-access/PropertyPathIterator.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\PropertyAccess; + +/** + * Traverses a property path and provides additional methods to find out + * information about the current element. + * + * @author Bernhard Schussek + */ +class PropertyPathIterator extends \ArrayIterator implements PropertyPathIteratorInterface +{ + protected $path; + + /** + * @param PropertyPathInterface $path The property path to traverse + */ + public function __construct(PropertyPathInterface $path) + { + parent::__construct($path->getElements()); + + $this->path = $path; + } + + /** + * {@inheritdoc} + */ + public function isIndex() + { + return $this->path->isIndex($this->key()); + } + + /** + * {@inheritdoc} + */ + public function isProperty() + { + return $this->path->isProperty($this->key()); + } +} diff --git a/vendor/symfony/property-access/PropertyPathIteratorInterface.php b/vendor/symfony/property-access/PropertyPathIteratorInterface.php new file mode 100644 index 0000000..79b1bbf --- /dev/null +++ b/vendor/symfony/property-access/PropertyPathIteratorInterface.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\PropertyAccess; + +/** + * @author Bernhard Schussek + */ +interface PropertyPathIteratorInterface extends \Iterator, \SeekableIterator +{ + /** + * Returns whether the current element in the property path is an array + * index. + * + * @return bool + */ + public function isIndex(); + + /** + * Returns whether the current element in the property path is a property + * name. + * + * @return bool + */ + public function isProperty(); +} diff --git a/vendor/symfony/property-access/README.md b/vendor/symfony/property-access/README.md new file mode 100644 index 0000000..1959fd9 --- /dev/null +++ b/vendor/symfony/property-access/README.md @@ -0,0 +1,14 @@ +PropertyAccess Component +======================== + +The PropertyAccess component provides function to read and write from/to an +object or array using a simple string notation. + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/property_access/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/vendor/symfony/property-access/StringUtil.php b/vendor/symfony/property-access/StringUtil.php new file mode 100644 index 0000000..02e598f --- /dev/null +++ b/vendor/symfony/property-access/StringUtil.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\PropertyAccess; + +use Symfony\Component\Inflector\Inflector; + +/** + * Creates singulars from plurals. + * + * @author Bernhard Schussek + * + * @deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector} instead. + */ +class StringUtil +{ + /** + * This class should not be instantiated. + */ + private function __construct() + { + } + + /** + * Returns the singular form of a word. + * + * If the method can't determine the form with certainty, an array of the + * possible singulars is returned. + * + * @param string $plural A word in plural form + * + * @return string|array The singular form or an array of possible singular + * forms + * + * @deprecated since version 3.1, to be removed in 4.0. Use {@see Symfony\Component\Inflector\Inflector::singularize} instead. + */ + public static function singularify($plural) + { + @trigger_error('StringUtil::singularify() is deprecated since Symfony 3.1 and will be removed in 4.0. Use Symfony\Component\Inflector\Inflector::singularize instead.', E_USER_DEPRECATED); + + return Inflector::singularize($plural); + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/NonTraversableArrayObject.php b/vendor/symfony/property-access/Tests/Fixtures/NonTraversableArrayObject.php new file mode 100644 index 0000000..d2ff935 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/NonTraversableArrayObject.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\PropertyAccess\Tests\Fixtures; + +/** + * This class is a hand written simplified version of PHP native `ArrayObject` + * class, to show that it behaves differently than the PHP native implementation. + */ +class NonTraversableArrayObject implements \ArrayAccess, \Countable, \Serializable +{ + private $array; + + public function __construct(array $array = null) + { + $this->array = $array ?: array(); + } + + public function offsetExists($offset) + { + return array_key_exists($offset, $this->array); + } + + public function offsetGet($offset) + { + return $this->array[$offset]; + } + + public function offsetSet($offset, $value) + { + if (null === $offset) { + $this->array[] = $value; + } else { + $this->array[$offset] = $value; + } + } + + public function offsetUnset($offset) + { + unset($this->array[$offset]); + } + + public function count() + { + return \count($this->array); + } + + public function serialize() + { + return serialize($this->array); + } + + public function unserialize($serialized) + { + $this->array = (array) unserialize((string) $serialized); + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/ReturnTyped.php b/vendor/symfony/property-access/Tests/Fixtures/ReturnTyped.php new file mode 100644 index 0000000..71c4a57 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/ReturnTyped.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\PropertyAccess\Tests\Fixtures; + +/** + * @author Kévin Dunglas + */ +class ReturnTyped +{ + public function getFoos(): array + { + return 'It doesn\'t respect the return type on purpose'; + } + + public function addFoo(\DateTime $dateTime) + { + } + + public function removeFoo(\DateTime $dateTime) + { + } + + public function setName($name): self + { + return 'This does not respect the return type on purpose.'; + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TestClass.php b/vendor/symfony/property-access/Tests/Fixtures/TestClass.php new file mode 100644 index 0000000..e63af3a --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TestClass.php @@ -0,0 +1,187 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests\Fixtures; + +class TestClass +{ + public $publicProperty; + protected $protectedProperty; + private $privateProperty; + + private $publicAccessor; + private $publicMethodAccessor; + private $publicGetSetter; + private $publicAccessorWithDefaultValue; + private $publicAccessorWithRequiredAndDefaultValue; + private $publicAccessorWithMoreRequiredParameters; + private $publicIsAccessor; + private $publicHasAccessor; + private $publicGetter; + private $date; + + public function __construct($value) + { + $this->publicProperty = $value; + $this->publicAccessor = $value; + $this->publicMethodAccessor = $value; + $this->publicGetSetter = $value; + $this->publicAccessorWithDefaultValue = $value; + $this->publicAccessorWithRequiredAndDefaultValue = $value; + $this->publicAccessorWithMoreRequiredParameters = $value; + $this->publicIsAccessor = $value; + $this->publicHasAccessor = $value; + $this->publicGetter = $value; + } + + public function setPublicAccessor($value) + { + $this->publicAccessor = $value; + } + + public function setPublicAccessorWithDefaultValue($value = null) + { + $this->publicAccessorWithDefaultValue = $value; + } + + public function setPublicAccessorWithRequiredAndDefaultValue($value, $optional = null) + { + $this->publicAccessorWithRequiredAndDefaultValue = $value; + } + + public function setPublicAccessorWithMoreRequiredParameters($value, $needed) + { + $this->publicAccessorWithMoreRequiredParameters = $value; + } + + public function getPublicAccessor() + { + return $this->publicAccessor; + } + + public function getPublicAccessorWithDefaultValue() + { + return $this->publicAccessorWithDefaultValue; + } + + public function getPublicAccessorWithRequiredAndDefaultValue() + { + return $this->publicAccessorWithRequiredAndDefaultValue; + } + + public function getPublicAccessorWithMoreRequiredParameters() + { + return $this->publicAccessorWithMoreRequiredParameters; + } + + public function setPublicIsAccessor($value) + { + $this->publicIsAccessor = $value; + } + + public function isPublicIsAccessor() + { + return $this->publicIsAccessor; + } + + public function setPublicHasAccessor($value) + { + $this->publicHasAccessor = $value; + } + + public function hasPublicHasAccessor() + { + return $this->publicHasAccessor; + } + + public function publicGetSetter($value = null) + { + if (null !== $value) { + $this->publicGetSetter = $value; + } + + return $this->publicGetSetter; + } + + public function getPublicMethodMutator() + { + return $this->publicGetSetter; + } + + protected function setProtectedAccessor($value) + { + } + + protected function getProtectedAccessor() + { + return 'foobar'; + } + + protected function setProtectedIsAccessor($value) + { + } + + protected function isProtectedIsAccessor() + { + return 'foobar'; + } + + protected function setProtectedHasAccessor($value) + { + } + + protected function hasProtectedHasAccessor() + { + return 'foobar'; + } + + private function setPrivateAccessor($value) + { + } + + private function getPrivateAccessor() + { + return 'foobar'; + } + + private function setPrivateIsAccessor($value) + { + } + + private function isPrivateIsAccessor() + { + return 'foobar'; + } + + private function setPrivateHasAccessor($value) + { + } + + private function hasPrivateHasAccessor() + { + return 'foobar'; + } + + public function getPublicGetter() + { + return $this->publicGetter; + } + + public function setDate(\DateTimeInterface $date) + { + $this->date = $date; + } + + public function getDate() + { + return $this->date; + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TestClassIsWritable.php b/vendor/symfony/property-access/Tests/Fixtures/TestClassIsWritable.php new file mode 100644 index 0000000..4e966cd --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TestClassIsWritable.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\PropertyAccess\Tests\Fixtures; + +class TestClassIsWritable +{ + protected $value; + + public function getValue() + { + return $this->value; + } + + public function __construct($value) + { + $this->value = $value; + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TestClassMagicCall.php b/vendor/symfony/property-access/Tests/Fixtures/TestClassMagicCall.php new file mode 100644 index 0000000..0d6c1f0 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TestClassMagicCall.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\PropertyAccess\Tests\Fixtures; + +class TestClassMagicCall +{ + private $magicCallProperty; + + public function __construct($value) + { + $this->magicCallProperty = $value; + } + + public function __call($method, array $args) + { + if ('getMagicCallProperty' === $method) { + return $this->magicCallProperty; + } + + if ('getConstantMagicCallProperty' === $method) { + return 'constant value'; + } + + if ('setMagicCallProperty' === $method) { + $this->magicCallProperty = reset($args); + } + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TestClassMagicGet.php b/vendor/symfony/property-access/Tests/Fixtures/TestClassMagicGet.php new file mode 100644 index 0000000..e465325 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TestClassMagicGet.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\PropertyAccess\Tests\Fixtures; + +class TestClassMagicGet +{ + private $magicProperty; + + public $publicProperty; + + public function __construct($value) + { + $this->magicProperty = $value; + } + + public function __set($property, $value) + { + if ('magicProperty' === $property) { + $this->magicProperty = $value; + } + } + + public function __get($property) + { + if ('magicProperty' === $property) { + return $this->magicProperty; + } + + if ('constantMagicProperty' === $property) { + return 'constant value'; + } + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TestClassSetValue.php b/vendor/symfony/property-access/Tests/Fixtures/TestClassSetValue.php new file mode 100644 index 0000000..f0a7f1f --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TestClassSetValue.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\PropertyAccess\Tests\Fixtures; + +class TestClassSetValue +{ + protected $value; + + public function getValue() + { + return $this->value; + } + + public function setValue($value) + { + $this->value = $value; + } + + public function __construct($value) + { + $this->value = $value; + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TestClassTypeErrorInsideCall.php b/vendor/symfony/property-access/Tests/Fixtures/TestClassTypeErrorInsideCall.php new file mode 100644 index 0000000..44a9390 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TestClassTypeErrorInsideCall.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\PropertyAccess\Tests\Fixtures; + +class TestClassTypeErrorInsideCall +{ + public function expectsDateTime(\DateTime $date) + { + } + + public function getProperty() + { + } + + public function setProperty($property) + { + $this->expectsDateTime(null); // throws TypeError + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/Ticket5775Object.php b/vendor/symfony/property-access/Tests/Fixtures/Ticket5775Object.php new file mode 100644 index 0000000..5954dc3 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/Ticket5775Object.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\PropertyAccess\Tests\Fixtures; + +class Ticket5775Object +{ + private $property; + + public function getProperty() + { + return $this->property; + } + + private function setProperty() + { + } + + public function __set($property, $value) + { + $this->$property = $value; + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TraversableArrayObject.php b/vendor/symfony/property-access/Tests/Fixtures/TraversableArrayObject.php new file mode 100644 index 0000000..6758f18 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TraversableArrayObject.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\PropertyAccess\Tests\Fixtures; + +/** + * This class is a hand written simplified version of PHP native `ArrayObject` + * class, to show that it behaves differently than the PHP native implementation. + */ +class TraversableArrayObject implements \ArrayAccess, \IteratorAggregate, \Countable, \Serializable +{ + private $array; + + public function __construct(array $array = null) + { + $this->array = $array ?: array(); + } + + public function offsetExists($offset) + { + return array_key_exists($offset, $this->array); + } + + public function offsetGet($offset) + { + return $this->array[$offset]; + } + + public function offsetSet($offset, $value) + { + if (null === $offset) { + $this->array[] = $value; + } else { + $this->array[$offset] = $value; + } + } + + public function offsetUnset($offset) + { + unset($this->array[$offset]); + } + + public function getIterator() + { + return new \ArrayIterator($this->array); + } + + public function count() + { + return \count($this->array); + } + + public function serialize() + { + return serialize($this->array); + } + + public function unserialize($serialized) + { + $this->array = (array) unserialize((string) $serialized); + } +} diff --git a/vendor/symfony/property-access/Tests/Fixtures/TypeHinted.php b/vendor/symfony/property-access/Tests/Fixtures/TypeHinted.php new file mode 100644 index 0000000..ce0f3d8 --- /dev/null +++ b/vendor/symfony/property-access/Tests/Fixtures/TypeHinted.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\PropertyAccess\Tests\Fixtures; + +/** + * @author Kévin Dunglas + */ +class TypeHinted +{ + private $date; + + /** + * @var \Countable + */ + private $countable; + + public function setDate(\DateTime $date) + { + $this->date = $date; + } + + public function getDate() + { + return $this->date; + } + + /** + * @return \Countable + */ + public function getCountable() + { + return $this->countable; + } + + /** + * @param \Countable $countable + */ + public function setCountable(\Countable $countable) + { + $this->countable = $countable; + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorArrayAccessTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorArrayAccessTest.php new file mode 100644 index 0000000..b0ed69c --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorArrayAccessTest.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 Symfony\Component\PropertyAccess\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessor; + +abstract class PropertyAccessorArrayAccessTest extends TestCase +{ + /** + * @var PropertyAccessor + */ + protected $propertyAccessor; + + protected function setUp() + { + $this->propertyAccessor = new PropertyAccessor(); + } + + abstract protected function getContainer(array $array); + + public function getValidPropertyPaths() + { + return array( + array($this->getContainer(array('firstName' => 'Bernhard')), '[firstName]', 'Bernhard'), + array($this->getContainer(array('person' => $this->getContainer(array('firstName' => 'Bernhard')))), '[person][firstName]', 'Bernhard'), + ); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testGetValue($collection, $path, $value) + { + $this->assertSame($value, $this->propertyAccessor->getValue($collection, $path)); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException + */ + public function testGetValueFailsIfNoSuchIndex() + { + $this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder() + ->enableExceptionOnInvalidIndex() + ->getPropertyAccessor(); + + $object = $this->getContainer(array('firstName' => 'Bernhard')); + + $this->propertyAccessor->getValue($object, '[lastName]'); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testSetValue($collection, $path) + { + $this->propertyAccessor->setValue($collection, $path, 'Updated'); + + $this->assertSame('Updated', $this->propertyAccessor->getValue($collection, $path)); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testIsReadable($collection, $path) + { + $this->assertTrue($this->propertyAccessor->isReadable($collection, $path)); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testIsWritable($collection, $path) + { + $this->assertTrue($this->propertyAccessor->isWritable($collection, $path)); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorArrayObjectTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorArrayObjectTest.php new file mode 100644 index 0000000..fb0b383 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorArrayObjectTest.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\PropertyAccess\Tests; + +class PropertyAccessorArrayObjectTest extends PropertyAccessorCollectionTest +{ + protected function getContainer(array $array) + { + return new \ArrayObject($array); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorArrayTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorArrayTest.php new file mode 100644 index 0000000..c982826 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorArrayTest.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\PropertyAccess\Tests; + +class PropertyAccessorArrayTest extends PropertyAccessorCollectionTest +{ + protected function getContainer(array $array) + { + return $array; + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorBuilderTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorBuilderTest.php new file mode 100644 index 0000000..63bd642 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorBuilderTest.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\PropertyAccess\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\PropertyAccess\PropertyAccessorBuilder; + +class PropertyAccessorBuilderTest extends TestCase +{ + /** + * @var PropertyAccessorBuilder + */ + protected $builder; + + protected function setUp() + { + $this->builder = new PropertyAccessorBuilder(); + } + + protected function tearDown() + { + $this->builder = null; + } + + public function testEnableMagicCall() + { + $this->assertSame($this->builder, $this->builder->enableMagicCall()); + } + + public function testDisableMagicCall() + { + $this->assertSame($this->builder, $this->builder->disableMagicCall()); + } + + public function testIsMagicCallEnable() + { + $this->assertFalse($this->builder->isMagicCallEnabled()); + $this->assertTrue($this->builder->enableMagicCall()->isMagicCallEnabled()); + $this->assertFalse($this->builder->disableMagicCall()->isMagicCallEnabled()); + } + + public function testGetPropertyAccessor() + { + $this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor()); + $this->assertInstanceOf(PropertyAccessor::class, $this->builder->enableMagicCall()->getPropertyAccessor()); + } + + public function testUseCache() + { + $cacheItemPool = new ArrayAdapter(); + $this->builder->setCacheItemPool($cacheItemPool); + $this->assertEquals($cacheItemPool, $this->builder->getCacheItemPool()); + $this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor()); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorCollectionTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorCollectionTest.php new file mode 100644 index 0000000..8a7ab10 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorCollectionTest.php @@ -0,0 +1,200 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests; + +class PropertyAccessorCollectionTest_Car +{ + private $axes; + + public function __construct($axes = null) + { + $this->axes = $axes; + } + + // In the test, use a name that StringUtil can't uniquely singularify + public function addAxis($axis) + { + $this->axes[] = $axis; + } + + public function removeAxis($axis) + { + foreach ($this->axes as $key => $value) { + if ($value === $axis) { + unset($this->axes[$key]); + + return; + } + } + } + + public function getAxes() + { + return $this->axes; + } +} + +class PropertyAccessorCollectionTest_CarOnlyAdder +{ + public function addAxis($axis) + { + } + + public function getAxes() + { + } +} + +class PropertyAccessorCollectionTest_CarOnlyRemover +{ + public function removeAxis($axis) + { + } + + public function getAxes() + { + } +} + +class PropertyAccessorCollectionTest_CarNoAdderAndRemover +{ + public function getAxes() + { + } +} + +class PropertyAccessorCollectionTest_CompositeCar +{ + public function getStructure() + { + } + + public function setStructure($structure) + { + } +} + +class PropertyAccessorCollectionTest_CarStructure +{ + public function addAxis($axis) + { + } + + public function removeAxis($axis) + { + } + + public function getAxes() + { + } +} + +abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAccessTest +{ + public function testSetValueCallsAdderAndRemoverForCollections() + { + $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth', 4 => 'fifth')); + $axesMerged = $this->getContainer(array(1 => 'first', 2 => 'second', 3 => 'third')); + $axesAfter = $this->getContainer(array(1 => 'second', 5 => 'first', 6 => 'third')); + $axesMergedCopy = \is_object($axesMerged) ? clone $axesMerged : $axesMerged; + + // Don't use a mock in order to test whether the collections are + // modified while iterating them + $car = new PropertyAccessorCollectionTest_Car($axesBefore); + + $this->propertyAccessor->setValue($car, 'axes', $axesMerged); + + $this->assertEquals($axesAfter, $car->getAxes()); + + // The passed collection was not modified + $this->assertEquals($axesMergedCopy, $axesMerged); + } + + public function testSetValueCallsAdderAndRemoverForNestedCollections() + { + $car = $this->getMockBuilder(__CLASS__.'_CompositeCar')->getMock(); + $structure = $this->getMockBuilder(__CLASS__.'_CarStructure')->getMock(); + $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth')); + $axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $car->expects($this->any()) + ->method('getStructure') + ->will($this->returnValue($structure)); + + $structure->expects($this->at(0)) + ->method('getAxes') + ->will($this->returnValue($axesBefore)); + $structure->expects($this->at(1)) + ->method('removeAxis') + ->with('fourth'); + $structure->expects($this->at(2)) + ->method('addAxis') + ->with('first'); + $structure->expects($this->at(3)) + ->method('addAxis') + ->with('third'); + + $this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * @expectedExceptionMessageRegExp /Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./ + */ + public function testSetValueFailsIfNoAdderNorRemoverFound() + { + $car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock(); + $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth')); + $axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third')); + + $car->expects($this->any()) + ->method('getAxes') + ->will($this->returnValue($axesBefore)); + + $this->propertyAccessor->setValue($car, 'axes', $axesAfter); + } + + public function testIsWritableReturnsTrueIfAdderAndRemoverExists() + { + $car = $this->getMockBuilder(__CLASS__.'_Car')->getMock(); + $this->assertTrue($this->propertyAccessor->isWritable($car, 'axes')); + } + + public function testIsWritableReturnsFalseIfOnlyAdderExists() + { + $car = $this->getMockBuilder(__CLASS__.'_CarOnlyAdder')->getMock(); + $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes')); + } + + public function testIsWritableReturnsFalseIfOnlyRemoverExists() + { + $car = $this->getMockBuilder(__CLASS__.'_CarOnlyRemover')->getMock(); + $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes')); + } + + public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists() + { + $car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock(); + $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes')); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + * expectedExceptionMessageRegExp /The property "axes" in class "Mock_PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis()", "removeAxis()" but the new value must be an array or an instance of \Traversable, "string" given./ + */ + public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable() + { + $car = $this->getMockBuilder(__CLASS__.'_Car')->getMock(); + + $this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable'); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorNonTraversableArrayObjectTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorNonTraversableArrayObjectTest.php new file mode 100644 index 0000000..6910d8b --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorNonTraversableArrayObjectTest.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\PropertyAccess\Tests; + +use Symfony\Component\PropertyAccess\Tests\Fixtures\NonTraversableArrayObject; + +class PropertyAccessorNonTraversableArrayObjectTest extends PropertyAccessorArrayAccessTest +{ + protected function getContainer(array $array) + { + return new NonTraversableArrayObject($array); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorTest.php new file mode 100644 index 0000000..07e5e2f --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorTest.php @@ -0,0 +1,689 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; +use Symfony\Component\PropertyAccess\PropertyAccessor; +use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassSetValue; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassTypeErrorInsideCall; +use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object; +use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted; + +class PropertyAccessorTest extends TestCase +{ + /** + * @var PropertyAccessor + */ + private $propertyAccessor; + + protected function setUp() + { + $this->propertyAccessor = new PropertyAccessor(); + } + + public function getPathsWithUnexpectedType() + { + return array( + array('', 'foobar'), + array('foo', 'foobar'), + array(null, 'foobar'), + array(123, 'foobar'), + array((object) array('prop' => null), 'prop.foobar'), + array((object) array('prop' => (object) array('subProp' => null)), 'prop.subProp.foobar'), + array(array('index' => null), '[index][foobar]'), + array(array('index' => array('subIndex' => null)), '[index][subIndex][foobar]'), + ); + } + + public function getPathsWithMissingProperty() + { + return array( + array((object) array('firstName' => 'Bernhard'), 'lastName'), + array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.lastName'), + array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].lastName'), + array(new TestClass('Bernhard'), 'protectedProperty'), + array(new TestClass('Bernhard'), 'privateProperty'), + array(new TestClass('Bernhard'), 'protectedAccessor'), + array(new TestClass('Bernhard'), 'protectedIsAccessor'), + array(new TestClass('Bernhard'), 'protectedHasAccessor'), + array(new TestClass('Bernhard'), 'privateAccessor'), + array(new TestClass('Bernhard'), 'privateIsAccessor'), + array(new TestClass('Bernhard'), 'privateHasAccessor'), + + // Properties are not camelized + array(new TestClass('Bernhard'), 'public_property'), + ); + } + + public function getPathsWithMissingIndex() + { + return array( + array(array('firstName' => 'Bernhard'), '[lastName]'), + array(array(), '[index][lastName]'), + array(array('index' => array()), '[index][lastName]'), + array(array('index' => array('firstName' => 'Bernhard')), '[index][lastName]'), + array((object) array('property' => array('firstName' => 'Bernhard')), 'property[lastName]'), + ); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testGetValue($objectOrArray, $path, $value) + { + $this->assertSame($value, $this->propertyAccessor->getValue($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingProperty + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + */ + public function testGetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path) + { + $this->propertyAccessor->getValue($objectOrArray, $path); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testGetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $path) + { + $this->assertNull($this->propertyAccessor->getValue($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingIndex + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException + */ + public function testGetValueThrowsExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path) + { + $this->propertyAccessor = new PropertyAccessor(false, true); + $this->propertyAccessor->getValue($objectOrArray, $path); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException + */ + public function testGetValueThrowsExceptionIfNotArrayAccess() + { + $this->propertyAccessor->getValue(new \stdClass(), '[index]'); + } + + public function testGetValueReadsMagicGet() + { + $this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'magicProperty')); + } + + public function testGetValueReadsArrayWithMissingIndexForCustomPropertyPath() + { + $object = new \ArrayObject(); + $array = array('child' => array('index' => $object)); + + $this->assertNull($this->propertyAccessor->getValue($array, '[child][index][foo][bar]')); + $this->assertSame(array(), $object->getArrayCopy()); + } + + // https://github.com/symfony/symfony/pull/4450 + public function testGetValueReadsMagicGetThatReturnsConstant() + { + $this->assertSame('constant value', $this->propertyAccessor->getValue(new TestClassMagicGet('Bernhard'), 'constantMagicProperty')); + } + + public function testGetValueNotModifyObject() + { + $object = new \stdClass(); + $object->firstName = array('Bernhard'); + + $this->assertNull($this->propertyAccessor->getValue($object, 'firstName[1]')); + $this->assertSame(array('Bernhard'), $object->firstName); + } + + public function testGetValueNotModifyObjectException() + { + $propertyAccessor = new PropertyAccessor(false, true); + $object = new \stdClass(); + $object->firstName = array('Bernhard'); + + try { + $propertyAccessor->getValue($object, 'firstName[1]'); + } catch (NoSuchIndexException $e) { + } + + $this->assertSame(array('Bernhard'), $object->firstName); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + */ + public function testGetValueDoesNotReadMagicCallByDefault() + { + $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty'); + } + + public function testGetValueReadsMagicCallIfEnabled() + { + $this->propertyAccessor = new PropertyAccessor(true); + + $this->assertSame('Bernhard', $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'magicCallProperty')); + } + + // https://github.com/symfony/symfony/pull/4450 + public function testGetValueReadsMagicCallThatReturnsConstant() + { + $this->propertyAccessor = new PropertyAccessor(true); + + $this->assertSame('constant value', $this->propertyAccessor->getValue(new TestClassMagicCall('Bernhard'), 'constantMagicCallProperty')); + } + + /** + * @dataProvider getPathsWithUnexpectedType + * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on + */ + public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path) + { + $this->propertyAccessor->getValue($objectOrArray, $path); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testSetValue($objectOrArray, $path) + { + $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated'); + + $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingProperty + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + */ + public function testSetValueThrowsExceptionIfPropertyNotFound($objectOrArray, $path) + { + $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated'); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testSetValueThrowsNoExceptionIfIndexNotFound($objectOrArray, $path) + { + $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated'); + + $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testSetValueThrowsNoExceptionIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path) + { + $this->propertyAccessor = new PropertyAccessor(false, true); + $this->propertyAccessor->setValue($objectOrArray, $path, 'Updated'); + + $this->assertSame('Updated', $this->propertyAccessor->getValue($objectOrArray, $path)); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchIndexException + */ + public function testSetValueThrowsExceptionIfNotArrayAccess() + { + $object = new \stdClass(); + + $this->propertyAccessor->setValue($object, '[index]', 'Updated'); + } + + public function testSetValueUpdatesMagicSet() + { + $author = new TestClassMagicGet('Bernhard'); + + $this->propertyAccessor->setValue($author, 'magicProperty', 'Updated'); + + $this->assertEquals('Updated', $author->__get('magicProperty')); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + */ + public function testSetValueThrowsExceptionIfThereAreMissingParameters() + { + $object = new TestClass('Bernhard'); + + $this->propertyAccessor->setValue($object, 'publicAccessorWithMoreRequiredParameters', 'Updated'); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException + */ + public function testSetValueDoesNotUpdateMagicCallByDefault() + { + $author = new TestClassMagicCall('Bernhard'); + + $this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated'); + } + + public function testSetValueUpdatesMagicCallIfEnabled() + { + $this->propertyAccessor = new PropertyAccessor(true); + + $author = new TestClassMagicCall('Bernhard'); + + $this->propertyAccessor->setValue($author, 'magicCallProperty', 'Updated'); + + $this->assertEquals('Updated', $author->__call('getMagicCallProperty', array())); + } + + /** + * @dataProvider getPathsWithUnexpectedType + * @expectedException \Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException + * @expectedExceptionMessage PropertyAccessor requires a graph of objects or arrays to operate on + */ + public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $path) + { + $this->propertyAccessor->setValue($objectOrArray, $path, 'value'); + } + + public function testGetValueWhenArrayValueIsNull() + { + $this->propertyAccessor = new PropertyAccessor(false, true); + $this->assertNull($this->propertyAccessor->getValue(array('index' => array('nullable' => null)), '[index][nullable]')); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testIsReadable($objectOrArray, $path) + { + $this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingProperty + */ + public function testIsReadableReturnsFalseIfPropertyNotFound($objectOrArray, $path) + { + $this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testIsReadableReturnsTrueIfIndexNotFound($objectOrArray, $path) + { + // Non-existing indices can be read. In this case, null is returned + $this->assertTrue($this->propertyAccessor->isReadable($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testIsReadableReturnsFalseIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path) + { + $this->propertyAccessor = new PropertyAccessor(false, true); + + // When exceptions are enabled, non-existing indices cannot be read + $this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path)); + } + + public function testIsReadableRecognizesMagicGet() + { + $this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicGet('Bernhard'), 'magicProperty')); + } + + public function testIsReadableDoesNotRecognizeMagicCallByDefault() + { + $this->assertFalse($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty')); + } + + public function testIsReadableRecognizesMagicCallIfEnabled() + { + $this->propertyAccessor = new PropertyAccessor(true); + + $this->assertTrue($this->propertyAccessor->isReadable(new TestClassMagicCall('Bernhard'), 'magicCallProperty')); + } + + /** + * @dataProvider getPathsWithUnexpectedType + */ + public function testIsReadableReturnsFalseIfNotObjectOrArray($objectOrArray, $path) + { + $this->assertFalse($this->propertyAccessor->isReadable($objectOrArray, $path)); + } + + /** + * @dataProvider getValidPropertyPaths + */ + public function testIsWritable($objectOrArray, $path) + { + $this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingProperty + */ + public function testIsWritableReturnsFalseIfPropertyNotFound($objectOrArray, $path) + { + $this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testIsWritableReturnsTrueIfIndexNotFound($objectOrArray, $path) + { + // Non-existing indices can be written. Arrays are created on-demand. + $this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path)); + } + + /** + * @dataProvider getPathsWithMissingIndex + */ + public function testIsWritableReturnsTrueIfIndexNotFoundAndIndexExceptionsEnabled($objectOrArray, $path) + { + $this->propertyAccessor = new PropertyAccessor(false, true); + + // Non-existing indices can be written even if exceptions are enabled + $this->assertTrue($this->propertyAccessor->isWritable($objectOrArray, $path)); + } + + public function testIsWritableRecognizesMagicSet() + { + $this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicGet('Bernhard'), 'magicProperty')); + } + + public function testIsWritableDoesNotRecognizeMagicCallByDefault() + { + $this->assertFalse($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty')); + } + + public function testIsWritableRecognizesMagicCallIfEnabled() + { + $this->propertyAccessor = new PropertyAccessor(true); + + $this->assertTrue($this->propertyAccessor->isWritable(new TestClassMagicCall('Bernhard'), 'magicCallProperty')); + } + + /** + * @dataProvider getPathsWithUnexpectedType + */ + public function testIsWritableReturnsFalseIfNotObjectOrArray($objectOrArray, $path) + { + $this->assertFalse($this->propertyAccessor->isWritable($objectOrArray, $path)); + } + + public function getValidPropertyPaths() + { + return array( + array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'), + array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'), + array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'), + array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'), + array((object) array('firstName' => 'Bernhard'), 'firstName', 'Bernhard'), + array((object) array('property' => array('firstName' => 'Bernhard')), 'property[firstName]', 'Bernhard'), + array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].firstName', 'Bernhard'), + array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.firstName', 'Bernhard'), + + // Accessor methods + array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'), + array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'), + array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'), + array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'), + array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'), + array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'), + array(new TestClass('Bernhard'), 'publicGetSetter', 'Bernhard'), + + // Methods are camelized + array(new TestClass('Bernhard'), 'public_accessor', 'Bernhard'), + array(new TestClass('Bernhard'), '_public_accessor', 'Bernhard'), + + // Missing indices + array(array('index' => array()), '[index][firstName]', null), + array(array('root' => array('index' => array())), '[root][index][firstName]', null), + + // Special chars + array(array('%!@$§.' => 'Bernhard'), '[%!@$§.]', 'Bernhard'), + array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'), + array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'), + array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'), + + // nested objects and arrays + array(array('foo' => new TestClass('bar')), '[foo].publicGetSetter', 'bar'), + array(new TestClass(array('foo' => 'bar')), 'publicGetSetter[foo]', 'bar'), + array(new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'), + array(new TestClass(array('foo' => new TestClass('bar'))), 'publicGetter[foo].publicGetSetter', 'bar'), + array(new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'), + array(new TestClass(array('foo' => array('baz' => new TestClass('bar')))), 'publicGetter[foo][baz].publicGetSetter', 'bar'), + ); + } + + public function testTicket5755() + { + $object = new Ticket5775Object(); + + $this->propertyAccessor->setValue($object, 'property', 'foobar'); + + $this->assertEquals('foobar', $object->getProperty()); + } + + public function testSetValueDeepWithMagicGetter() + { + $obj = new TestClassMagicGet('foo'); + $obj->publicProperty = array('foo' => array('bar' => 'some_value')); + $this->propertyAccessor->setValue($obj, 'publicProperty[foo][bar]', 'Updated'); + $this->assertSame('Updated', $obj->publicProperty['foo']['bar']); + } + + public function getReferenceChainObjectsForSetValue() + { + return array( + array(array('a' => array('b' => array('c' => 'old-value'))), '[a][b][c]', 'new-value'), + array(new TestClassSetValue(new TestClassSetValue('old-value')), 'value.value', 'new-value'), + array(new TestClassSetValue(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', 'new-value'), + array(new TestClassSetValue(array('a' => array('b' => 'old-value'))), 'value[a][b]', 'new-value'), + array(new \ArrayIterator(array('a' => array('b' => array('c' => 'old-value')))), '[a][b][c]', 'new-value'), + ); + } + + /** + * @dataProvider getReferenceChainObjectsForSetValue + */ + public function testSetValueForReferenceChainIssue($object, $path, $value) + { + $this->propertyAccessor->setValue($object, $path, $value); + + $this->assertEquals($value, $this->propertyAccessor->getValue($object, $path)); + } + + public function getReferenceChainObjectsForIsWritable() + { + return array( + array(new TestClassIsWritable(array('a' => array('b' => 'old-value'))), 'value[a][b]', false), + array(new TestClassIsWritable(new \ArrayIterator(array('a' => array('b' => 'old-value')))), 'value[a][b]', true), + array(new TestClassIsWritable(array('a' => array('b' => array('c' => new TestClassSetValue('old-value'))))), 'value[a][b][c].value', true), + ); + } + + /** + * @dataProvider getReferenceChainObjectsForIsWritable + */ + public function testIsWritableForReferenceChainIssue($object, $path, $value) + { + $this->assertEquals($value, $this->propertyAccessor->isWritable($object, $path)); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + * @expectedExceptionMessage Expected argument of type "DateTime", "string" given + */ + public function testThrowTypeError() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.'); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + * @expectedExceptionMessage Expected argument of type "DateTime", "NULL" given + */ + public function testThrowTypeErrorWithNullArgument() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', null); + } + + public function testSetTypeHint() + { + $date = new \DateTime(); + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'date', $date); + $this->assertSame($date, $object->getDate()); + } + + public function testArrayNotBeeingOverwritten() + { + $value = array('value1' => 'foo', 'value2' => 'bar'); + $object = new TestClass($value); + + $this->propertyAccessor->setValue($object, 'publicAccessor[value2]', 'baz'); + $this->assertSame('baz', $this->propertyAccessor->getValue($object, 'publicAccessor[value2]')); + $this->assertSame(array('value1' => 'foo', 'value2' => 'baz'), $object->getPublicAccessor()); + } + + public function testCacheReadAccess() + { + $obj = new TestClass('foo'); + + $propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter()); + $this->assertEquals('foo', $propertyAccessor->getValue($obj, 'publicGetSetter')); + $propertyAccessor->setValue($obj, 'publicGetSetter', 'bar'); + $propertyAccessor->setValue($obj, 'publicGetSetter', 'baz'); + $this->assertEquals('baz', $propertyAccessor->getValue($obj, 'publicGetSetter')); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + * @expectedExceptionMessage Expected argument of type "Countable", "string" given + */ + public function testThrowTypeErrorWithInterface() + { + $object = new TypeHinted(); + + $this->propertyAccessor->setValue($object, 'countable', 'This is a string, \Countable expected.'); + } + + /** + * @requires PHP 7.0 + */ + public function testAnonymousClassRead() + { + $value = 'bar'; + + $obj = $this->generateAnonymousClass($value); + + $propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter()); + + $this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo')); + } + + /** + * @requires PHP 7.0 + */ + public function testAnonymousClassWrite() + { + $value = 'bar'; + + $obj = $this->generateAnonymousClass(''); + + $propertyAccessor = new PropertyAccessor(false, false, new ArrayAdapter()); + $propertyAccessor->setValue($obj, 'foo', $value); + + $this->assertEquals($value, $propertyAccessor->getValue($obj, 'foo')); + } + + private function generateAnonymousClass($value) + { + $obj = eval('return new class($value) + { + private $foo; + + public function __construct($foo) + { + $this->foo = $foo; + } + + /** + * @return mixed + */ + public function getFoo() + { + return $this->foo; + } + + /** + * @param mixed $foo + */ + public function setFoo($foo) + { + $this->foo = $foo; + } + };'); + + return $obj; + } + + /** + * @requires PHP 7.0 + * @expectedException \TypeError + */ + public function testThrowTypeErrorInsideSetterCall() + { + $object = new TestClassTypeErrorInsideCall(); + + $this->propertyAccessor->setValue($object, 'property', 'foo'); + } + + /** + * @requires PHP 7 + * + * @expectedException \TypeError + */ + public function testDoNotDiscardReturnTypeError() + { + $object = new ReturnTyped(); + + $this->propertyAccessor->setValue($object, 'foos', array(new \DateTime())); + } + + /** + * @requires PHP 7 + * + * @expectedException \TypeError + */ + public function testDoNotDiscardReturnTypeErrorWhenWriterMethodIsMisconfigured() + { + $object = new ReturnTyped(); + + $this->propertyAccessor->setValue($object, 'name', 'foo'); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyAccessorTraversableArrayObjectTest.php b/vendor/symfony/property-access/Tests/PropertyAccessorTraversableArrayObjectTest.php new file mode 100644 index 0000000..4e45001 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyAccessorTraversableArrayObjectTest.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\PropertyAccess\Tests; + +use Symfony\Component\PropertyAccess\Tests\Fixtures\TraversableArrayObject; + +class PropertyAccessorTraversableArrayObjectTest extends PropertyAccessorCollectionTest +{ + protected function getContainer(array $array) + { + return new TraversableArrayObject($array); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyPathBuilderTest.php b/vendor/symfony/property-access/Tests/PropertyPathBuilderTest.php new file mode 100644 index 0000000..4482269 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyPathBuilderTest.php @@ -0,0 +1,288 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyAccess\PropertyPath; +use Symfony\Component\PropertyAccess\PropertyPathBuilder; + +/** + * @author Bernhard Schussek + */ +class PropertyPathBuilderTest extends TestCase +{ + const PREFIX = 'old1[old2].old3[old4][old5].old6'; + + /** + * @var PropertyPathBuilder + */ + private $builder; + + protected function setUp() + { + $this->builder = new PropertyPathBuilder(new PropertyPath(self::PREFIX)); + } + + public function testCreateEmpty() + { + $builder = new PropertyPathBuilder(); + + $this->assertNull($builder->getPropertyPath()); + } + + public function testCreateCopyPath() + { + $this->assertEquals(new PropertyPath(self::PREFIX), $this->builder->getPropertyPath()); + } + + public function testAppendIndex() + { + $this->builder->appendIndex('new1'); + + $path = new PropertyPath(self::PREFIX.'[new1]'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testAppendProperty() + { + $this->builder->appendProperty('new1'); + + $path = new PropertyPath(self::PREFIX.'.new1'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testAppend() + { + $this->builder->append(new PropertyPath('new1[new2]')); + + $path = new PropertyPath(self::PREFIX.'.new1[new2]'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testAppendUsingString() + { + $this->builder->append('new1[new2]'); + + $path = new PropertyPath(self::PREFIX.'.new1[new2]'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testAppendWithOffset() + { + $this->builder->append(new PropertyPath('new1[new2].new3'), 1); + + $path = new PropertyPath(self::PREFIX.'[new2].new3'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testAppendWithOffsetAndLength() + { + $this->builder->append(new PropertyPath('new1[new2].new3'), 1, 1); + + $path = new PropertyPath(self::PREFIX.'[new2]'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceByIndex() + { + $this->builder->replaceByIndex(1, 'new1'); + + $path = new PropertyPath('old1[new1].old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceByIndexWithoutName() + { + $this->builder->replaceByIndex(0); + + $path = new PropertyPath('[old1][old2].old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testReplaceByIndexDoesNotAllowInvalidOffsets() + { + $this->builder->replaceByIndex(6, 'new1'); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testReplaceByIndexDoesNotAllowNegativeOffsets() + { + $this->builder->replaceByIndex(-1, 'new1'); + } + + public function testReplaceByProperty() + { + $this->builder->replaceByProperty(1, 'new1'); + + $path = new PropertyPath('old1.new1.old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceByPropertyWithoutName() + { + $this->builder->replaceByProperty(1); + + $path = new PropertyPath('old1.old2.old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testReplaceByPropertyDoesNotAllowInvalidOffsets() + { + $this->builder->replaceByProperty(6, 'new1'); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testReplaceByPropertyDoesNotAllowNegativeOffsets() + { + $this->builder->replaceByProperty(-1, 'new1'); + } + + public function testReplace() + { + $this->builder->replace(1, 1, new PropertyPath('new1[new2].new3')); + + $path = new PropertyPath('old1.new1[new2].new3.old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceUsingString() + { + $this->builder->replace(1, 1, 'new1[new2].new3'); + + $path = new PropertyPath('old1.new1[new2].new3.old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceNegative() + { + $this->builder->replace(-1, 1, new PropertyPath('new1[new2].new3')); + + $path = new PropertyPath('old1[old2].old3[old4][old5].new1[new2].new3'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + /** + * @dataProvider provideInvalidOffsets + * @expectedException \OutOfBoundsException + */ + public function testReplaceDoesNotAllowInvalidOffsets($offset) + { + $this->builder->replace($offset, 1, new PropertyPath('new1[new2].new3')); + } + + public function provideInvalidOffsets() + { + return array( + array(6), + array(-7), + ); + } + + public function testReplaceWithLengthGreaterOne() + { + $this->builder->replace(0, 2, new PropertyPath('new1[new2].new3')); + + $path = new PropertyPath('new1[new2].new3.old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceSubstring() + { + $this->builder->replace(1, 1, new PropertyPath('new1[new2].new3.new4[new5]'), 1, 3); + + $path = new PropertyPath('old1[new2].new3.new4.old3[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + public function testReplaceSubstringWithLengthGreaterOne() + { + $this->builder->replace(1, 2, new PropertyPath('new1[new2].new3.new4[new5]'), 1, 3); + + $path = new PropertyPath('old1[new2].new3.new4[old4][old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + // https://github.com/symfony/symfony/issues/5605 + public function testReplaceWithLongerPath() + { + // error occurs when path contains at least two more elements + // than the builder + $path = new PropertyPath('new1.new2.new3'); + + $builder = new PropertyPathBuilder(new PropertyPath('old1')); + $builder->replace(0, 1, $path); + + $this->assertEquals($path, $builder->getPropertyPath()); + } + + public function testReplaceWithLongerPathKeepsOrder() + { + $path = new PropertyPath('new1.new2.new3'); + $expected = new PropertyPath('new1.new2.new3.old2'); + + $builder = new PropertyPathBuilder(new PropertyPath('old1.old2')); + $builder->replace(0, 1, $path); + + $this->assertEquals($expected, $builder->getPropertyPath()); + } + + public function testRemove() + { + $this->builder->remove(3); + + $path = new PropertyPath('old1[old2].old3[old5].old6'); + + $this->assertEquals($path, $this->builder->getPropertyPath()); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testRemoveDoesNotAllowInvalidOffsets() + { + $this->builder->remove(6); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testRemoveDoesNotAllowNegativeOffsets() + { + $this->builder->remove(-1); + } +} diff --git a/vendor/symfony/property-access/Tests/PropertyPathTest.php b/vendor/symfony/property-access/Tests/PropertyPathTest.php new file mode 100644 index 0000000..6c5c6b2 --- /dev/null +++ b/vendor/symfony/property-access/Tests/PropertyPathTest.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\PropertyAccess\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyAccess\PropertyPath; + +class PropertyPathTest extends TestCase +{ + public function testToString() + { + $path = new PropertyPath('reference.traversable[index].property'); + + $this->assertEquals('reference.traversable[index].property', $path->__toString()); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException + */ + public function testDotIsRequiredBeforeProperty() + { + new PropertyPath('[index]property'); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException + */ + public function testDotCannotBePresentAtTheBeginning() + { + new PropertyPath('.property'); + } + + public function providePathsContainingUnexpectedCharacters() + { + return array( + array('property.'), + array('property.['), + array('property..'), + array('property['), + array('property[['), + array('property[.'), + array('property[]'), + ); + } + + /** + * @dataProvider providePathsContainingUnexpectedCharacters + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException + */ + public function testUnexpectedCharacters($path) + { + new PropertyPath($path); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException + */ + public function testPathCannotBeEmpty() + { + new PropertyPath(''); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + */ + public function testPathCannotBeNull() + { + new PropertyPath(null); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + */ + public function testPathCannotBeFalse() + { + new PropertyPath(false); + } + + public function testZeroIsValidPropertyPath() + { + $propertyPath = new PropertyPath('0'); + + $this->assertSame('0', (string) $propertyPath); + } + + public function testGetParentWithDot() + { + $propertyPath = new PropertyPath('grandpa.parent.child'); + + $this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent()); + } + + public function testGetParentWithIndex() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent()); + } + + public function testGetParentWhenThereIsNoParent() + { + $propertyPath = new PropertyPath('path'); + + $this->assertNull($propertyPath->getParent()); + } + + public function testCopyConstructor() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + $copy = new PropertyPath($propertyPath); + + $this->assertEquals($propertyPath, $copy); + } + + public function testGetElement() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $this->assertEquals('child', $propertyPath->getElement(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetElementDoesNotAcceptInvalidIndices() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $propertyPath->getElement(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testGetElementDoesNotAcceptNegativeIndices() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $propertyPath->getElement(-1); + } + + public function testIsProperty() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $this->assertTrue($propertyPath->isProperty(1)); + $this->assertFalse($propertyPath->isProperty(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsPropertyDoesNotAcceptInvalidIndices() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $propertyPath->isProperty(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsPropertyDoesNotAcceptNegativeIndices() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $propertyPath->isProperty(-1); + } + + public function testIsIndex() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $this->assertFalse($propertyPath->isIndex(1)); + $this->assertTrue($propertyPath->isIndex(2)); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsIndexDoesNotAcceptInvalidIndices() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $propertyPath->isIndex(3); + } + + /** + * @expectedException \OutOfBoundsException + */ + public function testIsIndexDoesNotAcceptNegativeIndices() + { + $propertyPath = new PropertyPath('grandpa.parent[child]'); + + $propertyPath->isIndex(-1); + } +} diff --git a/vendor/symfony/property-access/Tests/StringUtilTest.php b/vendor/symfony/property-access/Tests/StringUtilTest.php new file mode 100644 index 0000000..86829f4 --- /dev/null +++ b/vendor/symfony/property-access/Tests/StringUtilTest.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\PropertyAccess\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyAccess\StringUtil; + +/** + * @group legacy + */ +class StringUtilTest extends TestCase +{ + public function singularifyProvider() + { + // This is only a stub to make sure the BC layer works + // Actual tests are in the Symfony Inflector component + return array( + array('axes', array('ax', 'axe', 'axis')), + ); + } + + /** + * @dataProvider singularifyProvider + */ + public function testSingularify($plural, $singular) + { + $single = StringUtil::singularify($plural); + if (\is_string($singular) && \is_array($single)) { + $this->fail("--- Expected\n`string`: ".$singular."\n+++ Actual\n`array`: ".implode(', ', $single)); + } elseif (\is_array($singular) && \is_string($single)) { + $this->fail("--- Expected\n`array`: ".implode(', ', $singular)."\n+++ Actual\n`string`: ".$single); + } + + $this->assertEquals($singular, $single); + } +} diff --git a/vendor/symfony/property-access/composer.json b/vendor/symfony/property-access/composer.json new file mode 100644 index 0000000..d6e7afb --- /dev/null +++ b/vendor/symfony/property-access/composer.json @@ -0,0 +1,41 @@ +{ + "name": "symfony/property-access", + "type": "library", + "description": "Symfony PropertyAccess Component", + "keywords": ["property", "index", "access", "object", "array", "extraction", "injection", "reflection", "property path"], + "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-php70": "~1.0", + "symfony/inflector": "~3.1|~4.0" + }, + "require-dev": { + "symfony/cache": "~3.1|~4.0" + }, + "suggest": { + "psr/cache-implementation": "To cache access methods." + }, + "autoload": { + "psr-4": { "Symfony\\Component\\PropertyAccess\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + } +} diff --git a/vendor/symfony/property-access/phpunit.xml.dist b/vendor/symfony/property-access/phpunit.xml.dist new file mode 100644 index 0000000..ebfc564 --- /dev/null +++ b/vendor/symfony/property-access/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/vendor/symfony/routing/.gitignore b/vendor/symfony/routing/.gitignore new file mode 100644 index 0000000..c49a5d8 --- /dev/null +++ b/vendor/symfony/routing/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/vendor/symfony/routing/Annotation/Route.php b/vendor/symfony/routing/Annotation/Route.php new file mode 100644 index 0000000..62ad8c8 --- /dev/null +++ b/vendor/symfony/routing/Annotation/Route.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\Routing\Annotation; + +/** + * Annotation class for @Route(). + * + * @Annotation + * @Target({"CLASS", "METHOD"}) + * + * @author Fabien Potencier + */ +class Route +{ + private $path; + private $localizedPaths = array(); + 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['localized_paths'])) { + throw new \BadMethodCallException(sprintf('Unknown property "localized_paths" on annotation "%s".', \get_class($this))); + } + + if (isset($data['value'])) { + $data[\is_array($data['value']) ? 'localized_paths' : 'path'] = $data['value']; + unset($data['value']); + } + + if (isset($data['path']) && \is_array($data['path'])) { + $data['localized_paths'] = $data['path']; + unset($data['path']); + } + + 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 setLocalizedPaths(array $localizedPaths) + { + $this->localizedPaths = $localizedPaths; + } + + public function getLocalizedPaths(): array + { + return $this->localizedPaths; + } + + 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/vendor/symfony/routing/CHANGELOG.md b/vendor/symfony/routing/CHANGELOG.md new file mode 100644 index 0000000..d088e2e --- /dev/null +++ b/vendor/symfony/routing/CHANGELOG.md @@ -0,0 +1,234 @@ +CHANGELOG +========= + +4.0.0 +----- + + * dropped support for using UTF-8 route patterns without using the `utf8` option + * dropped support for using UTF-8 route requirements without using the `utf8` option + +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/vendor/symfony/routing/CompiledRoute.php b/vendor/symfony/routing/CompiledRoute.php new file mode 100644 index 0000000..44484c0 --- /dev/null +++ b/vendor/symfony/routing/CompiledRoute.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\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(string $staticPrefix, string $regex, array $tokens, array $pathVariables, string $hostRegex = null, array $hostTokens = array(), array $hostVariables = array(), array $variables = array()) + { + $this->staticPrefix = $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) + { + $data = unserialize($serialized, array('allowed_classes' => false)); + + $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/vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php b/vendor/symfony/routing/DependencyInjection/RoutingResolverPass.php new file mode 100644 index 0000000..ab0b7d8 --- /dev/null +++ b/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\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * 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(string $resolverServiceId = 'routing.resolver', string $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/vendor/symfony/routing/Exception/ExceptionInterface.php b/vendor/symfony/routing/Exception/ExceptionInterface.php new file mode 100644 index 0000000..db76362 --- /dev/null +++ b/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/vendor/symfony/routing/Exception/InvalidParameterException.php b/vendor/symfony/routing/Exception/InvalidParameterException.php new file mode 100644 index 0000000..94d841f --- /dev/null +++ b/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/vendor/symfony/routing/Exception/MethodNotAllowedException.php b/vendor/symfony/routing/Exception/MethodNotAllowedException.php new file mode 100644 index 0000000..22ca6c3 --- /dev/null +++ b/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, string $message = null, int $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/vendor/symfony/routing/Exception/MissingMandatoryParametersException.php b/vendor/symfony/routing/Exception/MissingMandatoryParametersException.php new file mode 100644 index 0000000..57f3a40 --- /dev/null +++ b/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/vendor/symfony/routing/Exception/NoConfigurationException.php b/vendor/symfony/routing/Exception/NoConfigurationException.php new file mode 100644 index 0000000..333bc74 --- /dev/null +++ b/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/vendor/symfony/routing/Exception/ResourceNotFoundException.php b/vendor/symfony/routing/Exception/ResourceNotFoundException.php new file mode 100644 index 0000000..ccbca15 --- /dev/null +++ b/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/vendor/symfony/routing/Exception/RouteNotFoundException.php b/vendor/symfony/routing/Exception/RouteNotFoundException.php new file mode 100644 index 0000000..24ab0b4 --- /dev/null +++ b/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/vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php b/vendor/symfony/routing/Generator/ConfigurableRequirementsInterface.php new file mode 100644 index 0000000..dc97b7e --- /dev/null +++ b/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/vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php b/vendor/symfony/routing/Generator/Dumper/GeneratorDumper.php new file mode 100644 index 0000000..659c5ba --- /dev/null +++ b/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/vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php b/vendor/symfony/routing/Generator/Dumper/GeneratorDumperInterface.php new file mode 100644 index 0000000..fed3472 --- /dev/null +++ b/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/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php b/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.php new file mode 100644 index 0000000..97e0335 --- /dev/null +++ b/vendor/symfony/routing/Generator/Dumper/PhpGeneratorDumper.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\Routing\Generator\Dumper; + +use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; + +/** + * 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; + \$this->defaultLocale = \$defaultLocale; + 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, PhpMatcherDumper::export($properties)); + } + $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) + { + $locale = $parameters['_locale'] + ?? $this->context->getParameter('_locale') + ?: $this->defaultLocale; + + if (null !== $locale && (self::$declaredRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) { + unset($parameters['_locale']); + $name .= '.'.$locale; + } elseif (!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/vendor/symfony/routing/Generator/UrlGenerator.php b/vendor/symfony/routing/Generator/UrlGenerator.php new file mode 100644 index 0000000..38dd138 --- /dev/null +++ b/vendor/symfony/routing/Generator/UrlGenerator.php @@ -0,0 +1,330 @@ + + * + * 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 Psr\Log\LoggerInterface; +use Symfony\Component\Routing\Exception\InvalidParameterException; +use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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; + + private $defaultLocale; + + /** + * 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, string $defaultLocale = null) + { + $this->routes = $routes; + $this->context = $context; + $this->logger = $logger; + $this->defaultLocale = $defaultLocale; + } + + /** + * {@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) + { + $locale = $parameters['_locale'] + ?? $this->context->getParameter('_locale') + ?: $this->defaultLocale; + + if (null !== $locale && null !== ($route = $this->routes->get($name.'.'.$locale)) && $route->getDefault('_canonical_route') === $name) { + unset($parameters['_locale']); + } elseif (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/vendor/symfony/routing/Generator/UrlGeneratorInterface.php b/vendor/symfony/routing/Generator/UrlGeneratorInterface.php new file mode 100644 index 0000000..d6e7938 --- /dev/null +++ b/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/vendor/symfony/routing/LICENSE b/vendor/symfony/routing/LICENSE new file mode 100644 index 0000000..21d7fb9 --- /dev/null +++ b/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/vendor/symfony/routing/Loader/AnnotationClassLoader.php b/vendor/symfony/routing/Loader/AnnotationClassLoader.php new file mode 100644 index 0000000..958217f --- /dev/null +++ b/vendor/symfony/routing/Loader/AnnotationClassLoader.php @@ -0,0 +1,319 @@ + + * + * 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\Loader\LoaderInterface; +use Symfony\Component\Config\Loader\LoaderResolverInterface; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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')) { + foreach ($this->reader->getClassAnnotations($class) as $annot) { + if ($annot instanceof $this->routeAnnotationClass) { + $globals['path'] = ''; + $globals['name'] = ''; + $globals['localized_paths'] = array(); + + $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()); + $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']; + } + + $path = $annot->getLocalizedPaths() ?: $annot->getPath(); + $prefix = $globals['localized_paths'] ?: $globals['path']; + $paths = array(); + + if (\is_array($path)) { + if (!\is_array($prefix)) { + foreach ($path as $locale => $localePath) { + $paths[$locale] = $prefix.$localePath; + } + } elseif ($missing = array_diff_key($prefix, $path)) { + throw new \LogicException(sprintf('Route to "%s" is missing paths for locale(s) "%s".', $class->name.'::'.$method->name, implode('", "', array_keys($missing)))); + } else { + foreach ($path as $locale => $localePath) { + if (!isset($prefix[$locale])) { + throw new \LogicException(sprintf('Route to "%s" with locale "%s" is missing a corresponding prefix in class "%s".', $method->name, $locale, $class->name)); + } + + $paths[$locale] = $prefix[$locale].$localePath; + } + } + } elseif (\is_array($prefix)) { + foreach ($prefix as $locale => $localePrefix) { + $paths[$locale] = $localePrefix.$path; + } + } else { + $paths[] = $prefix.$path; + } + + foreach ($method->getParameters() as $param) { + if (isset($defaults[$param->name]) || !$param->isDefaultValueAvailable()) { + continue; + } + foreach ($paths as $locale => $path) { + if (false !== strpos($path, sprintf('{%s}', $param->name))) { + $defaults[$param->name] = $param->getDefaultValue(); + break; + } + } + } + + foreach ($paths as $locale => $path) { + $route = $this->createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition); + $this->configureRoute($route, $class, $method, $annot); + if (0 !== $locale) { + $route->setDefault('_locale', $locale); + $route->setDefault('_canonical_route', $name); + $collection->add($name.'.'.$locale, $route); + } else { + $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' => null, + 'localized_paths' => array(), + '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(); + } + + $globals['localized_paths'] = $annot->getLocalizedPaths(); + + 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/vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php b/vendor/symfony/routing/Loader/AnnotationDirectoryLoader.php new file mode 100644 index 0000000..3fb70ea --- /dev/null +++ b/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\Config\Resource\DirectoryResource; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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/vendor/symfony/routing/Loader/AnnotationFileLoader.php b/vendor/symfony/routing/Loader/AnnotationFileLoader.php new file mode 100644 index 0000000..ecd68e0 --- /dev/null +++ b/vendor/symfony/routing/Loader/AnnotationFileLoader.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\Loader; + +use Symfony\Component\Config\FileLocatorInterface; +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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)); + } + + // 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/vendor/symfony/routing/Loader/ClosureLoader.php b/vendor/symfony/routing/Loader/ClosureLoader.php new file mode 100644 index 0000000..5df9f6a --- /dev/null +++ b/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/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php b/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php new file mode 100644 index 0000000..e1de75e --- /dev/null +++ b/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.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\Configurator; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Nicolas Grekas + */ +class CollectionConfigurator +{ + use Traits\AddTrait; + use Traits\RouteTrait; + + private $parent; + private $parentConfigurator; + private $parentPrefixes; + + public function __construct(RouteCollection $parent, string $name, self $parentConfigurator = null, array $parentPrefixes = null) + { + $this->parent = $parent; + $this->name = $name; + $this->collection = new RouteCollection(); + $this->route = new Route(''); + $this->parentConfigurator = $parentConfigurator; // for GC control + $this->parentPrefixes = $parentPrefixes; + } + + public function __destruct() + { + if (null === $this->prefixes) { + $this->collection->addPrefix($this->route->getPath()); + } + + $this->parent->addCollection($this->collection); + } + + /** + * Creates a sub-collection. + * + * @return self + */ + final public function collection($name = '') + { + return new self($this->collection, $this->name.$name, $this, $this->prefixes); + } + + /** + * Sets the prefix to add to the path of all child routes. + * + * @param string|array $prefix the prefix, or the localized prefixes + * + * @return $this + */ + final public function prefix($prefix) + { + if (\is_array($prefix)) { + if (null === $this->parentPrefixes) { + // no-op + } elseif ($missing = array_diff_key($this->parentPrefixes, $prefix)) { + throw new \LogicException(sprintf('Collection "%s" is missing prefixes for locale(s) "%s".', $this->name, implode('", "', array_keys($missing)))); + } else { + foreach ($prefix as $locale => $localePrefix) { + if (!isset($this->parentPrefixes[$locale])) { + throw new \LogicException(sprintf('Collection "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $this->name, $locale)); + } + + $prefix[$locale] = $this->parentPrefixes[$locale].$localePrefix; + } + } + $this->prefixes = $prefix; + $this->route->setPath('/'); + } else { + $this->prefixes = null; + $this->route->setPath($prefix); + } + + return $this; + } + + private function createRoute($path): Route + { + return (clone $this->route)->setPath($path); + } +} diff --git a/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php b/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php new file mode 100644 index 0000000..92e7efd --- /dev/null +++ b/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.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\Configurator; + +use Symfony\Component\Routing\Route; +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|array $prefix the prefix, or the localized prefixes + * + * @return $this + */ + final public function prefix($prefix, bool $trailingSlashOnRoot = true) + { + if (!\is_array($prefix)) { + $this->route->addPrefix($prefix); + if (!$trailingSlashOnRoot) { + $rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath(); + foreach ($this->route->all() as $route) { + if ($route->getPath() === $rootPath) { + $route->setPath(rtrim($rootPath, '/')); + } + } + } + } else { + foreach ($prefix as $locale => $localePrefix) { + $prefix[$locale] = trim(trim($localePrefix), '/'); + } + foreach ($this->route->all() as $name => $route) { + if (null === $locale = $route->getDefault('_locale')) { + $this->route->remove($name); + foreach ($prefix as $locale => $localePrefix) { + $localizedRoute = clone $route; + $localizedRoute->setDefault('_locale', $locale); + $localizedRoute->setDefault('_canonical_route', $name); + $localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); + $this->route->add($name.'.'.$locale, $localizedRoute); + } + } elseif (!isset($prefix[$locale])) { + throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale)); + } else { + $route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); + $this->route->add($name, $route); + } + } + } + + return $this; + } + + /** + * Sets the prefix to add to the name of all child routes. + * + * @return $this + */ + final public function namePrefix(string $namePrefix) + { + $this->route->addNamePrefix($namePrefix); + + return $this; + } +} diff --git a/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php b/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.php new file mode 100644 index 0000000..e700f8d --- /dev/null +++ b/vendor/symfony/routing/Loader/Configurator/RouteConfigurator.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\Loader\Configurator; + +use Symfony\Component\Routing\RouteCollection; + +/** + * @author Nicolas Grekas + */ +class RouteConfigurator +{ + use Traits\AddTrait; + use Traits\RouteTrait; + + private $parentConfigurator; + + public function __construct(RouteCollection $collection, $route, string $name = '', CollectionConfigurator $parentConfigurator = null, array $prefixes = null) + { + $this->collection = $collection; + $this->route = $route; + $this->name = $name; + $this->parentConfigurator = $parentConfigurator; // for GC control + $this->prefixes = $prefixes; + } +} diff --git a/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php b/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php new file mode 100644 index 0000000..a315cfb --- /dev/null +++ b/vendor/symfony/routing/Loader/Configurator/RoutingConfigurator.php @@ -0,0 +1,62 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\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, string $path, string $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)); + $imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file); + if (!\is_array($imported)) { + return new ImportConfigurator($this->collection, $imported); + } + + $mergedCollection = new RouteCollection(); + foreach ($imported as $subCollection) { + $mergedCollection->addCollection($subCollection); + } + + return new ImportConfigurator($this->collection, $mergedCollection); + } + + /** + * @return CollectionConfigurator + */ + final public function collection($name = '') + { + return new CollectionConfigurator($this->collection, $name); + } +} diff --git a/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php b/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.php new file mode 100644 index 0000000..57dd71f --- /dev/null +++ b/vendor/symfony/routing/Loader/Configurator/Traits/AddTrait.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\Routing\Loader\Configurator\Traits; + +use Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator; +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 = ''; + + private $prefixes; + + /** + * Adds a route. + * + * @param string|array $path the path, or the localized paths of the route + */ + final public function add(string $name, $path): RouteConfigurator + { + $paths = array(); + $parentConfigurator = $this instanceof CollectionConfigurator ? $this : ($this instanceof RouteConfigurator ? $this->parentConfigurator : null); + + if (\is_array($path)) { + if (null === $this->prefixes) { + $paths = $path; + } elseif ($missing = array_diff_key($this->prefixes, $path)) { + throw new \LogicException(sprintf('Route "%s" is missing routes for locale(s) "%s".', $name, implode('", "', array_keys($missing)))); + } else { + foreach ($path as $locale => $localePath) { + if (!isset($this->prefixes[$locale])) { + throw new \LogicException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale)); + } + + $paths[$locale] = $this->prefixes[$locale].$localePath; + } + } + } elseif (null !== $this->prefixes) { + foreach ($this->prefixes as $locale => $prefix) { + $paths[$locale] = $prefix.$path; + } + } else { + $this->collection->add($this->name.$name, $route = $this->createRoute($path)); + + return new RouteConfigurator($this->collection, $route, $this->name, $parentConfigurator, $this->prefixes); + } + + $routes = new RouteCollection(); + + foreach ($paths as $locale => $path) { + $routes->add($name.'.'.$locale, $route = $this->createRoute($path)); + $this->collection->add($this->name.$name.'.'.$locale, $route); + $route->setDefault('_locale', $locale); + $route->setDefault('_canonical_route', $this->name.$name); + } + + return new RouteConfigurator($this->collection, $routes, $this->name, $parentConfigurator, $this->prefixes); + } + + /** + * Adds a route. + * + * @param string|array $path the path, or the localized paths of the route + */ + final public function __invoke(string $name, $path): RouteConfigurator + { + return $this->add($name, $path); + } + + private function createRoute($path): Route + { + return new Route($path); + } +} diff --git a/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php b/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.php new file mode 100644 index 0000000..3613f25 --- /dev/null +++ b/vendor/symfony/routing/Loader/Configurator/Traits/RouteTrait.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\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. + * + * @return $this + */ + final public function condition(string $condition) + { + $this->route->setCondition($condition); + + return $this; + } + + /** + * Sets the pattern for the host. + * + * @return $this + */ + final public function host(string $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/vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php b/vendor/symfony/routing/Loader/DependencyInjection/ServiceRouterLoader.php new file mode 100644 index 0000000..6c16216 --- /dev/null +++ b/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/vendor/symfony/routing/Loader/DirectoryLoader.php b/vendor/symfony/routing/Loader/DirectoryLoader.php new file mode 100644 index 0000000..08e833e --- /dev/null +++ b/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\Config\Resource\DirectoryResource; +use Symfony\Component\Routing\RouteCollection; + +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/vendor/symfony/routing/Loader/GlobFileLoader.php b/vendor/symfony/routing/Loader/GlobFileLoader.php new file mode 100644 index 0000000..03ee341 --- /dev/null +++ b/vendor/symfony/routing/Loader/GlobFileLoader.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\Routing\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Routing\RouteCollection; + +/** + * GlobFileLoader loads files from a glob pattern. + * + * @author Nicolas Grekas + */ +class GlobFileLoader extends FileLoader +{ + /** + * {@inheritdoc} + */ + public function load($resource, $type = null) + { + $collection = new RouteCollection(); + + foreach ($this->glob($resource, false, $globResource) as $path => $info) { + $collection->addCollection($this->import($path)); + } + + $collection->addResource($globResource); + + return $collection; + } + + /** + * {@inheritdoc} + */ + public function supports($resource, $type = null) + { + return 'glob' === $type; + } +} diff --git a/vendor/symfony/routing/Loader/ObjectRouteLoader.php b/vendor/symfony/routing/Loader/ObjectRouteLoader.php new file mode 100644 index 0000000..dd14487 --- /dev/null +++ b/vendor/symfony/routing/Loader/ObjectRouteLoader.php @@ -0,0 +1,100 @@ + + * + * 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) + { + if (1 === substr_count($resource, ':')) { + $resource = str_replace(':', '::', $resource); + @trigger_error(sprintf('Referencing service route loaders with a single colon is deprecated since Symfony 4.1. Use %s instead.', $resource), E_USER_DEPRECATED); + } + + $parts = explode('::', $resource); + if (2 != \count($parts)) { + throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the "service" route loader: use the format "service::method"', $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 (!\is_callable(array($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/vendor/symfony/routing/Loader/PhpFileLoader.php b/vendor/symfony/routing/Loader/PhpFileLoader.php new file mode 100644 index 0000000..8d7b0cb --- /dev/null +++ b/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 (\is_object($result) && \is_callable($result)) { + $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/vendor/symfony/routing/Loader/XmlFileLoader.php b/vendor/symfony/routing/Loader/XmlFileLoader.php new file mode 100644 index 0000000..54b0c65 --- /dev/null +++ b/vendor/symfony/routing/Loader/XmlFileLoader.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\Routing\Loader; + +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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')) { + throw new \InvalidArgumentException(sprintf('The element in file "%s" must have an "id" 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, $paths) = $this->parseConfigs($node, $path); + + if (!$paths && '' === $node->getAttribute('path')) { + throw new \InvalidArgumentException(sprintf('The element in file "%s" must have a "path" attribute or child nodes.', $path)); + } + + if ($paths && '' !== $node->getAttribute('path')) { + throw new \InvalidArgumentException(sprintf('The element in file "%s" must not have both a "path" attribute and child nodes.', $path)); + } + + if (!$paths) { + $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); + $collection->add($id, $route); + } else { + foreach ($paths as $locale => $p) { + $defaults['_locale'] = $locale; + $defaults['_canonical_route'] = $id; + $route = new Route($p, $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); + $collection->add($id.'.'.$locale, $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; + $trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true; + + list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes) = $this->parseConfigs($node, $path); + + if ('' !== $prefix && $prefixes) { + throw new \InvalidArgumentException(sprintf('The element in file "%s" must not have both a "prefix" attribute and child nodes.', $path)); + } + + $this->setCurrentDir(\dirname($path)); + + $imported = $this->import($resource, ('' !== $type ? $type : null), false, $file); + + if (!\is_array($imported)) { + $imported = array($imported); + } + + foreach ($imported as $subCollection) { + /* @var $subCollection RouteCollection */ + if ('' !== $prefix || !$prefixes) { + $subCollection->addPrefix($prefix); + if (!$trailingSlashOnRoot) { + $rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath(); + foreach ($subCollection->all() as $route) { + if ($route->getPath() === $rootPath) { + $route->setPath(rtrim($rootPath, '/')); + } + } + } + } else { + foreach ($prefixes as $locale => $localePrefix) { + $prefixes[$locale] = trim(trim($localePrefix), '/'); + } + foreach ($subCollection->all() as $name => $route) { + if (null === $locale = $route->getDefault('_locale')) { + $subCollection->remove($name); + foreach ($prefixes as $locale => $localePrefix) { + $localizedRoute = clone $route; + $localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); + $localizedRoute->setDefault('_locale', $locale); + $localizedRoute->setDefault('_canonical_route', $name); + $subCollection->add($name.'.'.$locale, $localizedRoute); + } + } elseif (!isset($prefixes[$locale])) { + throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $path)); + } else { + $route->setPath($prefixes[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); + $subCollection->add($name, $route); + } + } + } + + 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); + + if ($namePrefix = $node->getAttribute('name-prefix')) { + $subCollection->addNamePrefix($namePrefix); + } + + $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; + $prefixes = array(); + $paths = array(); + + foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) { + if ($node !== $n->parentNode) { + continue; + } + + switch ($n->localName) { + case 'path': + $paths[$n->getAttribute('locale')] = trim($n->textContent); + break; + case 'prefix': + $prefixes[$n->getAttribute('locale')] = trim($n->textContent); + break; + 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, $paths, $prefixes); + } + + /** + * 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/vendor/symfony/routing/Loader/YamlFileLoader.php b/vendor/symfony/routing/Loader/YamlFileLoader.php new file mode 100644 index 0000000..bd35c75 --- /dev/null +++ b/vendor/symfony/routing/Loader/YamlFileLoader.php @@ -0,0 +1,262 @@ + + * + * 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\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Parser as YamlParser; +use Symfony\Component\Yaml\Yaml; + +/** + * 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', 'name_prefix', 'trailing_slash_on_root', + ); + 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(); + } + + try { + $parsedConfig = $this->yamlParser->parseFile($path, Yaml::PARSE_CONSTANT); + } catch (ParseException $e) { + throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML.', $path), 0, $e); + } + + $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']; + } + + if (\is_array($config['path'])) { + $route = new Route('', $defaults, $requirements, $options, $host, $schemes, $methods, $condition); + + foreach ($config['path'] as $locale => $path) { + $localizedRoute = clone $route; + $localizedRoute->setDefault('_locale', $locale); + $localizedRoute->setDefault('_canonical_route', $name); + $localizedRoute->setPath($path); + $collection->add($name.'.'.$locale, $localizedRoute); + } + } else { + $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; + $trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true; + + if (isset($config['controller'])) { + $defaults['_controller'] = $config['controller']; + } + + $this->setCurrentDir(\dirname($path)); + + $imported = $this->import($config['resource'], $type, false, $file); + + if (!\is_array($imported)) { + $imported = array($imported); + } + + foreach ($imported as $subCollection) { + /* @var $subCollection RouteCollection */ + if (!\is_array($prefix)) { + $subCollection->addPrefix($prefix); + if (!$trailingSlashOnRoot) { + $rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath(); + foreach ($subCollection->all() as $route) { + if ($route->getPath() === $rootPath) { + $route->setPath(rtrim($rootPath, '/')); + } + } + } + } else { + foreach ($prefix as $locale => $localePrefix) { + $prefix[$locale] = trim(trim($localePrefix), '/'); + } + foreach ($subCollection->all() as $name => $route) { + if (null === $locale = $route->getDefault('_locale')) { + $subCollection->remove($name); + foreach ($prefix as $locale => $localePrefix) { + $localizedRoute = clone $route; + $localizedRoute->setDefault('_locale', $locale); + $localizedRoute->setDefault('_canonical_route', $name); + $localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); + $subCollection->add($name.'.'.$locale, $localizedRoute); + } + } elseif (!isset($prefix[$locale])) { + throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $file)); + } else { + $route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); + $subCollection->add($name, $route); + } + } + } + + 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); + + if (isset($config['name_prefix'])) { + $subCollection->addNamePrefix($config['name_prefix']); + } + + $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/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd b/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd new file mode 100644 index 0000000..1ea4651 --- /dev/null +++ b/vendor/symfony/routing/Loader/schema/routing/routing-1.0.xsd @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php b/vendor/symfony/routing/Matcher/Dumper/MatcherDumper.php new file mode 100644 index 0000000..ea51ab4 --- /dev/null +++ b/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/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php b/vendor/symfony/routing/Matcher/Dumper/MatcherDumperInterface.php new file mode 100644 index 0000000..5e7c134 --- /dev/null +++ b/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/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php b/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php new file mode 100644 index 0000000..14e5cc3 --- /dev/null +++ b/vendor/symfony/routing/Matcher/Dumper/PhpMatcherDumper.php @@ -0,0 +1,778 @@ + + * + * 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\ExpressionLanguage\ExpressionFunctionProviderInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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 + * @author Nicolas Grekas + */ +class PhpMatcherDumper extends MatcherDumper +{ + private $expressionLanguage; + private $signalingException; + + /** + * @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[RedirectableUrlMatcherInterface::class]); + + return <<context = \$context; + } + +{$this->generateMatchMethod($supportsRedirections)} +} + +EOF; + } + + public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) + { + $this->expressionLanguageProviders[] = $provider; + } + + /** + * Generates the code for the match method implementing UrlMatcherInterface. + */ + private function generateMatchMethod(bool $supportsRedirections): string + { + // Group hosts by same-suffix, re-order when possible + $matchHost = false; + $routes = new StaticPrefixCollection(); + foreach ($this->getRoutes()->all() as $name => $route) { + if ($host = $route->getHost()) { + $matchHost = true; + $host = '/'.strtr(strrev($host), '}.{', '(/)'); + } + + $routes->addRoute($host ?: '/(.*)', array($name, $route)); + } + $routes = $matchHost ? $routes->populateCollection(new RouteCollection()) : $this->getRoutes(); + + $code = rtrim($this->compileRoutes($routes, $matchHost), "\n"); + $fetchHost = $matchHost ? " \$host = strtolower(\$context->getHost());\n" : ''; + + $code = <<context; + \$requestMethod = \$canonicalMethod = \$context->getMethod(); +{$fetchHost} + if ('HEAD' === \$requestMethod) { + \$canonicalMethod = 'GET'; + } + +$code + +EOF; + + if ($supportsRedirections) { + return <<<'EOF' + public function match($pathinfo) + { + $allow = $allowSchemes = array(); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $ret; + } + if ($allow) { + throw new MethodNotAllowedException(array_keys($allow)); + } + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) { + // no-op + } elseif ($allowSchemes) { + redirect_scheme: + $scheme = $this->context->getScheme(); + $this->context->setScheme(key($allowSchemes)); + try { + if ($ret = $this->doMatch($pathinfo)) { + return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret; + } + } finally { + $this->context->setScheme($scheme); + } + } elseif ('/' !== $pathinfo) { + $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $this->redirect($pathinfo, $ret['_route']) + $ret; + } + if ($allowSchemes) { + goto redirect_scheme; + } + } + + throw new ResourceNotFoundException(); + } + + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + +EOF + .$code."\n return null;\n }"; + } + + return " public function match(\$rawPathinfo)\n".$code."\n throw \$allow ? new MethodNotAllowedException(array_keys(\$allow)) : new ResourceNotFoundException();\n }"; + } + + /** + * Generates PHP code to match a RouteCollection with all its routes. + */ + private function compileRoutes(RouteCollection $routes, bool $matchHost): string + { + list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes); + + $code = $this->compileStaticRoutes($staticRoutes, $matchHost); + $chunkLimit = \count($dynamicRoutes); + + while (true) { + try { + $this->signalingException = new \RuntimeException('preg_match(): Compilation failed: regular expression is too large'); + $code .= $this->compileDynamicRoutes($dynamicRoutes, $matchHost, $chunkLimit); + break; + } catch (\Exception $e) { + if (1 < $chunkLimit && $this->signalingException === $e) { + $chunkLimit = 1 + ($chunkLimit >> 1); + continue; + } + throw $e; + } + } + + // used to display the Welcome Page in apps that don't define a homepage + $code .= " if ('/' === \$pathinfo && !\$allow && !\$allowSchemes) {\n"; + $code .= " throw new Symfony\Component\Routing\Exception\NoConfigurationException();\n"; + $code .= " }\n"; + + return $code; + } + + /** + * Splits static routes from dynamic routes, so that they can be matched first, using a simple switch. + */ + private function groupStaticRoutes(RouteCollection $collection): array + { + $staticRoutes = $dynamicRegex = array(); + $dynamicRoutes = new RouteCollection(); + + foreach ($collection->all() as $name => $route) { + $compiledRoute = $route->compile(); + $hostRegex = $compiledRoute->getHostRegex(); + $regex = $compiledRoute->getRegex(); + if (!$compiledRoute->getPathVariables()) { + $host = !$compiledRoute->getHostVariables() ? $route->getHost() : ''; + $url = $route->getPath(); + foreach ($dynamicRegex as list($hostRx, $rx)) { + if (preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) { + $dynamicRegex[] = array($hostRegex, $regex); + $dynamicRoutes->add($name, $route); + continue 2; + } + } + + $staticRoutes[$url][$name] = $route; + } else { + $dynamicRegex[] = array($hostRegex, $regex); + $dynamicRoutes->add($name, $route); + } + } + + return array($staticRoutes, $dynamicRoutes); + } + + /** + * Compiles static routes in a switch statement. + * + * Condition-less paths are put in a static array in the switch's default, with generic matching logic. + * Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases. + * + * @throws \LogicException + */ + private function compileStaticRoutes(array $staticRoutes, bool $matchHost): string + { + if (!$staticRoutes) { + return ''; + } + $code = $default = ''; + + foreach ($staticRoutes as $url => $routes) { + if (1 === \count($routes)) { + foreach ($routes as $name => $route) { + } + + if (!$route->getCondition()) { + $defaults = $route->getDefaults(); + if (isset($defaults['_canonical_route'])) { + $name = $defaults['_canonical_route']; + unset($defaults['_canonical_route']); + } + $default .= sprintf( + "%s => array(%s, %s, %s, %s),\n", + self::export($url), + self::export(array('_route' => $name) + $defaults), + self::export(!$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex() ?: null), + self::export(array_flip($route->getMethods()) ?: null), + self::export(array_flip($route->getSchemes()) ?: null) + ); + continue; + } + } + + $code .= sprintf(" case %s:\n", self::export($url)); + foreach ($routes as $name => $route) { + $code .= $this->compileRoute($route, $name, true); + } + $code .= " break;\n"; + } + + if ($default) { + $code .= <<indent($default, 4)} ); + + if (!isset(\$routes[\$pathinfo])) { + break; + } + list(\$ret, \$requiredHost, \$requiredMethods, \$requiredSchemes) = \$routes[\$pathinfo]; +{$this->compileSwitchDefault(false, $matchHost)} +EOF; + } + + return sprintf(" switch (\$pathinfo) {\n%s }\n\n", $this->indent($code)); + } + + /** + * Compiles a regular expression followed by a switch statement to match dynamic routes. + * + * The regular expression matches both the host and the pathinfo at the same time. For stellar performance, + * it is built as a tree of patterns, with re-ordering logic to group same-prefix routes together when possible. + * + * Patterns are named so that we know which one matched (https://pcre.org/current/doc/html/pcre2syntax.html#SEC23). + * This name is used to "switch" to the additional logic required to match the final route. + * + * Condition-less paths are put in a static array in the switch's default, with generic matching logic. + * Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases. + * + * Last but not least: + * - Because it is not possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated. + * - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the + * matching-but-failing subpattern is blacklisted by replacing its name by "(*F)", which forces a failure-to-match. + * To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur. + */ + private function compileDynamicRoutes(RouteCollection $collection, bool $matchHost, int $chunkLimit): string + { + if (!$collection->all()) { + return ''; + } + $code = ''; + $state = (object) array( + 'regex' => '', + 'switch' => '', + 'default' => '', + 'mark' => 0, + 'markTail' => 0, + 'hostVars' => array(), + 'vars' => array(), + ); + $state->getVars = static function ($m) use ($state) { + if ('_route' === $m[1]) { + return '?:'; + } + + $state->vars[] = $m[1]; + + return ''; + }; + + $chunkSize = 0; + $prev = null; + $perModifiers = array(); + foreach ($collection->all() as $name => $route) { + preg_match('#[a-zA-Z]*$#', $route->compile()->getRegex(), $rx); + if ($chunkLimit < ++$chunkSize || $prev !== $rx[0] && $route->compile()->getPathVariables()) { + $chunkSize = 1; + $routes = new RouteCollection(); + $perModifiers[] = array($rx[0], $routes); + $prev = $rx[0]; + } + $routes->add($name, $route); + } + + foreach ($perModifiers as list($modifiers, $routes)) { + $prev = false; + $perHost = array(); + foreach ($routes->all() as $name => $route) { + $regex = $route->compile()->getHostRegex(); + if ($prev !== $regex) { + $routes = new RouteCollection(); + $perHost[] = array($regex, $routes); + $prev = $regex; + } + $routes->add($name, $route); + } + $prev = false; + $rx = '{^(?'; + $code .= "\n {$state->mark} => ".self::export($rx); + $state->mark += \strlen($rx); + $state->regex = $rx; + + foreach ($perHost as list($hostRegex, $routes)) { + if ($matchHost) { + if ($hostRegex) { + preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx); + $state->vars = array(); + $hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.'; + $state->hostVars = $state->vars; + } else { + $hostRegex = '(?:(?:[^./]*+\.)++)'; + $state->hostVars = array(); + } + $state->mark += \strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?"); + $code .= "\n .".self::export($rx); + $state->regex .= $rx; + $prev = true; + } + + $tree = new StaticPrefixCollection(); + foreach ($routes->all() as $name => $route) { + preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $route->compile()->getRegex(), $rx); + + $state->vars = array(); + $regex = preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]); + $tree->addRoute($regex, array($name, $regex, $state->vars, $route)); + } + + $code .= $this->compileStaticPrefixCollection($tree, $state); + } + if ($matchHost) { + $code .= "\n .')'"; + $state->regex .= ')'; + } + $rx = ")$}{$modifiers}"; + $code .= "\n .'{$rx}',"; + $state->regex .= $rx; + $state->markTail = 0; + + // if the regex is too large, throw a signaling exception to recompute with smaller chunk size + set_error_handler(function ($type, $message) { throw 0 === strpos($message, $this->signalingException->getMessage()) ? $this->signalingException : new \ErrorException($message); }); + try { + preg_match($state->regex, ''); + } finally { + restore_error_handler(); + } + } + + if ($state->default) { + $state->switch .= <<indent($state->default, 4)} ); + + list(\$ret, \$vars, \$requiredMethods, \$requiredSchemes) = \$routes[\$m]; +{$this->compileSwitchDefault(true, $matchHost)} +EOF; + } + + $matchedPathinfo = $matchHost ? '$host.\'.\'.$pathinfo' : '$pathinfo'; + unset($state->getVars); + + return << \$regex) { + while (preg_match(\$regex, \$matchedPathinfo, \$matches)) { + switch (\$m = (int) \$matches['MARK']) { +{$this->indent($state->switch, 3)} } + + if ({$state->mark} === \$m) { + break; + } + \$regex = substr_replace(\$regex, 'F', \$m - \$offset, 1 + strlen(\$m)); + \$offset += strlen(\$m); + } + } + +EOF; + } + + /** + * Compiles a regexp tree of subpatterns that matches nested same-prefix routes. + * + * @param \stdClass $state A simple state object that keeps track of the progress of the compilation, + * and gathers the generated switch's "case" and "default" statements + */ + private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \stdClass $state, int $prefixLen = 0): string + { + $code = ''; + $prevRegex = null; + $routes = $tree->getRoutes(); + + foreach ($routes as $i => $route) { + if ($route instanceof StaticPrefixCollection) { + $prevRegex = null; + $prefix = substr($route->getPrefix(), $prefixLen); + $state->mark += \strlen($rx = "|{$prefix}(?"); + $code .= "\n .".self::export($rx); + $state->regex .= $rx; + $code .= $this->indent($this->compileStaticPrefixCollection($route, $state, $prefixLen + \strlen($prefix))); + $code .= "\n .')'"; + $state->regex .= ')'; + ++$state->markTail; + continue; + } + + list($name, $regex, $vars, $route) = $route; + $compiledRoute = $route->compile(); + + if ($compiledRoute->getRegex() === $prevRegex) { + $state->switch = substr_replace($state->switch, $this->compileRoute($route, $name, false)."\n", -19, 0); + continue; + } + + $state->mark += 3 + $state->markTail + \strlen($regex) - $prefixLen; + $state->markTail = 2 + \strlen($state->mark); + $rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark); + $code .= "\n .".self::export($rx); + $state->regex .= $rx; + $vars = array_merge($state->hostVars, $vars); + + if (!$route->getCondition() && (!\is_array($next = $routes[1 + $i] ?? null) || $regex !== $next[1])) { + $prevRegex = null; + $defaults = $route->getDefaults(); + if (isset($defaults['_canonical_route'])) { + $name = $defaults['_canonical_route']; + unset($defaults['_canonical_route']); + } + $state->default .= sprintf( + "%s => array(%s, %s, %s, %s),\n", + $state->mark, + self::export(array('_route' => $name) + $defaults), + self::export($vars), + self::export(array_flip($route->getMethods()) ?: null), + self::export(array_flip($route->getSchemes()) ?: null) + ); + } else { + $prevRegex = $compiledRoute->getRegex(); + $combine = ' $matches = array('; + foreach ($vars as $j => $m) { + $combine .= sprintf('%s => $matches[%d] ?? null, ', self::export($m), 1 + $j); + } + $combine = $vars ? substr_replace($combine, ");\n\n", -2) : ''; + + $state->switch .= <<mark}: +{$combine}{$this->compileRoute($route, $name, false)} + break; + +EOF; + } + } + + return $code; + } + + /** + * A simple helper to compiles the switch's "default" for both static and dynamic routes. + */ + private function compileSwitchDefault(bool $hasVars, bool $matchHost): string + { + if ($hasVars) { + $code = << \$v) { + if (isset(\$matches[1 + \$i])) { + \$ret[\$v] = \$matches[1 + \$i]; + } + } + +EOF; + } elseif ($matchHost) { + $code = <<mergeDefaults(\$hostMatches, \$ret); + } + } + +EOF; + } else { + $code = ''; + } + + $code .= <<getScheme()]); + if (\$requiredMethods && !isset(\$requiredMethods[\$canonicalMethod]) && !isset(\$requiredMethods[\$requestMethod])) { + if (\$hasRequiredScheme) { + \$allow += \$requiredMethods; + } + break; + } + if (!\$hasRequiredScheme) { + \$allowSchemes += \$requiredSchemes; + break; + } + + return \$ret; + +EOF; + + return $code; + } + + /** + * Compiles a single Route to PHP code used to match it against the path info. + * + * @throws \LogicException + */ + private function compileRoute(Route $route, string $name, bool $checkHost): string + { + $code = ''; + $compiledRoute = $route->compile(); + $conditions = array(); + $matches = (bool) $compiledRoute->getPathVariables(); + $hostMatches = (bool) $compiledRoute->getHostVariables(); + $methods = array_flip($route->getMethods()); + + if ($route->getCondition()) { + $expression = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request')); + + if (false !== strpos($expression, '$request')) { + $conditions[] = '($request = $request ?? $this->request ?: $this->createRequest($pathinfo))'; + } + $conditions[] = $expression; + } + + if (!$checkHost || !$compiledRoute->getHostRegex()) { + // no-op + } elseif ($hostMatches) { + $conditions[] = sprintf('preg_match(%s, $host, $hostMatches)', self::export($compiledRoute->getHostRegex())); + } else { + $conditions[] = sprintf('%s === $host', self::export($route->getHost())); + } + + $conditions = implode(' && ', $conditions); + + if ($conditions) { + $code .= <<getDefaults(); + if (isset($defaults['_canonical_route'])) { + $name = $defaults['_canonical_route']; + unset($defaults['_canonical_route']); + } + + // optimize parameters array + if ($matches || $hostMatches) { + $vars = array("array('_route' => '$name')"); + if ($matches || ($hostMatches && !$checkHost)) { + $vars[] = '$matches'; + } + if ($hostMatches && $checkHost) { + $vars[] = '$hostMatches'; + } + + $code .= sprintf( + " \$ret = \$this->mergeDefaults(%s, %s);\n", + implode(' + ', $vars), + self::export($defaults) + ); + } elseif ($defaults) { + $code .= sprintf(" \$ret = %s;\n", self::export(array('_route' => $name) + $defaults)); + } else { + $code .= sprintf(" \$ret = array('_route' => '%s');\n", $name); + } + + if ($methods) { + $methodVariable = isset($methods['GET']) ? '$canonicalMethod' : '$requestMethod'; + $methods = self::export($methods); + } + + if ($schemes = $route->getSchemes()) { + $schemes = self::export(array_flip($schemes)); + if ($methods) { + $code .= <<getScheme()]); + if (!isset((\$a = {$methods})[{$methodVariable}])) { + if (\$hasRequiredScheme) { + \$allow += \$a; + } + goto $gotoname; + } + if (!\$hasRequiredScheme) { + \$allowSchemes += \$requiredSchemes; + goto $gotoname; + } + + +EOF; + } else { + $code .= <<getScheme()])) { + \$allowSchemes += \$requiredSchemes; + goto $gotoname; + } + + +EOF; + } + } elseif ($methods) { + $code .= <<indent($code) : $code; + } + + 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; + } + + private function indent($code, $level = 1) + { + return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code); + } + + /** + * @internal + */ + public static function export($value): string + { + if (null === $value) { + return 'null'; + } + if (!\is_array($value)) { + if (\is_object($value)) { + throw new \InvalidArgumentException('Symfony\Component\Routing\Route cannot contain objects.'); + } + + return str_replace("\n", '\'."\n".\'', var_export($value, true)); + } + if (!$value) { + return 'array()'; + } + + $i = 0; + $export = 'array('; + + foreach ($value as $k => $v) { + if ($i === $k) { + ++$i; + } else { + $export .= self::export($k).' => '; + + if (\is_int($k) && $i < $k) { + $i = 1 + $k; + } + } + + $export .= self::export($v).', '; + } + + return substr_replace($export, ')', -2); + } +} diff --git a/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php b/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php new file mode 100644 index 0000000..4eea859 --- /dev/null +++ b/vendor/symfony/routing/Matcher/Dumper/StaticPrefixCollection.php @@ -0,0 +1,202 @@ + + * + * 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; + +/** + * Prefix tree of routes preserving routes order. + * + * @author Frank de Jonge + * @author Nicolas Grekas + * + * @internal + */ +class StaticPrefixCollection +{ + private $prefix; + + /** + * @var string[] + */ + private $staticPrefixes = array(); + + /** + * @var string[] + */ + private $prefixes = array(); + + /** + * @var array[]|self[] + */ + private $items = array(); + + public function __construct(string $prefix = '/') + { + $this->prefix = $prefix; + } + + public function getPrefix(): string + { + return $this->prefix; + } + + /** + * @return array[]|self[] + */ + public function getRoutes(): array + { + return $this->items; + } + + /** + * Adds a route to a group. + * + * @param array|self $route + */ + public function addRoute(string $prefix, $route) + { + list($prefix, $staticPrefix) = $this->getCommonPrefix($prefix, $prefix); + + for ($i = \count($this->items) - 1; 0 <= $i; --$i) { + $item = $this->items[$i]; + + list($commonPrefix, $commonStaticPrefix) = $this->getCommonPrefix($prefix, $this->prefixes[$i]); + + if ($this->prefix === $commonPrefix) { + // the new route and a previous one have no common prefix, let's see if they are exclusive to each others + + if ($this->prefix !== $staticPrefix && $this->prefix !== $this->staticPrefixes[$i]) { + // the new route and the previous one have exclusive static prefixes + continue; + } + + if ($this->prefix === $staticPrefix && $this->prefix === $this->staticPrefixes[$i]) { + // the new route and the previous one have no static prefix + break; + } + + if ($this->prefixes[$i] !== $this->staticPrefixes[$i] && $this->prefix === $this->staticPrefixes[$i]) { + // the previous route is non-static and has no static prefix + break; + } + + if ($prefix !== $staticPrefix && $this->prefix === $staticPrefix) { + // the new route is non-static and has no static prefix + break; + } + + continue; + } + + if ($item instanceof self && $this->prefixes[$i] === $commonPrefix) { + // the new route is a child of a previous one, let's nest it + $item->addRoute($prefix, $route); + } else { + // the new route and a previous one have a common prefix, let's merge them + $child = new self($commonPrefix); + list($child->prefixes[0], $child->staticPrefixes[0]) = $child->getCommonPrefix($this->prefixes[$i], $this->prefixes[$i]); + list($child->prefixes[1], $child->staticPrefixes[1]) = $child->getCommonPrefix($prefix, $prefix); + $child->items = array($this->items[$i], $route); + + $this->staticPrefixes[$i] = $commonStaticPrefix; + $this->prefixes[$i] = $commonPrefix; + $this->items[$i] = $child; + } + + return; + } + + // No optimised case was found, in this case we simple add the route for possible + // grouping when new routes are added. + $this->staticPrefixes[] = $staticPrefix; + $this->prefixes[] = $prefix; + $this->items[] = $route; + } + + /** + * Linearizes back a set of nested routes into a collection. + */ + public function populateCollection(RouteCollection $routes): RouteCollection + { + foreach ($this->items as $route) { + if ($route instanceof self) { + $route->populateCollection($routes); + } else { + $routes->add(...$route); + } + } + + return $routes; + } + + /** + * Gets the full and static common prefixes between two route patterns. + * + * The static prefix stops at last at the first opening bracket. + */ + private function getCommonPrefix(string $prefix, string $anotherPrefix): array + { + $baseLength = \strlen($this->prefix); + $end = min(\strlen($prefix), \strlen($anotherPrefix)); + $staticLength = null; + set_error_handler(array(__CLASS__, 'handleError')); + + for ($i = $baseLength; $i < $end && $prefix[$i] === $anotherPrefix[$i]; ++$i) { + if ('(' === $prefix[$i]) { + $staticLength = $staticLength ?? $i; + for ($j = 1 + $i, $n = 1; $j < $end && 0 < $n; ++$j) { + if ($prefix[$j] !== $anotherPrefix[$j]) { + break 2; + } + if ('(' === $prefix[$j]) { + ++$n; + } elseif (')' === $prefix[$j]) { + --$n; + } elseif ('\\' === $prefix[$j] && (++$j === $end || $prefix[$j] !== $anotherPrefix[$j])) { + --$j; + break; + } + } + if (0 < $n) { + break; + } + if (('?' === ($prefix[$j] ?? '') || '?' === ($anotherPrefix[$j] ?? '')) && ($prefix[$j] ?? '') !== ($anotherPrefix[$j] ?? '')) { + break; + } + $subPattern = substr($prefix, $i, $j - $i); + if ($prefix !== $anotherPrefix && !preg_match('/^\(\[[^\]]++\]\+\+\)$/', $subPattern) && !preg_match('{(?> 6) && preg_match('//u', $prefix.' '.$anotherPrefix)) { + do { + // Prevent cutting in the middle of an UTF-8 characters + --$i; + } while (0b10 === (\ord($prefix[$i]) >> 6)); + } + + return array(substr($prefix, 0, $i), substr($prefix, 0, $staticLength ?? $i)); + } + + public static function handleError($type, $msg) + { + return 0 === strpos($msg, 'preg_match(): Compilation failed: lookbehind assertion is not fixed length'); + } +} diff --git a/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php b/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php new file mode 100644 index 0000000..e60552f --- /dev/null +++ b/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.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\Routing\Matcher; + +use Symfony\Component\Routing\Exception\ExceptionInterface; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +/** + * @author Fabien Potencier + */ +abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + /** + * {@inheritdoc} + */ + public function match($pathinfo) + { + try { + return parent::match($pathinfo); + } catch (ResourceNotFoundException $e) { + if (!\in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) { + throw $e; + } + + if ($this->allowSchemes) { + redirect_scheme: + $scheme = $this->context->getScheme(); + $this->context->setScheme(current($this->allowSchemes)); + try { + $ret = parent::match($pathinfo); + + return $this->redirect($pathinfo, $ret['_route'] ?? null, $this->context->getScheme()) + $ret; + } catch (ExceptionInterface $e2) { + throw $e; + } finally { + $this->context->setScheme($scheme); + } + } elseif ('/' === $pathinfo) { + throw $e; + } else { + try { + $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1); + $ret = parent::match($pathinfo); + + return $this->redirect($pathinfo, $ret['_route'] ?? null) + $ret; + } catch (ExceptionInterface $e2) { + if ($this->allowSchemes) { + goto redirect_scheme; + } + throw $e; + } + } + } + } +} diff --git a/vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php b/vendor/symfony/routing/Matcher/RedirectableUrlMatcherInterface.php new file mode 100644 index 0000000..7c27bc8 --- /dev/null +++ b/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/vendor/symfony/routing/Matcher/RequestMatcherInterface.php b/vendor/symfony/routing/Matcher/RequestMatcherInterface.php new file mode 100644 index 0000000..0c193ff --- /dev/null +++ b/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\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +/** + * 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/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php b/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php new file mode 100644 index 0000000..61cad8e --- /dev/null +++ b/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/vendor/symfony/routing/Matcher/UrlMatcher.php b/vendor/symfony/routing/Matcher/UrlMatcher.php new file mode 100644 index 0000000..095e6dc --- /dev/null +++ b/vendor/symfony/routing/Matcher/UrlMatcher.php @@ -0,0 +1,271 @@ + + * + * 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\ExpressionLanguage\ExpressionFunctionProviderInterface; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * 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; + + /** + * Collects HTTP methods that would be allowed for the request. + */ + protected $allow = array(); + + /** + * Collects URI schemes that would be allowed for the request. + * + * @internal + */ + protected $allowSchemes = 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 = $this->allowSchemes = array(); + + if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) { + return $ret; + } + + if ('/' === $pathinfo && !$this->allow) { + 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; + } + + $status = $this->handleRouteRequirements($pathinfo, $name, $route); + + if (self::REQUIREMENT_MISMATCH === $status[0]) { + continue; + } + + $hasRequiredScheme = !$route->getSchemes() || $route->hasScheme($this->context->getScheme()); + 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)) { + if ($hasRequiredScheme) { + $this->allow = array_merge($this->allow, $requiredMethods); + } + + continue; + } + } + + if (!$hasRequiredScheme) { + $this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes()); + + 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) + { + $defaults = $route->getDefaults(); + if (isset($defaults['_canonical_route'])) { + $name = $defaults['_canonical_route']; + unset($defaults['_canonical_route']); + } + $attributes['_route'] = $name; + + return $this->mergeDefaults($attributes, $defaults); + } + + /** + * 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); + } + + return array(self::REQUIREMENT_MATCH, 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) && null !== $value) { + $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/vendor/symfony/routing/Matcher/UrlMatcherInterface.php b/vendor/symfony/routing/Matcher/UrlMatcherInterface.php new file mode 100644 index 0000000..17f1f97 --- /dev/null +++ b/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\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\NoConfigurationException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * 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/vendor/symfony/routing/README.md b/vendor/symfony/routing/README.md new file mode 100644 index 0000000..88fb1fd --- /dev/null +++ b/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/vendor/symfony/routing/RequestContext.php b/vendor/symfony/routing/RequestContext.php new file mode 100644 index 0000000..0a68fe0 --- /dev/null +++ b/vendor/symfony/routing/RequestContext.php @@ -0,0 +1,326 @@ + + * + * 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(); + + public function __construct(string $baseUrl = '', string $method = 'GET', string $host = 'localhost', string $scheme = 'http', int $httpPort = 80, int $httpsPort = 443, string $path = '/', string $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/vendor/symfony/routing/RequestContextAwareInterface.php b/vendor/symfony/routing/RequestContextAwareInterface.php new file mode 100644 index 0000000..df5b9fc --- /dev/null +++ b/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/vendor/symfony/routing/Route.php b/vendor/symfony/routing/Route.php new file mode 100644 index 0000000..46724db --- /dev/null +++ b/vendor/symfony/routing/Route.php @@ -0,0 +1,571 @@ + + * + * 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 CompiledRoute|null + */ + 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(string $path, array $defaults = array(), array $requirements = array(), array $options = array(), ?string $host = '', $schemes = array(), $methods = array(), ?string $condition = '') + { + $this->setPath($path); + $this->addDefaults($defaults); + $this->addRequirements($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) + { + if (false !== strpbrk($pattern, '?<')) { + $pattern = preg_replace_callback('#\{(\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) { + if (isset($m[3][0])) { + $this->setDefault($m[1], '?' !== $m[3] ? substr($m[3], 1) : null); + } + if (isset($m[2][0])) { + $this->setRequirement($m[1], substr($m[2], 1, -1)); + } + + return '{'.$m[1].'}'; + }, $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/vendor/symfony/routing/RouteCollection.php b/vendor/symfony/routing/RouteCollection.php new file mode 100644 index 0000000..4525404 --- /dev/null +++ b/vendor/symfony/routing/RouteCollection.php @@ -0,0 +1,297 @@ + + * + * 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(self $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); + } + } + + /** + * Adds a prefix to the name of all the routes within in the collection. + */ + public function addNamePrefix(string $prefix) + { + $prefixedRoutes = array(); + + foreach ($this->routes as $name => $route) { + $prefixedRoutes[$prefix.$name] = $route; + if (null !== $name = $route->getDefault('_canonical_route')) { + $route->setDefault('_canonical_route', $prefix.$name); + } + } + + $this->routes = $prefixedRoutes; + } + + /** + * 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/vendor/symfony/routing/RouteCollectionBuilder.php b/vendor/symfony/routing/RouteCollectionBuilder.php new file mode 100644 index 0000000..eb42887 --- /dev/null +++ b/vendor/symfony/routing/RouteCollectionBuilder.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\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[] $collections */ + $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, self $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. + * + * @return $this + */ + private function addResource(ResourceInterface $resource): self + { + $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. + */ + private function generateRouteName(Route $route): string + { + $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, string $type = null): array + { + 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/vendor/symfony/routing/RouteCompiler.php b/vendor/symfony/routing/RouteCompiler.php new file mode 100644 index 0000000..66b291b --- /dev/null +++ b/vendor/symfony/routing/RouteCompiler.php @@ -0,0 +1,329 @@ + + * + * 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)) { + throw new \LogicException(sprintf('Cannot use UTF-8 route patterns without setting the "utf8" option for route "%s".', $route->getPath())); + } + 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.'sD'.($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. + */ + private static function determineStaticPrefix(Route $route, array $tokens): string + { + 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 (or the empty string when none available). + */ + private static function findNextSeparator(string $pattern, bool $useUtf8): string + { + 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, int $index, int $firstOptional): string + { + $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; + } + } + } + + private static function transformCapturingGroupsToNonCapturings(string $regexp): string + { + for ($i = 0; $i < \strlen($regexp); ++$i) { + if ('\\' === $regexp[$i]) { + ++$i; + continue; + } + if ('(' !== $regexp[$i] || !isset($regexp[$i + 2])) { + continue; + } + if ('*' === $regexp[++$i] || '?' === $regexp[$i]) { + ++$i; + continue; + } + $regexp = substr_replace($regexp, '?:', $i, 0); + ++$i; + } + + return $regexp; + } +} diff --git a/vendor/symfony/routing/RouteCompilerInterface.php b/vendor/symfony/routing/RouteCompilerInterface.php new file mode 100644 index 0000000..ddfa7ca --- /dev/null +++ b/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/vendor/symfony/routing/Router.php b/vendor/symfony/routing/Router.php new file mode 100644 index 0000000..56842a4 --- /dev/null +++ b/vendor/symfony/routing/Router.php @@ -0,0 +1,388 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Config\ConfigCacheFactory; +use Symfony\Component\Config\ConfigCacheFactoryInterface; +use Symfony\Component\Config\ConfigCacheInterface; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface; +use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface; +use Symfony\Component\Routing\Matcher\RequestMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; + +/** + * 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()); + } + ); + + if (!class_exists($this->options['matcher_cache_class'], false)) { + 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()); + } + ); + + if (!class_exists($this->options['generator_cache_class'], false)) { + 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/vendor/symfony/routing/RouterInterface.php b/vendor/symfony/routing/RouterInterface.php new file mode 100644 index 0000000..a10ae34 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Annotation/RouteTest.php b/vendor/symfony/routing/Tests/Annotation/RouteTest.php new file mode 100644 index 0000000..4d6b2a9 --- /dev/null +++ b/vendor/symfony/routing/Tests/Annotation/RouteTest.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\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')); + } + + /** + * @expectedException \BadMethodCallException + */ + public function testTryingToSetLocalesDirectly() + { + $route = new Route(array('locales' => array('nl' => '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'), + array('value', array('nl' => '/hier', 'en' => '/here'), 'getLocalizedPaths'), + ); + } +} diff --git a/vendor/symfony/routing/Tests/CompiledRouteTest.php b/vendor/symfony/routing/Tests/CompiledRouteTest.php new file mode 100644 index 0000000..5bec7bb --- /dev/null +++ b/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/vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php b/vendor/symfony/routing/Tests/DependencyInjection/RoutingResolverPassTest.php new file mode 100644 index 0000000..97a34c9 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php b/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/AbstractClass.php new file mode 100644 index 0000000..56bcab2 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php b/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BarClass.php new file mode 100644 index 0000000..a388277 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php b/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/BazClass.php new file mode 100644 index 0000000..471968b --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php b/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooClass.php new file mode 100644 index 0000000..320dc35 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php b/vendor/symfony/routing/Tests/Fixtures/AnnotatedClasses/FooTrait.php new file mode 100644 index 0000000..ee8f4b0 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php b/vendor/symfony/routing/Tests/Fixtures/CustomRouteCompiler.php new file mode 100644 index 0000000..c2e2afd --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php b/vendor/symfony/routing/Tests/Fixtures/CustomXmlFileLoader.php new file mode 100644 index 0000000..b7a02b6 --- /dev/null +++ b/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\Config\Util\XmlUtils; +use Symfony\Component\Routing\Loader\XmlFileLoader; + +/** + * XmlFileLoader with schema validation turned off. + */ +class CustomXmlFileLoader extends XmlFileLoader +{ + protected function loadFile($file) + { + return XmlUtils::loadFile($file, function () { return true; }); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php b/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php new file mode 100644 index 0000000..de87895 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php b/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php new file mode 100644 index 0000000..8900d34 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.php @@ -0,0 +1,3 @@ +class NoStartTagClass +{ +} diff --git a/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php b/vendor/symfony/routing/Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.php new file mode 100644 index 0000000..729c9b4 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php b/vendor/symfony/routing/Tests/Fixtures/RedirectableUrlMatcher.php new file mode 100644 index 0000000..08b67a0 --- /dev/null +++ b/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\RedirectableUrlMatcherInterface; +use Symfony\Component\Routing\Matcher\UrlMatcher; + +/** + * @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/vendor/symfony/routing/Tests/Fixtures/annotated.php b/vendor/symfony/routing/Tests/Fixtures/annotated.php new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/routing/Tests/Fixtures/bad_format.yml b/vendor/symfony/routing/Tests/Fixtures/bad_format.yml new file mode 100644 index 0000000..8ba50e2 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/bar.xml b/vendor/symfony/routing/Tests/Fixtures/bar.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml b/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml new file mode 100644 index 0000000..bbe727d --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.xml @@ -0,0 +1,10 @@ + + + + + FrameworkBundle:Template:template + + diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml b/vendor/symfony/routing/Tests/Fixtures/controller/import__controller.yml new file mode 100644 index 0000000..4240b74 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml b/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml new file mode 100644 index 0000000..378b9ca --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml b/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml new file mode 100644 index 0000000..1a71c62 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/controller/import_controller.yml @@ -0,0 +1,3 @@ +_static: + resource: routing.yml + controller: FrameworkBundle:Template:template diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml new file mode 100644 index 0000000..e3c755a --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.xml @@ -0,0 +1,10 @@ + + + + + AppBundle:Blog:index + + diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml b/vendor/symfony/routing/Tests/Fixtures/controller/import_override_defaults.yml new file mode 100644 index 0000000..db1ab3c --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml new file mode 100644 index 0000000..f47c57b --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.xml @@ -0,0 +1,10 @@ + + + + + AppBundle:Blog:index + + diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml b/vendor/symfony/routing/Tests/Fixtures/controller/override_defaults.yml new file mode 100644 index 0000000..00a2c0e --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml b/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml new file mode 100644 index 0000000..6420138 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/controller/routing.xml @@ -0,0 +1,14 @@ + + + + + + + AppBundle:Blog:list + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/controller/routing.yml b/vendor/symfony/routing/Tests/Fixtures/controller/routing.yml new file mode 100644 index 0000000..cb71ec3 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml b/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml new file mode 100644 index 0000000..d078836 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes1.yml @@ -0,0 +1,2 @@ +route1: + path: /route/1 diff --git a/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml b/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml new file mode 100644 index 0000000..938fb24 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/directory/recurse/routes2.yml @@ -0,0 +1,2 @@ +route2: + path: /route/2 diff --git a/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml b/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml new file mode 100644 index 0000000..088cfb4 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/directory/routes3.yml @@ -0,0 +1,2 @@ +route3: + path: /route/3 diff --git a/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml b/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml new file mode 100644 index 0000000..af829e5 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/directory_import/import.yml @@ -0,0 +1,3 @@ +_directory: + resource: "../directory" + type: directory diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php new file mode 100644 index 0000000..e7c0765 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher0.php @@ -0,0 +1,35 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php new file mode 100644 index 0000000..68e7741 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher1.php @@ -0,0 +1,247 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + default: + $routes = array( + '/test/baz' => array(array('_route' => 'baz'), null, null, null), + '/test/baz.html' => array(array('_route' => 'baz2'), null, null, null), + '/test/baz3/' => array(array('_route' => 'baz3'), null, null, null), + '/foofoo' => array(array('_route' => 'foofoo', 'def' => 'test'), null, null, null), + '/spa ce' => array(array('_route' => 'space'), null, null, null), + '/multi/new' => array(array('_route' => 'overridden2'), null, null, null), + '/multi/hey/' => array(array('_route' => 'hey'), null, null, null), + '/ababa' => array(array('_route' => 'ababa'), null, null, null), + '/route1' => array(array('_route' => 'route1'), 'a.example.com', null, null), + '/c2/route2' => array(array('_route' => 'route2'), 'a.example.com', null, null), + '/route4' => array(array('_route' => 'route4'), 'a.example.com', null, null), + '/c2/route3' => array(array('_route' => 'route3'), 'b.example.com', null, null), + '/route5' => array(array('_route' => 'route5'), 'c.example.com', null, null), + '/route6' => array(array('_route' => 'route6'), null, null, null), + '/route11' => array(array('_route' => 'route11'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route12' => array(array('_route' => 'route12', 'var1' => 'val'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route17' => array(array('_route' => 'route17'), null, null, null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + if ($requiredHost) { + if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) { + break; + } + if ('#' === $requiredHost[0] && $hostMatches) { + $hostMatches['_route'] = $ret['_route']; + $ret = $this->mergeDefaults($hostMatches, $ret); + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + $matchedPathinfo = $host.'.'.$pathinfo; + $regexList = array( + 0 => '{^(?' + .'|(?:(?:[^./]*+\\.)++)(?' + .'|/foo/(baz|symfony)(*:47)' + .'|/bar(?' + .'|/([^/]++)(*:70)' + .'|head/([^/]++)(*:90)' + .')' + .'|/test/([^/]++)/(?' + .'|(*:116)' + .')' + .'|/([\']+)(*:132)' + .'|/a/(?' + .'|b\'b/([^/]++)(?' + .'|(*:161)' + .'|(*:169)' + .')' + .'|(.*)(*:182)' + .'|b\'b/([^/]++)(?' + .'|(*:205)' + .'|(*:213)' + .')' + .')' + .'|/multi/hello(?:/([^/]++))?(*:249)' + .'|/([^/]++)/b/([^/]++)(?' + .'|(*:280)' + .'|(*:288)' + .')' + .'|/aba/([^/]++)(*:310)' + .')|(?i:([^\\.]++)\\.example\\.com)\\.(?' + .'|/route1(?' + .'|3/([^/]++)(*:372)' + .'|4/([^/]++)(*:390)' + .')' + .')|(?i:c\\.example\\.com)\\.(?' + .'|/route15/([^/]++)(*:442)' + .')|(?:(?:[^./]*+\\.)++)(?' + .'|/route16/([^/]++)(*:490)' + .'|/a/(?' + .'|a\\.\\.\\.(*:511)' + .'|b/(?' + .'|([^/]++)(*:532)' + .'|c/([^/]++)(*:550)' + .')' + .')' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + case 116: + $matches = array('foo' => $matches[1] ?? null); + + // baz4 + return $this->mergeDefaults(array('_route' => 'baz4') + $matches, array()); + + // baz5 + $ret = $this->mergeDefaults(array('_route' => 'baz5') + $matches, array()); + if (!isset(($a = array('POST' => 0))[$requestMethod])) { + $allow += $a; + goto not_baz5; + } + + return $ret; + not_baz5: + + // baz.baz6 + $ret = $this->mergeDefaults(array('_route' => 'baz.baz6') + $matches, array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_bazbaz6; + } + + return $ret; + not_bazbaz6: + + break; + case 161: + $matches = array('foo' => $matches[1] ?? null); + + // foo1 + $ret = $this->mergeDefaults(array('_route' => 'foo1') + $matches, array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_foo1; + } + + return $ret; + not_foo1: + + break; + case 205: + $matches = array('foo1' => $matches[1] ?? null); + + // foo2 + return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); + + break; + case 280: + $matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); + + // foo3 + return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array()); + + break; + default: + $routes = array( + 47 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), + 70 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), + 90 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), + 132 => array(array('_route' => 'quoter'), array('quoter'), null, null), + 169 => array(array('_route' => 'bar1'), array('bar'), null, null), + 182 => array(array('_route' => 'overridden'), array('var'), null, null), + 213 => array(array('_route' => 'bar2'), array('bar1'), null, null), + 249 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), + 288 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), + 310 => array(array('_route' => 'foo4'), array('foo'), null, null), + 372 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), + 390 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), + 442 => array(array('_route' => 'route15'), array('name'), null, null), + 490 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), + 511 => array(array('_route' => 'a'), array(), null, null), + 532 => array(array('_route' => 'b'), array('var'), null, null), + 550 => array(array('_route' => 'c'), array('var'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (550 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher10.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher10.php new file mode 100644 index 0000000..2f5cc3f --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher10.php @@ -0,0 +1,2830 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/c(?' + .'|f(?' + .'|cd20/([^/]++)/([^/]++)/([^/]++)/cfcd20(*:54)' + .'|e(?' + .'|cdb/([^/]++)/([^/]++)/([^/]++)/cfecdb(*:102)' + .'|e39/([^/]++)/([^/]++)/([^/]++)/cfee39(*:147)' + .')' + .'|a086/([^/]++)/([^/]++)/([^/]++)/cfa086(*:194)' + .'|004f/([^/]++)/([^/]++)/([^/]++)/cf004f(*:240)' + .')' + .'|4(?' + .'|ca42/([^/]++)/([^/]++)/([^/]++)/c4ca42(*:291)' + .'|5147/([^/]++)/([^/]++)/([^/]++)/c45147(*:337)' + .'|1000/([^/]++)/([^/]++)/([^/]++)/c41000(*:383)' + .')' + .'|8(?' + .'|1e72/([^/]++)/([^/]++)/([^/]++)/c81e72(*:434)' + .'|ffe9/([^/]++)/([^/]++)/([^/]++)/c8ffe9(*:480)' + .'|6a7e/([^/]++)/([^/]++)/([^/]++)/c86a7e(*:526)' + .')' + .'|9(?' + .'|f0f8/([^/]++)/([^/]++)/([^/]++)/c9f0f8(*:577)' + .'|e107/([^/]++)/([^/]++)/([^/]++)/c9e107(*:623)' + .')' + .'|2(?' + .'|0(?' + .'|ad4/([^/]++)/([^/]++)/([^/]++)/c20ad4(*:677)' + .'|3d8/([^/]++)/([^/]++)/([^/]++)/c203d8(*:722)' + .')' + .'|4cd7/([^/]++)/([^/]++)/([^/]++)/c24cd7(*:769)' + .')' + .'|5(?' + .'|1ce4/([^/]++)/([^/]++)/([^/]++)/c51ce4(*:820)' + .'|2f1b/([^/]++)/([^/]++)/([^/]++)/c52f1b(*:866)' + .'|ff25/([^/]++)/([^/]++)/([^/]++)/c5ff25(*:912)' + .')' + .'|7(?' + .'|4d97/([^/]++)/([^/]++)/([^/]++)/c74d97(*:963)' + .'|e124/([^/]++)/([^/]++)/([^/]++)/c7e124(*:1009)' + .')' + .'|16a53/([^/]++)/([^/]++)/([^/]++)/c16a53(*:1058)' + .'|0(?' + .'|c7c7/([^/]++)/([^/]++)/([^/]++)/c0c7c7(*:1109)' + .'|e190/([^/]++)/([^/]++)/([^/]++)/c0e190(*:1156)' + .'|42f4/([^/]++)/([^/]++)/([^/]++)/c042f4(*:1203)' + .'|58f5/([^/]++)/([^/]++)/([^/]++)/c058f5(*:1250)' + .')' + .'|e(?' + .'|debb/([^/]++)/([^/]++)/([^/]++)/cedebb(*:1302)' + .'|e631/([^/]++)/([^/]++)/([^/]++)/cee631(*:1349)' + .')' + .'|a(?' + .'|46c1/([^/]++)/([^/]++)/([^/]++)/ca46c1(*:1401)' + .'|f1a3/([^/]++)/([^/]++)/([^/]++)/caf1a3(*:1448)' + .')' + .'|b70ab/([^/]++)/([^/]++)/([^/]++)/cb70ab(*:1497)' + .'|d0069/([^/]++)/([^/]++)/([^/]++)/cd0069(*:1545)' + .'|3(?' + .'|e878/([^/]++)/([^/]++)/([^/]++)/c3e878(*:1596)' + .'|c59e/([^/]++)/([^/]++)/([^/]++)/c3c59e(*:1643)' + .')' + .')' + .'|/e(?' + .'|c(?' + .'|cbc8/([^/]++)/([^/]++)/([^/]++)/eccbc8(*:1701)' + .'|8(?' + .'|956/([^/]++)/([^/]++)/([^/]++)/ec8956(*:1751)' + .'|ce6/([^/]++)/([^/]++)/([^/]++)/ec8ce6(*:1797)' + .')' + .'|5dec/([^/]++)/([^/]++)/([^/]++)/ec5dec(*:1845)' + .')' + .'|4(?' + .'|da3b/([^/]++)/([^/]++)/([^/]++)/e4da3b(*:1897)' + .'|a622/([^/]++)/([^/]++)/([^/]++)/e4a622(*:1944)' + .'|6de7/([^/]++)/([^/]++)/([^/]++)/e46de7(*:1991)' + .'|4fea/([^/]++)/([^/]++)/([^/]++)/e44fea(*:2038)' + .')' + .'|3(?' + .'|6985/([^/]++)/([^/]++)/([^/]++)/e36985(*:2090)' + .'|796a/([^/]++)/([^/]++)/([^/]++)/e3796a(*:2137)' + .')' + .'|a(?' + .'|5d2f/([^/]++)/([^/]++)/([^/]++)/ea5d2f(*:2189)' + .'|e27d/([^/]++)/([^/]++)/([^/]++)/eae27d(*:2236)' + .')' + .'|2(?' + .'|c(?' + .'|420/([^/]++)/([^/]++)/([^/]++)/e2c420(*:2291)' + .'|0be/([^/]++)/([^/]++)/([^/]++)/e2c0be(*:2337)' + .')' + .'|ef52/([^/]++)/([^/]++)/([^/]++)/e2ef52(*:2385)' + .')' + .'|d(?' + .'|3d2c/([^/]++)/([^/]++)/([^/]++)/ed3d2c(*:2437)' + .'|a80a/([^/]++)/([^/]++)/([^/]++)/eda80a(*:2484)' + .'|dea8/([^/]++)/([^/]++)/([^/]++)/eddea8(*:2531)' + .')' + .'|b(?' + .'|16(?' + .'|0d/([^/]++)/([^/]++)/([^/]++)/eb160d(*:2586)' + .'|37/([^/]++)/([^/]++)/([^/]++)/eb1637(*:2631)' + .')' + .'|a0dc/([^/]++)/([^/]++)/([^/]++)/eba0dc(*:2679)' + .')' + .'|0(?' + .'|0da0/([^/]++)/([^/]++)/([^/]++)/e00da0(*:2731)' + .'|c641/([^/]++)/([^/]++)/([^/]++)/e0c641(*:2778)' + .')' + .'|e(?' + .'|cca5/([^/]++)/([^/]++)/([^/]++)/eecca5(*:2830)' + .'|d5af/([^/]++)/([^/]++)/([^/]++)/eed5af(*:2877)' + .')' + .'|96ed4/([^/]++)/([^/]++)/([^/]++)/e96ed4(*:2926)' + .'|1(?' + .'|6542/([^/]++)/([^/]++)/([^/]++)/e16542(*:2977)' + .'|e32e/([^/]++)/([^/]++)/([^/]++)/e1e32e(*:3024)' + .')' + .'|56954/([^/]++)/([^/]++)/([^/]++)/e56954(*:3073)' + .'|f(?' + .'|0d39/([^/]++)/([^/]++)/([^/]++)/ef0d39(*:3124)' + .'|e937/([^/]++)/([^/]++)/([^/]++)/efe937(*:3171)' + .'|575e/([^/]++)/([^/]++)/([^/]++)/ef575e(*:3218)' + .')' + .'|7b24b/([^/]++)/([^/]++)/([^/]++)/e7b24b(*:3267)' + .'|836d8/([^/]++)/([^/]++)/([^/]++)/e836d8(*:3315)' + .')' + .'|/a(?' + .'|8(?' + .'|7ff6/([^/]++)/([^/]++)/([^/]++)/a87ff6(*:3372)' + .'|baa5/([^/]++)/([^/]++)/([^/]++)/a8baa5(*:3419)' + .'|f15e/([^/]++)/([^/]++)/([^/]++)/a8f15e(*:3466)' + .'|c88a/([^/]++)/([^/]++)/([^/]++)/a8c88a(*:3513)' + .'|abb4/([^/]++)/([^/]++)/([^/]++)/a8abb4(*:3560)' + .')' + .'|a(?' + .'|b323/([^/]++)/([^/]++)/([^/]++)/aab323(*:3612)' + .'|942a/([^/]++)/([^/]++)/([^/]++)/aa942a(*:3659)' + .')' + .'|5(?' + .'|bfc9/([^/]++)/([^/]++)/([^/]++)/a5bfc9(*:3711)' + .'|771b/([^/]++)/([^/]++)/([^/]++)/a5771b(*:3758)' + .'|e001/([^/]++)/([^/]++)/([^/]++)/a5e001(*:3805)' + .'|97e5/([^/]++)/([^/]++)/([^/]++)/a597e5(*:3852)' + .'|16a8/([^/]++)/([^/]++)/([^/]++)/a516a8(*:3899)' + .')' + .'|1d0c6/([^/]++)/([^/]++)/([^/]++)/a1d0c6(*:3948)' + .'|6(?' + .'|84ec/([^/]++)/([^/]++)/([^/]++)/a684ec(*:3999)' + .'|6658/([^/]++)/([^/]++)/([^/]++)/a66658(*:4046)' + .')' + .'|3(?' + .'|f390/([^/]++)/([^/]++)/([^/]++)/a3f390(*:4098)' + .'|c65c/([^/]++)/([^/]++)/([^/]++)/a3c65c(*:4145)' + .')' + .'|d(?' + .'|61ab/([^/]++)/([^/]++)/([^/]++)/ad61ab(*:4197)' + .'|13a2/([^/]++)/([^/]++)/([^/]++)/ad13a2(*:4244)' + .'|972f/([^/]++)/([^/]++)/([^/]++)/ad972f(*:4291)' + .')' + .'|c(?' + .'|627a/([^/]++)/([^/]++)/([^/]++)/ac627a(*:4343)' + .'|1dd2/([^/]++)/([^/]++)/([^/]++)/ac1dd2(*:4390)' + .')' + .'|9(?' + .'|7da6/([^/]++)/([^/]++)/([^/]++)/a97da6(*:4442)' + .'|6b65/([^/]++)/([^/]++)/([^/]++)/a96b65(*:4489)' + .')' + .'|0(?' + .'|a080/([^/]++)/([^/]++)/([^/]++)/a0a080(*:4541)' + .'|2ffd/([^/]++)/([^/]++)/([^/]++)/a02ffd(*:4588)' + .'|1a03/([^/]++)/([^/]++)/([^/]++)/a01a03(*:4635)' + .')' + .'|4(?' + .'|a042/([^/]++)/([^/]++)/([^/]++)/a4a042(*:4687)' + .'|f236/([^/]++)/([^/]++)/([^/]++)/a4f236(*:4734)' + .'|9e94/([^/]++)/([^/]++)/([^/]++)/a49e94(*:4781)' + .')' + .'|2557a/([^/]++)/([^/]++)/([^/]++)/a2557a(*:4830)' + .'|b817c/([^/]++)/([^/]++)/([^/]++)/ab817c(*:4878)' + .')' + .'|/1(?' + .'|6(?' + .'|7909/([^/]++)/([^/]++)/([^/]++)/167909(*:4935)' + .'|a5cd/([^/]++)/([^/]++)/([^/]++)/16a5cd(*:4982)' + .'|51cf/([^/]++)/([^/]++)/([^/]++)/1651cf(*:5029)' + .')' + .'|f(?' + .'|0e3d/([^/]++)/([^/]++)/([^/]++)/1f0e3d(*:5081)' + .'|f(?' + .'|1de/([^/]++)/([^/]++)/([^/]++)/1ff1de(*:5131)' + .'|8a7/([^/]++)/([^/]++)/([^/]++)/1ff8a7(*:5177)' + .')' + .')' + .'|8(?' + .'|2be0/([^/]++)/([^/]++)/([^/]++)/182be0(*:5230)' + .'|d804/([^/]++)/([^/]++)/([^/]++)/18d804(*:5277)' + .'|9977/([^/]++)/([^/]++)/([^/]++)/189977(*:5324)' + .')' + .'|c(?' + .'|383c/([^/]++)/([^/]++)/([^/]++)/1c383c(*:5376)' + .'|9ac0/([^/]++)/([^/]++)/([^/]++)/1c9ac0(*:5423)' + .')' + .'|9(?' + .'|ca14/([^/]++)/([^/]++)/([^/]++)/19ca14(*:5475)' + .'|f3cd/([^/]++)/([^/]++)/([^/]++)/19f3cd(*:5522)' + .')' + .'|7(?' + .'|e621/([^/]++)/([^/]++)/([^/]++)/17e621(*:5574)' + .'|0000/([^/]++)/([^/]++)/([^/]++)/170000(*:5621)' + .'|d63b/([^/]++)/([^/]++)/([^/]++)/17d63b(*:5668)' + .')' + .'|4(?' + .'|bfa6/([^/]++)/([^/]++)/([^/]++)/14bfa6(*:5720)' + .'|0f69/([^/]++)/([^/]++)/([^/]++)/140f69(*:5767)' + .'|9e96/([^/]++)/([^/]++)/([^/]++)/149e96(*:5814)' + .'|2949/([^/]++)/([^/]++)/([^/]++)/142949(*:5861)' + .')' + .'|a(?' + .'|fa34/([^/]++)/([^/]++)/([^/]++)/1afa34(*:5913)' + .'|5b1e/([^/]++)/([^/]++)/([^/]++)/1a5b1e(*:5960)' + .')' + .'|3(?' + .'|8(?' + .'|597/([^/]++)/([^/]++)/([^/]++)/138597(*:6015)' + .'|bb0/([^/]++)/([^/]++)/([^/]++)/138bb0(*:6061)' + .')' + .'|f(?' + .'|e9d/([^/]++)/([^/]++)/([^/]++)/13fe9d(*:6112)' + .'|989/([^/]++)/([^/]++)/([^/]++)/13f989(*:6158)' + .'|3cf/([^/]++)/([^/]++)/([^/]++)/13f3cf(*:6204)' + .')' + .')' + .'|d7f7a/([^/]++)/([^/]++)/([^/]++)/1d7f7a(*:6254)' + .'|5(?' + .'|34b7/([^/]++)/([^/]++)/([^/]++)/1534b7(*:6305)' + .'|8f30/([^/]++)/([^/]++)/([^/]++)/158f30(*:6352)' + .'|4384/([^/]++)/([^/]++)/([^/]++)/154384(*:6399)' + .'|d4e8/([^/]++)/([^/]++)/([^/]++)/15d4e8(*:6446)' + .')' + .'|1(?' + .'|5f89/([^/]++)/([^/]++)/([^/]++)/115f89(*:6498)' + .'|b984/([^/]++)/([^/]++)/([^/]++)/11b984(*:6545)' + .')' + .'|068c6/([^/]++)/([^/]++)/([^/]++)/1068c6(*:6594)' + .'|be3bc/([^/]++)/([^/]++)/([^/]++)/1be3bc(*:6642)' + .')' + .'|/8(?' + .'|f(?' + .'|1(?' + .'|4e4/([^/]++)/([^/]++)/([^/]++)/8f14e4(*:6702)' + .'|21c/([^/]++)/([^/]++)/([^/]++)/8f121c(*:6748)' + .')' + .'|8551/([^/]++)/([^/]++)/([^/]++)/8f8551(*:6796)' + .'|5329/([^/]++)/([^/]++)/([^/]++)/8f5329(*:6843)' + .'|e009/([^/]++)/([^/]++)/([^/]++)/8fe009(*:6890)' + .')' + .'|e(?' + .'|296a/([^/]++)/([^/]++)/([^/]++)/8e296a(*:6942)' + .'|98d8/([^/]++)/([^/]++)/([^/]++)/8e98d8(*:6989)' + .'|fb10/([^/]++)/([^/]++)/([^/]++)/8efb10(*:7036)' + .'|6b42/([^/]++)/([^/]++)/([^/]++)/8e6b42(*:7083)' + .')' + .'|61398/([^/]++)/([^/]++)/([^/]++)/861398(*:7132)' + .'|1(?' + .'|2b4b/([^/]++)/([^/]++)/([^/]++)/812b4b(*:7183)' + .'|9f46/([^/]++)/([^/]++)/([^/]++)/819f46(*:7230)' + .'|6b11/([^/]++)/([^/]++)/([^/]++)/816b11(*:7277)' + .')' + .'|d(?' + .'|5e95/([^/]++)/([^/]++)/([^/]++)/8d5e95(*:7329)' + .'|3bba/([^/]++)/([^/]++)/([^/]++)/8d3bba(*:7376)' + .'|d48d/([^/]++)/([^/]++)/([^/]++)/8dd48d(*:7423)' + .'|7d8e/([^/]++)/([^/]++)/([^/]++)/8d7d8e(*:7470)' + .')' + .'|2(?' + .'|aa4b/([^/]++)/([^/]++)/([^/]++)/82aa4b(*:7522)' + .'|1(?' + .'|612/([^/]++)/([^/]++)/([^/]++)/821612(*:7572)' + .'|fa7/([^/]++)/([^/]++)/([^/]++)/821fa7(*:7618)' + .')' + .'|cec9/([^/]++)/([^/]++)/([^/]++)/82cec9(*:7666)' + .')' + .'|5(?' + .'|d8ce/([^/]++)/([^/]++)/([^/]++)/85d8ce(*:7718)' + .'|4d(?' + .'|6f/([^/]++)/([^/]++)/([^/]++)/854d6f(*:7768)' + .'|9f/([^/]++)/([^/]++)/([^/]++)/854d9f(*:7813)' + .')' + .')' + .'|4d9ee/([^/]++)/([^/]++)/([^/]++)/84d9ee(*:7863)' + .'|c(?' + .'|19f5/([^/]++)/([^/]++)/([^/]++)/8c19f5(*:7914)' + .'|b22b/([^/]++)/([^/]++)/([^/]++)/8cb22b(*:7961)' + .')' + .'|39ab4/([^/]++)/([^/]++)/([^/]++)/839ab4(*:8010)' + .'|9f0fd/([^/]++)/([^/]++)/([^/]++)/89f0fd(*:8058)' + .'|bf121/([^/]++)/([^/]++)/([^/]++)/8bf121(*:8106)' + .'|77a9b/([^/]++)/([^/]++)/([^/]++)/877a9b(*:8154)' + .')' + .'|/4(?' + .'|5(?' + .'|c48c/([^/]++)/([^/]++)/([^/]++)/45c48c(*:8211)' + .'|fbc6/([^/]++)/([^/]++)/([^/]++)/45fbc6(*:8258)' + .')' + .'|e732c/([^/]++)/([^/]++)/([^/]++)/4e732c(*:8307)' + .'|4f683/([^/]++)/([^/]++)/([^/]++)/44f683(*:8355)' + .'|3(?' + .'|ec51/([^/]++)/([^/]++)/([^/]++)/43ec51(*:8406)' + .'|2aca/([^/]++)/([^/]++)/([^/]++)/432aca(*:8453)' + .')' + .'|c5(?' + .'|6ff/([^/]++)/([^/]++)/([^/]++)/4c56ff(*:8505)' + .'|bde/([^/]++)/([^/]++)/([^/]++)/4c5bde(*:8551)' + .')' + .'|2(?' + .'|a0e1/([^/]++)/([^/]++)/([^/]++)/42a0e1(*:8603)' + .'|e7aa/([^/]++)/([^/]++)/([^/]++)/42e7aa(*:8650)' + .'|998c/([^/]++)/([^/]++)/([^/]++)/42998c(*:8697)' + .'|8fca/([^/]++)/([^/]++)/([^/]++)/428fca(*:8744)' + .')' + .'|7(?' + .'|d1e9/([^/]++)/([^/]++)/([^/]++)/47d1e9(*:8796)' + .'|34ba/([^/]++)/([^/]++)/([^/]++)/4734ba(*:8843)' + .')' + .'|6ba9f/([^/]++)/([^/]++)/([^/]++)/46ba9f(*:8892)' + .'|8aedb/([^/]++)/([^/]++)/([^/]++)/48aedb(*:8940)' + .'|9(?' + .'|182f/([^/]++)/([^/]++)/([^/]++)/49182f(*:8991)' + .'|6e05/([^/]++)/([^/]++)/([^/]++)/496e05(*:9038)' + .'|ae49/([^/]++)/([^/]++)/([^/]++)/49ae49(*:9085)' + .')' + .'|0008b/([^/]++)/([^/]++)/([^/]++)/40008b(*:9134)' + .'|1(?' + .'|f1f1/([^/]++)/([^/]++)/([^/]++)/41f1f1(*:9185)' + .'|ae36/([^/]++)/([^/]++)/([^/]++)/41ae36(*:9232)' + .')' + .'|f(?' + .'|6ffe/([^/]++)/([^/]++)/([^/]++)/4f6ffe(*:9284)' + .'|4adc/([^/]++)/([^/]++)/([^/]++)/4f4adc(*:9331)' + .')' + .')' + .'|/d(?' + .'|3(?' + .'|d944/([^/]++)/([^/]++)/([^/]++)/d3d944(*:9389)' + .'|9577/([^/]++)/([^/]++)/([^/]++)/d39577(*:9436)' + .'|4ab1/([^/]++)/([^/]++)/([^/]++)/d34ab1(*:9483)' + .')' + .'|6(?' + .'|7d8a/([^/]++)/([^/]++)/([^/]++)/d67d8a(*:9535)' + .'|4592/([^/]++)/([^/]++)/([^/]++)/d64592(*:9582)' + .'|baf6/([^/]++)/([^/]++)/([^/]++)/d6baf6(*:9629)' + .'|1e4b/([^/]++)/([^/]++)/([^/]++)/d61e4b(*:9676)' + .')' + .'|9(?' + .'|d4f4/([^/]++)/([^/]++)/([^/]++)/d9d4f4(*:9728)' + .'|6409/([^/]++)/([^/]++)/([^/]++)/d96409(*:9775)' + .'|47bf/([^/]++)/([^/]++)/([^/]++)/d947bf(*:9822)' + .'|fc5b/([^/]++)/([^/]++)/([^/]++)/d9fc5b(*:9869)' + .')' + .'|8(?' + .'|2c8d/([^/]++)/([^/]++)/([^/]++)/d82c8d(*:9921)' + .'|1f9c/([^/]++)/([^/]++)/([^/]++)/d81f9c(*:9968)' + .')' + .'|2(?' + .'|ddea/([^/]++)/([^/]++)/([^/]++)/d2ddea(*:10020)' + .'|96c1/([^/]++)/([^/]++)/([^/]++)/d296c1(*:10068)' + .')' + .'|0(?' + .'|9bf4/([^/]++)/([^/]++)/([^/]++)/d09bf4(*:10121)' + .'|7e70/([^/]++)/([^/]++)/([^/]++)/d07e70(*:10169)' + .')' + .'|1(?' + .'|f(?' + .'|e17/([^/]++)/([^/]++)/([^/]++)/d1fe17(*:10225)' + .'|491/([^/]++)/([^/]++)/([^/]++)/d1f491(*:10272)' + .'|255/([^/]++)/([^/]++)/([^/]++)/d1f255(*:10319)' + .')' + .'|c38a/([^/]++)/([^/]++)/([^/]++)/d1c38a(*:10368)' + .'|8f65/([^/]++)/([^/]++)/([^/]++)/d18f65(*:10416)' + .')' + .'|a4fb5/([^/]++)/([^/]++)/([^/]++)/da4fb5(*:10466)' + .'|b8e1a/([^/]++)/([^/]++)/([^/]++)/db8e1a(*:10515)' + .'|709f3/([^/]++)/([^/]++)/([^/]++)/d709f3(*:10564)' + .'|c(?' + .'|912a/([^/]++)/([^/]++)/([^/]++)/dc912a(*:10616)' + .'|6a64/([^/]++)/([^/]++)/([^/]++)/dc6a64(*:10664)' + .')' + .'|db306/([^/]++)/([^/]++)/([^/]++)/ddb306(*:10714)' + .')' + .'|/6(?' + .'|5(?' + .'|12bd/([^/]++)/([^/]++)/([^/]++)/6512bd(*:10772)' + .'|b9ee/([^/]++)/([^/]++)/([^/]++)/65b9ee(*:10820)' + .'|ded5/([^/]++)/([^/]++)/([^/]++)/65ded5(*:10868)' + .')' + .'|f(?' + .'|4922/([^/]++)/([^/]++)/([^/]++)/6f4922(*:10921)' + .'|3ef7/([^/]++)/([^/]++)/([^/]++)/6f3ef7(*:10969)' + .'|aa80/([^/]++)/([^/]++)/([^/]++)/6faa80(*:11017)' + .')' + .'|e(?' + .'|a(?' + .'|9ab/([^/]++)/([^/]++)/([^/]++)/6ea9ab(*:11073)' + .'|2ef/([^/]++)/([^/]++)/([^/]++)/6ea2ef(*:11120)' + .')' + .'|cbdd/([^/]++)/([^/]++)/([^/]++)/6ecbdd(*:11169)' + .')' + .'|3(?' + .'|64d3/([^/]++)/([^/]++)/([^/]++)/6364d3(*:11222)' + .'|dc7e/([^/]++)/([^/]++)/([^/]++)/63dc7e(*:11270)' + .'|923f/([^/]++)/([^/]++)/([^/]++)/63923f(*:11318)' + .')' + .'|c(?' + .'|8349/([^/]++)/([^/]++)/([^/]++)/6c8349(*:11371)' + .'|4b76/([^/]++)/([^/]++)/([^/]++)/6c4b76(*:11419)' + .'|dd60/([^/]++)/([^/]++)/([^/]++)/6cdd60(*:11467)' + .'|9882/([^/]++)/([^/]++)/([^/]++)/6c9882(*:11515)' + .'|524f/([^/]++)/([^/]++)/([^/]++)/6c524f(*:11563)' + .')' + .'|7(?' + .'|c6a1/([^/]++)/([^/]++)/([^/]++)/67c6a1(*:11616)' + .'|f7fb/([^/]++)/([^/]++)/([^/]++)/67f7fb(*:11664)' + .')' + .'|42e92/([^/]++)/([^/]++)/([^/]++)/642e92(*:11714)' + .'|6(?' + .'|f041/([^/]++)/([^/]++)/([^/]++)/66f041(*:11766)' + .'|808e/([^/]++)/([^/]++)/([^/]++)/66808e(*:11814)' + .'|3682/([^/]++)/([^/]++)/([^/]++)/663682(*:11862)' + .')' + .'|8(?' + .'|d30a/([^/]++)/([^/]++)/([^/]++)/68d30a(*:11915)' + .'|8396/([^/]++)/([^/]++)/([^/]++)/688396(*:11963)' + .'|5545/([^/]++)/([^/]++)/([^/]++)/685545(*:12011)' + .'|ce19/([^/]++)/([^/]++)/([^/]++)/68ce19(*:12059)' + .')' + .'|9(?' + .'|74ce/([^/]++)/([^/]++)/([^/]++)/6974ce(*:12112)' + .'|8d51/([^/]++)/([^/]++)/([^/]++)/698d51(*:12160)' + .'|adc1/([^/]++)/([^/]++)/([^/]++)/69adc1(*:12208)' + .'|cb3e/([^/]++)/([^/]++)/([^/]++)/69cb3e(*:12256)' + .')' + .'|da(?' + .'|900/([^/]++)/([^/]++)/([^/]++)/6da900(*:12309)' + .'|37d/([^/]++)/([^/]++)/([^/]++)/6da37d(*:12356)' + .')' + .'|21bf6/([^/]++)/([^/]++)/([^/]++)/621bf6(*:12406)' + .'|a9aed/([^/]++)/([^/]++)/([^/]++)/6a9aed(*:12455)' + .')' + .'|/9(?' + .'|b(?' + .'|f31c/([^/]++)/([^/]++)/([^/]++)/9bf31c(*:12513)' + .'|8619/([^/]++)/([^/]++)/([^/]++)/9b8619(*:12561)' + .'|04d1/([^/]++)/([^/]++)/([^/]++)/9b04d1(*:12609)' + .'|e40c/([^/]++)/([^/]++)/([^/]++)/9be40c(*:12657)' + .'|70e8/([^/]++)/([^/]++)/([^/]++)/9b70e8(*:12705)' + .')' + .'|8(?' + .'|f137/([^/]++)/([^/]++)/([^/]++)/98f137(*:12758)' + .'|dce8/([^/]++)/([^/]++)/([^/]++)/98dce8(*:12806)' + .'|72ed/([^/]++)/([^/]++)/([^/]++)/9872ed(*:12854)' + .'|b297/([^/]++)/([^/]++)/([^/]++)/98b297(*:12902)' + .')' + .'|a(?' + .'|1158/([^/]++)/([^/]++)/([^/]++)/9a1158(*:12955)' + .'|9687/([^/]++)/([^/]++)/([^/]++)/9a9687(*:13003)' + .')' + .'|f(?' + .'|6140/([^/]++)/([^/]++)/([^/]++)/9f6140(*:13056)' + .'|c3d7/([^/]++)/([^/]++)/([^/]++)/9fc3d7(*:13104)' + .'|d818/([^/]++)/([^/]++)/([^/]++)/9fd818(*:13152)' + .')' + .'|7(?' + .'|78d5/([^/]++)/([^/]++)/([^/]++)/9778d5(*:13205)' + .'|6652/([^/]++)/([^/]++)/([^/]++)/976652(*:13253)' + .'|9d47/([^/]++)/([^/]++)/([^/]++)/979d47(*:13301)' + .')' + .'|3db85/([^/]++)/([^/]++)/([^/]++)/93db85(*:13351)' + .'|2c(?' + .'|c22/([^/]++)/([^/]++)/([^/]++)/92cc22(*:13403)' + .'|8c9/([^/]++)/([^/]++)/([^/]++)/92c8c9(*:13450)' + .')' + .'|03ce9/([^/]++)/([^/]++)/([^/]++)/903ce9(*:13500)' + .'|6da2f/([^/]++)/([^/]++)/([^/]++)/96da2f(*:13549)' + .'|d(?' + .'|cb88/([^/]++)/([^/]++)/([^/]++)/9dcb88(*:13601)' + .'|fcd5/([^/]++)/([^/]++)/([^/]++)/9dfcd5(*:13649)' + .'|e6d1/([^/]++)/([^/]++)/([^/]++)/9de6d1(*:13697)' + .')' + .'|c(?' + .'|fdf1/([^/]++)/([^/]++)/([^/]++)/9cfdf1(*:13750)' + .'|838d/([^/]++)/([^/]++)/([^/]++)/9c838d(*:13798)' + .')' + .'|18(?' + .'|890/([^/]++)/([^/]++)/([^/]++)/918890(*:13851)' + .'|317/([^/]++)/([^/]++)/([^/]++)/918317(*:13898)' + .')' + .'|4(?' + .'|f6d7/([^/]++)/([^/]++)/([^/]++)/94f6d7(*:13951)' + .'|1e1a/([^/]++)/([^/]++)/([^/]++)/941e1a(*:13999)' + .'|31c8/([^/]++)/([^/]++)/([^/]++)/9431c8(*:14047)' + .'|61cc/([^/]++)/([^/]++)/([^/]++)/9461cc(*:14095)' + .')' + .'|50a41/([^/]++)/([^/]++)/([^/]++)/950a41(*:14145)' + .')' + .'|/7(?' + .'|0(?' + .'|efdf/([^/]++)/([^/]++)/([^/]++)/70efdf(*:14203)' + .'|5f21/([^/]++)/([^/]++)/([^/]++)/705f21(*:14251)' + .'|c639/([^/]++)/([^/]++)/([^/]++)/70c639(*:14299)' + .')' + .'|2b32a/([^/]++)/([^/]++)/([^/]++)/72b32a(*:14349)' + .'|f(?' + .'|39f8/([^/]++)/([^/]++)/([^/]++)/7f39f8(*:14401)' + .'|6ffa/([^/]++)/([^/]++)/([^/]++)/7f6ffa(*:14449)' + .'|1(?' + .'|de2/([^/]++)/([^/]++)/([^/]++)/7f1de2(*:14500)' + .'|00b/([^/]++)/([^/]++)/([^/]++)/7f100b(*:14547)' + .')' + .'|e1f8/([^/]++)/([^/]++)/([^/]++)/7fe1f8(*:14596)' + .')' + .'|3(?' + .'|5b90/([^/]++)/([^/]++)/([^/]++)/735b90(*:14649)' + .'|278a/([^/]++)/([^/]++)/([^/]++)/73278a(*:14697)' + .'|80ad/([^/]++)/([^/]++)/([^/]++)/7380ad(*:14745)' + .')' + .'|cbbc4/([^/]++)/([^/]++)/([^/]++)/7cbbc4(*:14795)' + .'|6(?' + .'|4796/([^/]++)/([^/]++)/([^/]++)/764796(*:14847)' + .'|dc61/([^/]++)/([^/]++)/([^/]++)/76dc61(*:14895)' + .')' + .'|e(?' + .'|f605/([^/]++)/([^/]++)/([^/]++)/7ef605(*:14948)' + .'|7757/([^/]++)/([^/]++)/([^/]++)/7e7757(*:14996)' + .'|a(?' + .'|be3/([^/]++)/([^/]++)/([^/]++)/7eabe3(*:15047)' + .'|cb5/([^/]++)/([^/]++)/([^/]++)/7eacb5(*:15094)' + .')' + .')' + .'|5(?' + .'|7b50/([^/]++)/([^/]++)/([^/]++)/757b50(*:15148)' + .'|8874/([^/]++)/([^/]++)/([^/]++)/758874(*:15196)' + .'|fc09/([^/]++)/([^/]++)/([^/]++)/75fc09(*:15244)' + .')' + .'|4(?' + .'|db12/([^/]++)/([^/]++)/([^/]++)/74db12(*:15297)' + .'|071a/([^/]++)/([^/]++)/([^/]++)/74071a(*:15345)' + .')' + .'|a614f/([^/]++)/([^/]++)/([^/]++)/7a614f(*:15395)' + .'|d04bb/([^/]++)/([^/]++)/([^/]++)/7d04bb(*:15444)' + .')' + .'|/3(?' + .'|c(?' + .'|59dc/([^/]++)/([^/]++)/([^/]++)/3c59dc(*:15502)' + .'|ec07/([^/]++)/([^/]++)/([^/]++)/3cec07(*:15550)' + .'|7781/([^/]++)/([^/]++)/([^/]++)/3c7781(*:15598)' + .'|f166/([^/]++)/([^/]++)/([^/]++)/3cf166(*:15646)' + .')' + .'|7(?' + .'|693c/([^/]++)/([^/]++)/([^/]++)/37693c(*:15699)' + .'|a749/([^/]++)/([^/]++)/([^/]++)/37a749(*:15747)' + .'|bc2f/([^/]++)/([^/]++)/([^/]++)/37bc2f(*:15795)' + .'|1bce/([^/]++)/([^/]++)/([^/]++)/371bce(*:15843)' + .')' + .'|3(?' + .'|e75f/([^/]++)/([^/]++)/([^/]++)/33e75f(*:15896)' + .'|5f53/([^/]++)/([^/]++)/([^/]++)/335f53(*:15944)' + .')' + .'|4(?' + .'|1(?' + .'|73c/([^/]++)/([^/]++)/([^/]++)/34173c(*:16000)' + .'|6a7/([^/]++)/([^/]++)/([^/]++)/3416a7(*:16047)' + .')' + .'|ed06/([^/]++)/([^/]++)/([^/]++)/34ed06(*:16096)' + .')' + .'|2(?' + .'|95c7/([^/]++)/([^/]++)/([^/]++)/3295c7(*:16149)' + .'|bb90/([^/]++)/([^/]++)/([^/]++)/32bb90(*:16197)' + .'|0722/([^/]++)/([^/]++)/([^/]++)/320722(*:16245)' + .')' + .'|5(?' + .'|f4a8/([^/]++)/([^/]++)/([^/]++)/35f4a8(*:16298)' + .'|7a6f/([^/]++)/([^/]++)/([^/]++)/357a6f(*:16346)' + .'|2fe2/([^/]++)/([^/]++)/([^/]++)/352fe2(*:16394)' + .'|0510/([^/]++)/([^/]++)/([^/]++)/350510(*:16442)' + .')' + .'|ef815/([^/]++)/([^/]++)/([^/]++)/3ef815(*:16492)' + .'|8(?' + .'|b3ef/([^/]++)/([^/]++)/([^/]++)/38b3ef(*:16544)' + .'|af86/([^/]++)/([^/]++)/([^/]++)/38af86(*:16592)' + .'|db3a/([^/]++)/([^/]++)/([^/]++)/38db3a(*:16640)' + .')' + .'|d(?' + .'|ef18/([^/]++)/([^/]++)/([^/]++)/3def18(*:16693)' + .'|d48a/([^/]++)/([^/]++)/([^/]++)/3dd48a(*:16741)' + .')' + .'|9(?' + .'|88c7/([^/]++)/([^/]++)/([^/]++)/3988c7(*:16794)' + .'|0597/([^/]++)/([^/]++)/([^/]++)/390597(*:16842)' + .'|461a/([^/]++)/([^/]++)/([^/]++)/39461a(*:16890)' + .')' + .'|6(?' + .'|3663/([^/]++)/([^/]++)/([^/]++)/363663(*:16943)' + .'|44a6/([^/]++)/([^/]++)/([^/]++)/3644a6(*:16991)' + .'|660e/([^/]++)/([^/]++)/([^/]++)/36660e(*:17039)' + .')' + .'|1(?' + .'|fefc/([^/]++)/([^/]++)/([^/]++)/31fefc(*:17092)' + .'|0dcb/([^/]++)/([^/]++)/([^/]++)/310dcb(*:17140)' + .')' + .'|b8a61/([^/]++)/([^/]++)/([^/]++)/3b8a61(*:17190)' + .'|fe94a/([^/]++)/([^/]++)/([^/]++)/3fe94a(*:17239)' + .'|ad7c2/([^/]++)/([^/]++)/([^/]++)/3ad7c2(*:17288)' + .')' + .'|/b(?' + .'|6(?' + .'|d767/([^/]++)/([^/]++)/([^/]++)/b6d767(*:17346)' + .'|f047/([^/]++)/([^/]++)/([^/]++)/b6f047(*:17394)' + .')' + .'|53(?' + .'|b3a/([^/]++)/([^/]++)/([^/]++)/b53b3a(*:17447)' + .'|4ba/([^/]++)/([^/]++)/([^/]++)/b534ba(*:17494)' + .')' + .'|3(?' + .'|e3e3/([^/]++)/([^/]++)/([^/]++)/b3e3e3(*:17547)' + .'|967a/([^/]++)/([^/]++)/([^/]++)/b3967a(*:17595)' + .')' + .'|7(?' + .'|3ce3/([^/]++)/([^/]++)/([^/]++)/b73ce3(*:17648)' + .'|b16e/([^/]++)/([^/]++)/([^/]++)/b7b16e(*:17696)' + .')' + .'|d(?' + .'|4c9a/([^/]++)/([^/]++)/([^/]++)/bd4c9a(*:17749)' + .'|686f/([^/]++)/([^/]++)/([^/]++)/bd686f(*:17797)' + .')' + .'|f8229/([^/]++)/([^/]++)/([^/]++)/bf8229(*:17847)' + .'|1(?' + .'|d10e/([^/]++)/([^/]++)/([^/]++)/b1d10e(*:17899)' + .'|a59b/([^/]++)/([^/]++)/([^/]++)/b1a59b(*:17947)' + .')' + .'|c(?' + .'|be33/([^/]++)/([^/]++)/([^/]++)/bcbe33(*:18000)' + .'|6dc4/([^/]++)/([^/]++)/([^/]++)/bc6dc4(*:18048)' + .'|a82e/([^/]++)/([^/]++)/([^/]++)/bca82e(*:18096)' + .')' + .'|e(?' + .'|83ab/([^/]++)/([^/]++)/([^/]++)/be83ab(*:18149)' + .'|ed13/([^/]++)/([^/]++)/([^/]++)/beed13(*:18197)' + .')' + .'|2eb73/([^/]++)/([^/]++)/([^/]++)/b2eb73(*:18247)' + .'|83aac/([^/]++)/([^/]++)/([^/]++)/b83aac(*:18296)' + .'|ac916/([^/]++)/([^/]++)/([^/]++)/bac916(*:18345)' + .'|b(?' + .'|f94b/([^/]++)/([^/]++)/([^/]++)/bbf94b(*:18397)' + .'|cbff/([^/]++)/([^/]++)/([^/]++)/bbcbff(*:18445)' + .')' + .'|9228e/([^/]++)/([^/]++)/([^/]++)/b9228e(*:18495)' + .')' + .'|/0(?' + .'|2(?' + .'|e74f/([^/]++)/([^/]++)/([^/]++)/02e74f(*:18553)' + .'|522a/([^/]++)/([^/]++)/([^/]++)/02522a(*:18601)' + .'|66e3/([^/]++)/([^/]++)/([^/]++)/0266e3(*:18649)' + .')' + .'|9(?' + .'|3f65/([^/]++)/([^/]++)/([^/]++)/093f65(*:18702)' + .'|1d58/([^/]++)/([^/]++)/([^/]++)/091d58(*:18750)' + .')' + .'|7(?' + .'|2b03/([^/]++)/([^/]++)/([^/]++)/072b03(*:18803)' + .'|e1cd/([^/]++)/([^/]++)/([^/]++)/07e1cd(*:18851)' + .'|7(?' + .'|7d5/([^/]++)/([^/]++)/([^/]++)/0777d5(*:18902)' + .'|e29/([^/]++)/([^/]++)/([^/]++)/077e29(*:18949)' + .')' + .'|cdfd/([^/]++)/([^/]++)/([^/]++)/07cdfd(*:18998)' + .')' + .'|3(?' + .'|afdb/([^/]++)/([^/]++)/([^/]++)/03afdb(*:19051)' + .'|36dc/([^/]++)/([^/]++)/([^/]++)/0336dc(*:19099)' + .'|c6b0/([^/]++)/([^/]++)/([^/]++)/03c6b0(*:19147)' + .'|53ab/([^/]++)/([^/]++)/([^/]++)/0353ab(*:19195)' + .')' + .'|6(?' + .'|9059/([^/]++)/([^/]++)/([^/]++)/069059(*:19248)' + .'|4096/([^/]++)/([^/]++)/([^/]++)/064096(*:19296)' + .'|0ad9/([^/]++)/([^/]++)/([^/]++)/060ad9(*:19344)' + .'|138b/([^/]++)/([^/]++)/([^/]++)/06138b(*:19392)' + .'|eb61/([^/]++)/([^/]++)/([^/]++)/06eb61(*:19440)' + .')' + .'|1(?' + .'|3(?' + .'|d40/([^/]++)/([^/]++)/([^/]++)/013d40(*:19496)' + .'|86b/([^/]++)/([^/]++)/([^/]++)/01386b(*:19543)' + .')' + .'|161a/([^/]++)/([^/]++)/([^/]++)/01161a(*:19592)' + .'|9d38/([^/]++)/([^/]++)/([^/]++)/019d38(*:19640)' + .')' + .'|f(?' + .'|28b5/([^/]++)/([^/]++)/([^/]++)/0f28b5(*:19693)' + .'|49c8/([^/]++)/([^/]++)/([^/]++)/0f49c8(*:19741)' + .')' + .'|a(?' + .'|09c8/([^/]++)/([^/]++)/([^/]++)/0a09c8(*:19794)' + .'|a188/([^/]++)/([^/]++)/([^/]++)/0aa188(*:19842)' + .')' + .'|0(?' + .'|6f52/([^/]++)/([^/]++)/([^/]++)/006f52(*:19895)' + .'|4114/([^/]++)/([^/]++)/([^/]++)/004114(*:19943)' + .'|ec53/([^/]++)/([^/]++)/([^/]++)/00ec53(*:19991)' + .')' + .'|4(?' + .'|5117/([^/]++)/([^/]++)/([^/]++)/045117(*:20044)' + .'|0259/([^/]++)/([^/]++)/([^/]++)/040259(*:20092)' + .')' + .'|84b6f/([^/]++)/([^/]++)/([^/]++)/084b6f(*:20142)' + .'|e(?' + .'|6597/([^/]++)/([^/]++)/([^/]++)/0e6597(*:20194)' + .'|0193/([^/]++)/([^/]++)/([^/]++)/0e0193(*:20242)' + .')' + .'|bb4ae/([^/]++)/([^/]++)/([^/]++)/0bb4ae(*:20292)' + .'|5(?' + .'|049e/([^/]++)/([^/]++)/([^/]++)/05049e(*:20344)' + .'|84ce/([^/]++)/([^/]++)/([^/]++)/0584ce(*:20392)' + .'|f971/([^/]++)/([^/]++)/([^/]++)/05f971(*:20440)' + .')' + .'|c74b7/([^/]++)/([^/]++)/([^/]++)/0c74b7(*:20490)' + .'|d(?' + .'|0fd7/([^/]++)/([^/]++)/([^/]++)/0d0fd7(*:20542)' + .'|eb1c/([^/]++)/([^/]++)/([^/]++)/0deb1c(*:20590)' + .')' + .')' + .'|/f(?' + .'|7(?' + .'|1(?' + .'|771/([^/]++)/([^/]++)/([^/]++)/f71771(*:20652)' + .'|849/([^/]++)/([^/]++)/([^/]++)/f71849(*:20699)' + .')' + .'|e6c8/([^/]++)/([^/]++)/([^/]++)/f7e6c8(*:20748)' + .'|6640/([^/]++)/([^/]++)/([^/]++)/f76640(*:20796)' + .'|3b76/([^/]++)/([^/]++)/([^/]++)/f73b76(*:20844)' + .'|4909/([^/]++)/([^/]++)/([^/]++)/f74909(*:20892)' + .'|70b6/([^/]++)/([^/]++)/([^/]++)/f770b6(*:20940)' + .')' + .'|4(?' + .'|57c5/([^/]++)/([^/]++)/([^/]++)/f457c5(*:20993)' + .'|b9ec/([^/]++)/([^/]++)/([^/]++)/f4b9ec(*:21041)' + .'|f6dc/([^/]++)/([^/]++)/([^/]++)/f4f6dc(*:21089)' + .')' + .'|c(?' + .'|490c/([^/]++)/([^/]++)/([^/]++)/fc490c(*:21142)' + .'|2213/([^/]++)/([^/]++)/([^/]++)/fc2213(*:21190)' + .'|cb60/([^/]++)/([^/]++)/([^/]++)/fccb60(*:21238)' + .')' + .'|b(?' + .'|d793/([^/]++)/([^/]++)/([^/]++)/fbd793(*:21291)' + .'|7b9f/([^/]++)/([^/]++)/([^/]++)/fb7b9f(*:21339)' + .')' + .'|0(?' + .'|33ab/([^/]++)/([^/]++)/([^/]++)/f033ab(*:21392)' + .'|935e/([^/]++)/([^/]++)/([^/]++)/f0935e(*:21440)' + .')' + .'|e(?' + .'|9fc2/([^/]++)/([^/]++)/([^/]++)/fe9fc2(*:21493)' + .'|131d/([^/]++)/([^/]++)/([^/]++)/fe131d(*:21541)' + .'|73f6/([^/]++)/([^/]++)/([^/]++)/fe73f6(*:21589)' + .')' + .'|8(?' + .'|9913/([^/]++)/([^/]++)/([^/]++)/f89913(*:21642)' + .'|c1f2/([^/]++)/([^/]++)/([^/]++)/f8c1f2(*:21690)' + .'|5454/([^/]++)/([^/]++)/([^/]++)/f85454(*:21738)' + .')' + .'|2(?' + .'|2170/([^/]++)/([^/]++)/([^/]++)/f22170(*:21791)' + .'|fc99/([^/]++)/([^/]++)/([^/]++)/f2fc99(*:21839)' + .')' + .'|a(?' + .'|7cdf/([^/]++)/([^/]++)/([^/]++)/fa7cdf(*:21892)' + .'|a9af/([^/]++)/([^/]++)/([^/]++)/faa9af(*:21940)' + .')' + .'|340f1/([^/]++)/([^/]++)/([^/]++)/f340f1(*:21990)' + .'|9(?' + .'|0f2a/([^/]++)/([^/]++)/([^/]++)/f90f2a(*:22042)' + .'|b902/([^/]++)/([^/]++)/([^/]++)/f9b902(*:22090)' + .')' + .'|fd52f/([^/]++)/([^/]++)/([^/]++)/ffd52f(*:22140)' + .'|61d69/([^/]++)/([^/]++)/([^/]++)/f61d69(*:22189)' + .'|5f859/([^/]++)/([^/]++)/([^/]++)/f5f859(*:22238)' + .'|1b6f2/([^/]++)/([^/]++)/([^/]++)/f1b6f2(*:22287)' + .')' + .'|/2(?' + .'|8(?' + .'|3802/([^/]++)/([^/]++)/([^/]++)/283802(*:22345)' + .'|dd2c/([^/]++)/([^/]++)/([^/]++)/28dd2c(*:22393)' + .'|9dff/([^/]++)/([^/]++)/([^/]++)/289dff(*:22441)' + .'|f0b8/([^/]++)/([^/]++)/([^/]++)/28f0b8(*:22489)' + .')' + .'|a(?' + .'|38a4/([^/]++)/([^/]++)/([^/]++)/2a38a4(*:22542)' + .'|79ea/([^/]++)/([^/]++)/([^/]++)/2a79ea(*:22590)' + .')' + .'|6(?' + .'|657d/([^/]++)/([^/]++)/([^/]++)/26657d(*:22643)' + .'|e359/([^/]++)/([^/]++)/([^/]++)/26e359(*:22691)' + .'|3373/([^/]++)/([^/]++)/([^/]++)/263373(*:22739)' + .')' + .'|7(?' + .'|23d0/([^/]++)/([^/]++)/([^/]++)/2723d0(*:22792)' + .'|4ad4/([^/]++)/([^/]++)/([^/]++)/274ad4(*:22840)' + .')' + .'|b(?' + .'|4492/([^/]++)/([^/]++)/([^/]++)/2b4492(*:22893)' + .'|24d4/([^/]++)/([^/]++)/([^/]++)/2b24d4(*:22941)' + .')' + .'|0(?' + .'|2cb9/([^/]++)/([^/]++)/([^/]++)/202cb9(*:22994)' + .'|f075/([^/]++)/([^/]++)/([^/]++)/20f075(*:23042)' + .'|50e0/([^/]++)/([^/]++)/([^/]++)/2050e0(*:23090)' + .')' + .'|f(?' + .'|2b26/([^/]++)/([^/]++)/([^/]++)/2f2b26(*:23143)' + .'|5570/([^/]++)/([^/]++)/([^/]++)/2f5570(*:23191)' + .')' + .'|4(?' + .'|b16f/([^/]++)/([^/]++)/([^/]++)/24b16f(*:23244)' + .'|8e84/([^/]++)/([^/]++)/([^/]++)/248e84(*:23292)' + .'|21fc/([^/]++)/([^/]++)/([^/]++)/2421fc(*:23340)' + .')' + .'|5(?' + .'|b282/([^/]++)/([^/]++)/([^/]++)/25b282(*:23393)' + .'|0cf8/([^/]++)/([^/]++)/([^/]++)/250cf8(*:23441)' + .'|ddc0/([^/]++)/([^/]++)/([^/]++)/25ddc0(*:23489)' + .')' + .'|18a0a/([^/]++)/([^/]++)/([^/]++)/218a0a(*:23539)' + .')' + .'|/5(?' + .'|4229a/([^/]++)/([^/]++)/([^/]++)/54229a(*:23594)' + .'|f(?' + .'|93f9/([^/]++)/([^/]++)/([^/]++)/5f93f9(*:23646)' + .'|d0b3/([^/]++)/([^/]++)/([^/]++)/5fd0b3(*:23694)' + .')' + .'|ef(?' + .'|0(?' + .'|59/([^/]++)/([^/]++)/([^/]++)/5ef059(*:23750)' + .'|b4/([^/]++)/([^/]++)/([^/]++)/5ef0b4(*:23796)' + .')' + .'|698/([^/]++)/([^/]++)/([^/]++)/5ef698(*:23844)' + .')' + .'|8(?' + .'|78a7/([^/]++)/([^/]++)/([^/]++)/5878a7(*:23897)' + .'|a2fc/([^/]++)/([^/]++)/([^/]++)/58a2fc(*:23945)' + .'|238e/([^/]++)/([^/]++)/([^/]++)/58238e(*:23993)' + .')' + .'|7(?' + .'|aeee/([^/]++)/([^/]++)/([^/]++)/57aeee(*:24046)' + .'|7(?' + .'|ef1/([^/]++)/([^/]++)/([^/]++)/577ef1(*:24097)' + .'|bcc/([^/]++)/([^/]++)/([^/]++)/577bcc(*:24144)' + .')' + .'|37c6/([^/]++)/([^/]++)/([^/]++)/5737c6(*:24193)' + .')' + .'|3(?' + .'|9fd5/([^/]++)/([^/]++)/([^/]++)/539fd5(*:24246)' + .'|c3bc/([^/]++)/([^/]++)/([^/]++)/53c3bc(*:24294)' + .')' + .'|5(?' + .'|5d67/([^/]++)/([^/]++)/([^/]++)/555d67(*:24347)' + .'|0a14/([^/]++)/([^/]++)/([^/]++)/550a14(*:24395)' + .'|9cb9/([^/]++)/([^/]++)/([^/]++)/559cb9(*:24443)' + .'|a7cf/([^/]++)/([^/]++)/([^/]++)/55a7cf(*:24491)' + .')' + .'|02e4a/([^/]++)/([^/]++)/([^/]++)/502e4a(*:24541)' + .'|b8add/([^/]++)/([^/]++)/([^/]++)/5b8add(*:24590)' + .'|2720e/([^/]++)/([^/]++)/([^/]++)/52720e(*:24639)' + .'|a4b25/([^/]++)/([^/]++)/([^/]++)/5a4b25(*:24688)' + .'|1d92b/([^/]++)/([^/]++)/([^/]++)/51d92b(*:24737)' + .'|98b3e/([^/]++)/([^/]++)/([^/]++)/598b3e(*:24786)' + .')' + .')$}sD', + 24786 => '{^(?' + .'|/5(?' + .'|b69b9/([^/]++)/([^/]++)/([^/]++)/5b69b9(*:24837)' + .'|9(?' + .'|b90e/([^/]++)/([^/]++)/([^/]++)/59b90e(*:24889)' + .'|c330/([^/]++)/([^/]++)/([^/]++)/59c330(*:24937)' + .')' + .'|3(?' + .'|fde9/([^/]++)/([^/]++)/([^/]++)/53fde9(*:24990)' + .'|e3a7/([^/]++)/([^/]++)/([^/]++)/53e3a7(*:25038)' + .')' + .'|e(?' + .'|a164/([^/]++)/([^/]++)/([^/]++)/5ea164(*:25091)' + .'|3881/([^/]++)/([^/]++)/([^/]++)/5e3881(*:25139)' + .'|9f92/([^/]++)/([^/]++)/([^/]++)/5e9f92(*:25187)' + .'|c91a/([^/]++)/([^/]++)/([^/]++)/5ec91a(*:25235)' + .')' + .'|7(?' + .'|3703/([^/]++)/([^/]++)/([^/]++)/573703(*:25288)' + .'|51ec/([^/]++)/([^/]++)/([^/]++)/5751ec(*:25336)' + .'|05e1/([^/]++)/([^/]++)/([^/]++)/5705e1(*:25384)' + .')' + .'|8(?' + .'|ae74/([^/]++)/([^/]++)/([^/]++)/58ae74(*:25437)' + .'|d4d1/([^/]++)/([^/]++)/([^/]++)/58d4d1(*:25485)' + .'|07a6/([^/]++)/([^/]++)/([^/]++)/5807a6(*:25533)' + .'|e4d4/([^/]++)/([^/]++)/([^/]++)/58e4d4(*:25581)' + .')' + .'|d(?' + .'|44ee/([^/]++)/([^/]++)/([^/]++)/5d44ee(*:25634)' + .'|d9db/([^/]++)/([^/]++)/([^/]++)/5dd9db(*:25682)' + .')' + .'|5(?' + .'|b37c/([^/]++)/([^/]++)/([^/]++)/55b37c(*:25735)' + .'|743c/([^/]++)/([^/]++)/([^/]++)/55743c(*:25783)' + .'|6f39/([^/]++)/([^/]++)/([^/]++)/556f39(*:25831)' + .')' + .'|c(?' + .'|0492/([^/]++)/([^/]++)/([^/]++)/5c0492(*:25884)' + .'|572e/([^/]++)/([^/]++)/([^/]++)/5c572e(*:25932)' + .'|9362/([^/]++)/([^/]++)/([^/]++)/5c9362(*:25980)' + .')' + .'|4(?' + .'|8731/([^/]++)/([^/]++)/([^/]++)/548731(*:26033)' + .'|a367/([^/]++)/([^/]++)/([^/]++)/54a367(*:26081)' + .')' + .'|0(?' + .'|0e75/([^/]++)/([^/]++)/([^/]++)/500e75(*:26134)' + .'|c3d7/([^/]++)/([^/]++)/([^/]++)/50c3d7(*:26182)' + .')' + .'|f(?' + .'|2c22/([^/]++)/([^/]++)/([^/]++)/5f2c22(*:26235)' + .'|0f5e/([^/]++)/([^/]++)/([^/]++)/5f0f5e(*:26283)' + .')' + .'|1ef18/([^/]++)/([^/]++)/([^/]++)/51ef18(*:26333)' + .')' + .'|/b(?' + .'|5(?' + .'|b41f/([^/]++)/([^/]++)/([^/]++)/b5b41f(*:26391)' + .'|dc4e/([^/]++)/([^/]++)/([^/]++)/b5dc4e(*:26439)' + .'|6a18/([^/]++)/([^/]++)/([^/]++)/b56a18(*:26487)' + .'|5ec2/([^/]++)/([^/]++)/([^/]++)/b55ec2(*:26535)' + .')' + .'|337e8/([^/]++)/([^/]++)/([^/]++)/b337e8(*:26585)' + .'|a(?' + .'|2fd3/([^/]++)/([^/]++)/([^/]++)/ba2fd3(*:26637)' + .'|3866/([^/]++)/([^/]++)/([^/]++)/ba3866(*:26685)' + .')' + .'|2(?' + .'|eeb7/([^/]++)/([^/]++)/([^/]++)/b2eeb7(*:26738)' + .'|f627/([^/]++)/([^/]++)/([^/]++)/b2f627(*:26786)' + .')' + .'|7(?' + .'|3dfe/([^/]++)/([^/]++)/([^/]++)/b73dfe(*:26839)' + .'|bb35/([^/]++)/([^/]++)/([^/]++)/b7bb35(*:26887)' + .'|ee6f/([^/]++)/([^/]++)/([^/]++)/b7ee6f(*:26935)' + .'|892f/([^/]++)/([^/]++)/([^/]++)/b7892f(*:26983)' + .'|0683/([^/]++)/([^/]++)/([^/]++)/b70683(*:27031)' + .')' + .'|4(?' + .'|288d/([^/]++)/([^/]++)/([^/]++)/b4288d(*:27084)' + .'|a528/([^/]++)/([^/]++)/([^/]++)/b4a528(*:27132)' + .')' + .'|e(?' + .'|3159/([^/]++)/([^/]++)/([^/]++)/be3159(*:27185)' + .'|b22f/([^/]++)/([^/]++)/([^/]++)/beb22f(*:27233)' + .'|a595/([^/]++)/([^/]++)/([^/]++)/bea595(*:27281)' + .')' + .'|1(?' + .'|eec3/([^/]++)/([^/]++)/([^/]++)/b1eec3(*:27334)' + .'|37fd/([^/]++)/([^/]++)/([^/]++)/b137fd(*:27382)' + .')' + .'|0(?' + .'|56eb/([^/]++)/([^/]++)/([^/]++)/b056eb(*:27435)' + .'|b183/([^/]++)/([^/]++)/([^/]++)/b0b183(*:27483)' + .')' + .'|f6276/([^/]++)/([^/]++)/([^/]++)/bf6276(*:27533)' + .'|6(?' + .'|edc1/([^/]++)/([^/]++)/([^/]++)/b6edc1(*:27585)' + .'|a108/([^/]++)/([^/]++)/([^/]++)/b6a108(*:27633)' + .')' + .'|86e8d/([^/]++)/([^/]++)/([^/]++)/b86e8d(*:27683)' + .')' + .'|/2(?' + .'|8(?' + .'|5e19/([^/]++)/([^/]++)/([^/]++)/285e19(*:27741)' + .'|2(?' + .'|3f4/([^/]++)/([^/]++)/([^/]++)/2823f4(*:27792)' + .'|67a/([^/]++)/([^/]++)/([^/]++)/28267a(*:27839)' + .')' + .'|8cc0/([^/]++)/([^/]++)/([^/]++)/288cc0(*:27888)' + .'|7e03/([^/]++)/([^/]++)/([^/]++)/287e03(*:27936)' + .')' + .'|d(?' + .'|6cc4/([^/]++)/([^/]++)/([^/]++)/2d6cc4(*:27989)' + .'|ea61/([^/]++)/([^/]++)/([^/]++)/2dea61(*:28037)' + .'|ace7/([^/]++)/([^/]++)/([^/]++)/2dace7(*:28085)' + .')' + .'|b(?' + .'|8a61/([^/]++)/([^/]++)/([^/]++)/2b8a61(*:28138)' + .'|b232/([^/]++)/([^/]++)/([^/]++)/2bb232(*:28186)' + .'|a596/([^/]++)/([^/]++)/([^/]++)/2ba596(*:28234)' + .'|cab9/([^/]++)/([^/]++)/([^/]++)/2bcab9(*:28282)' + .')' + .'|9(?' + .'|8f95/([^/]++)/([^/]++)/([^/]++)/298f95(*:28335)' + .'|1597/([^/]++)/([^/]++)/([^/]++)/291597(*:28383)' + .')' + .'|58be1/([^/]++)/([^/]++)/([^/]++)/258be1(*:28433)' + .'|3(?' + .'|3509/([^/]++)/([^/]++)/([^/]++)/233509(*:28485)' + .'|ce18/([^/]++)/([^/]++)/([^/]++)/23ce18(*:28533)' + .')' + .'|6(?' + .'|dd0d/([^/]++)/([^/]++)/([^/]++)/26dd0d(*:28586)' + .'|408f/([^/]++)/([^/]++)/([^/]++)/26408f(*:28634)' + .')' + .'|f(?' + .'|37d1/([^/]++)/([^/]++)/([^/]++)/2f37d1(*:28687)' + .'|885d/([^/]++)/([^/]++)/([^/]++)/2f885d(*:28735)' + .')' + .'|2(?' + .'|91d2/([^/]++)/([^/]++)/([^/]++)/2291d2(*:28788)' + .'|ac3c/([^/]++)/([^/]++)/([^/]++)/22ac3c(*:28836)' + .'|fb0c/([^/]++)/([^/]++)/([^/]++)/22fb0c(*:28884)' + .')' + .'|4(?' + .'|6819/([^/]++)/([^/]++)/([^/]++)/246819(*:28937)' + .'|896e/([^/]++)/([^/]++)/([^/]++)/24896e(*:28985)' + .')' + .'|a(?' + .'|fe45/([^/]++)/([^/]++)/([^/]++)/2afe45(*:29038)' + .'|084e/([^/]++)/([^/]++)/([^/]++)/2a084e(*:29086)' + .'|9d12/([^/]++)/([^/]++)/([^/]++)/2a9d12(*:29134)' + .'|b564/([^/]++)/([^/]++)/([^/]++)/2ab564(*:29182)' + .')' + .'|1(?' + .'|7eed/([^/]++)/([^/]++)/([^/]++)/217eed(*:29235)' + .'|0f76/([^/]++)/([^/]++)/([^/]++)/210f76(*:29283)' + .')' + .'|e65f2/([^/]++)/([^/]++)/([^/]++)/2e65f2(*:29333)' + .'|ca65f/([^/]++)/([^/]++)/([^/]++)/2ca65f(*:29382)' + .'|0aee3/([^/]++)/([^/]++)/([^/]++)/20aee3(*:29431)' + .')' + .'|/e(?' + .'|8(?' + .'|c065/([^/]++)/([^/]++)/([^/]++)/e8c065(*:29489)' + .'|20a4/([^/]++)/([^/]++)/([^/]++)/e820a4(*:29537)' + .')' + .'|2(?' + .'|230b/([^/]++)/([^/]++)/([^/]++)/e2230b(*:29590)' + .'|a2dc/([^/]++)/([^/]++)/([^/]++)/e2a2dc(*:29638)' + .'|05ee/([^/]++)/([^/]++)/([^/]++)/e205ee(*:29686)' + .')' + .'|b(?' + .'|d962/([^/]++)/([^/]++)/([^/]++)/ebd962(*:29739)' + .'|6fdc/([^/]++)/([^/]++)/([^/]++)/eb6fdc(*:29787)' + .')' + .'|d(?' + .'|265b/([^/]++)/([^/]++)/([^/]++)/ed265b(*:29840)' + .'|fbe1/([^/]++)/([^/]++)/([^/]++)/edfbe1(*:29888)' + .'|e7e2/([^/]++)/([^/]++)/([^/]++)/ede7e2(*:29936)' + .')' + .'|6(?' + .'|b4b2/([^/]++)/([^/]++)/([^/]++)/e6b4b2(*:29989)' + .'|cb2a/([^/]++)/([^/]++)/([^/]++)/e6cb2a(*:30037)' + .')' + .'|5(?' + .'|f6ad/([^/]++)/([^/]++)/([^/]++)/e5f6ad(*:30090)' + .'|55eb/([^/]++)/([^/]++)/([^/]++)/e555eb(*:30138)' + .'|841d/([^/]++)/([^/]++)/([^/]++)/e5841d(*:30186)' + .'|7c6b/([^/]++)/([^/]++)/([^/]++)/e57c6b(*:30234)' + .')' + .'|aae33/([^/]++)/([^/]++)/([^/]++)/eaae33(*:30284)' + .'|4(?' + .'|bb4c/([^/]++)/([^/]++)/([^/]++)/e4bb4c(*:30336)' + .'|9b8b/([^/]++)/([^/]++)/([^/]++)/e49b8b(*:30384)' + .')' + .'|7(?' + .'|0611/([^/]++)/([^/]++)/([^/]++)/e70611(*:30437)' + .'|f8a7/([^/]++)/([^/]++)/([^/]++)/e7f8a7(*:30485)' + .'|44f9/([^/]++)/([^/]++)/([^/]++)/e744f9(*:30533)' + .')' + .'|9(?' + .'|95f9/([^/]++)/([^/]++)/([^/]++)/e995f9(*:30586)' + .'|4550/([^/]++)/([^/]++)/([^/]++)/e94550(*:30634)' + .'|7ee2/([^/]++)/([^/]++)/([^/]++)/e97ee2(*:30682)' + .')' + .'|e(?' + .'|fc9e/([^/]++)/([^/]++)/([^/]++)/eefc9e(*:30735)' + .'|b69a/([^/]++)/([^/]++)/([^/]++)/eeb69a(*:30783)' + .')' + .'|0(?' + .'|7413/([^/]++)/([^/]++)/([^/]++)/e07413(*:30836)' + .'|cf1f/([^/]++)/([^/]++)/([^/]++)/e0cf1f(*:30884)' + .'|ec45/([^/]++)/([^/]++)/([^/]++)/e0ec45(*:30932)' + .')' + .'|f4e3b/([^/]++)/([^/]++)/([^/]++)/ef4e3b(*:30982)' + .'|c5aa0/([^/]++)/([^/]++)/([^/]++)/ec5aa0(*:31031)' + .')' + .'|/f(?' + .'|f(?' + .'|4d5f/([^/]++)/([^/]++)/([^/]++)/ff4d5f(*:31089)' + .'|eabd/([^/]++)/([^/]++)/([^/]++)/ffeabd(*:31137)' + .')' + .'|3(?' + .'|f27a/([^/]++)/([^/]++)/([^/]++)/f3f27a(*:31190)' + .'|8762/([^/]++)/([^/]++)/([^/]++)/f38762(*:31238)' + .')' + .'|4(?' + .'|be00/([^/]++)/([^/]++)/([^/]++)/f4be00(*:31291)' + .'|5526/([^/]++)/([^/]++)/([^/]++)/f45526(*:31339)' + .'|7d0a/([^/]++)/([^/]++)/([^/]++)/f47d0a(*:31387)' + .')' + .'|0(?' + .'|e52b/([^/]++)/([^/]++)/([^/]++)/f0e52b(*:31440)' + .'|adc8/([^/]++)/([^/]++)/([^/]++)/f0adc8(*:31488)' + .')' + .'|de926/([^/]++)/([^/]++)/([^/]++)/fde926(*:31538)' + .'|5(?' + .'|deae/([^/]++)/([^/]++)/([^/]++)/f5deae(*:31590)' + .'|7a2f/([^/]++)/([^/]++)/([^/]++)/f57a2f(*:31638)' + .')' + .'|7(?' + .'|6a89/([^/]++)/([^/]++)/([^/]++)/f76a89(*:31691)' + .'|9921/([^/]++)/([^/]++)/([^/]++)/f79921(*:31739)' + .'|e905/([^/]++)/([^/]++)/([^/]++)/f7e905(*:31787)' + .')' + .'|2(?' + .'|9c21/([^/]++)/([^/]++)/([^/]++)/f29c21(*:31840)' + .'|201f/([^/]++)/([^/]++)/([^/]++)/f2201f(*:31888)' + .')' + .'|a(?' + .'|e0b2/([^/]++)/([^/]++)/([^/]++)/fae0b2(*:31941)' + .'|14d4/([^/]++)/([^/]++)/([^/]++)/fa14d4(*:31989)' + .'|3a3c/([^/]++)/([^/]++)/([^/]++)/fa3a3c(*:32037)' + .'|83a1/([^/]++)/([^/]++)/([^/]++)/fa83a1(*:32085)' + .')' + .'|c(?' + .'|cb3c/([^/]++)/([^/]++)/([^/]++)/fccb3c(*:32138)' + .'|8001/([^/]++)/([^/]++)/([^/]++)/fc8001(*:32186)' + .'|3cf4/([^/]++)/([^/]++)/([^/]++)/fc3cf4(*:32234)' + .'|4930/([^/]++)/([^/]++)/([^/]++)/fc4930(*:32282)' + .')' + .'|64eac/([^/]++)/([^/]++)/([^/]++)/f64eac(*:32332)' + .'|b8970/([^/]++)/([^/]++)/([^/]++)/fb8970(*:32381)' + .'|1c159/([^/]++)/([^/]++)/([^/]++)/f1c159(*:32430)' + .'|9(?' + .'|028f/([^/]++)/([^/]++)/([^/]++)/f9028f(*:32482)' + .'|a40a/([^/]++)/([^/]++)/([^/]++)/f9a40a(*:32530)' + .')' + .'|e(?' + .'|8c15/([^/]++)/([^/]++)/([^/]++)/fe8c15(*:32583)' + .'|c8d4/([^/]++)/([^/]++)/([^/]++)/fec8d4(*:32631)' + .'|7ee8/([^/]++)/([^/]++)/([^/]++)/fe7ee8(*:32679)' + .')' + .')' + .'|/3(?' + .'|8(?' + .'|9(?' + .'|bc7/([^/]++)/([^/]++)/([^/]++)/389bc7(*:32741)' + .'|13e/([^/]++)/([^/]++)/([^/]++)/38913e(*:32788)' + .')' + .'|71bd/([^/]++)/([^/]++)/([^/]++)/3871bd(*:32837)' + .')' + .'|d(?' + .'|c487/([^/]++)/([^/]++)/([^/]++)/3dc487(*:32890)' + .'|2d8c/([^/]++)/([^/]++)/([^/]++)/3d2d8c(*:32938)' + .'|8e28/([^/]++)/([^/]++)/([^/]++)/3d8e28(*:32986)' + .'|f1d4/([^/]++)/([^/]++)/([^/]++)/3df1d4(*:33034)' + .')' + .'|7f0e8/([^/]++)/([^/]++)/([^/]++)/37f0e8(*:33084)' + .'|3(?' + .'|e807/([^/]++)/([^/]++)/([^/]++)/33e807(*:33136)' + .'|28bd/([^/]++)/([^/]++)/([^/]++)/3328bd(*:33184)' + .')' + .'|a(?' + .'|0(?' + .'|772/([^/]++)/([^/]++)/([^/]++)/3a0772(*:33240)' + .'|66b/([^/]++)/([^/]++)/([^/]++)/3a066b(*:33287)' + .')' + .'|835d/([^/]++)/([^/]++)/([^/]++)/3a835d(*:33336)' + .')' + .'|0(?' + .'|bb38/([^/]++)/([^/]++)/([^/]++)/30bb38(*:33389)' + .'|3ed4/([^/]++)/([^/]++)/([^/]++)/303ed4(*:33437)' + .'|ef30/([^/]++)/([^/]++)/([^/]++)/30ef30(*:33485)' + .'|1ad0/([^/]++)/([^/]++)/([^/]++)/301ad0(*:33533)' + .')' + .'|4(?' + .'|9389/([^/]++)/([^/]++)/([^/]++)/349389(*:33586)' + .'|35c3/([^/]++)/([^/]++)/([^/]++)/3435c3(*:33634)' + .')' + .'|62(?' + .'|1f1/([^/]++)/([^/]++)/([^/]++)/3621f1(*:33687)' + .'|e80/([^/]++)/([^/]++)/([^/]++)/362e80(*:33734)' + .')' + .'|5(?' + .'|cf86/([^/]++)/([^/]++)/([^/]++)/35cf86(*:33787)' + .'|2407/([^/]++)/([^/]++)/([^/]++)/352407(*:33835)' + .')' + .'|2b30a/([^/]++)/([^/]++)/([^/]++)/32b30a(*:33885)' + .'|1839b/([^/]++)/([^/]++)/([^/]++)/31839b(*:33934)' + .'|b(?' + .'|5dca/([^/]++)/([^/]++)/([^/]++)/3b5dca(*:33986)' + .'|3dba/([^/]++)/([^/]++)/([^/]++)/3b3dba(*:34034)' + .')' + .'|e89eb/([^/]++)/([^/]++)/([^/]++)/3e89eb(*:34084)' + .'|cef96/([^/]++)/([^/]++)/([^/]++)/3cef96(*:34133)' + .')' + .'|/0(?' + .'|8(?' + .'|7408/([^/]++)/([^/]++)/([^/]++)/087408(*:34191)' + .'|b255/([^/]++)/([^/]++)/([^/]++)/08b255(*:34239)' + .'|c543/([^/]++)/([^/]++)/([^/]++)/08c543(*:34287)' + .'|d986/([^/]++)/([^/]++)/([^/]++)/08d986(*:34335)' + .'|419b/([^/]++)/([^/]++)/([^/]++)/08419b(*:34383)' + .')' + .'|7(?' + .'|563a/([^/]++)/([^/]++)/([^/]++)/07563a(*:34436)' + .'|6a0c/([^/]++)/([^/]++)/([^/]++)/076a0c(*:34484)' + .'|a96b/([^/]++)/([^/]++)/([^/]++)/07a96b(*:34532)' + .'|c580/([^/]++)/([^/]++)/([^/]++)/07c580(*:34580)' + .'|8719/([^/]++)/([^/]++)/([^/]++)/078719(*:34628)' + .')' + .'|f(?' + .'|cbc6/([^/]++)/([^/]++)/([^/]++)/0fcbc6(*:34681)' + .'|9661/([^/]++)/([^/]++)/([^/]++)/0f9661(*:34729)' + .'|f(?' + .'|39b/([^/]++)/([^/]++)/([^/]++)/0ff39b(*:34780)' + .'|803/([^/]++)/([^/]++)/([^/]++)/0ff803(*:34827)' + .')' + .'|840b/([^/]++)/([^/]++)/([^/]++)/0f840b(*:34876)' + .')' + .'|1(?' + .'|f78b/([^/]++)/([^/]++)/([^/]++)/01f78b(*:34929)' + .'|3a00/([^/]++)/([^/]++)/([^/]++)/013a00(*:34977)' + .'|8825/([^/]++)/([^/]++)/([^/]++)/018825(*:35025)' + .')' + .'|6(?' + .'|9(?' + .'|d3b/([^/]++)/([^/]++)/([^/]++)/069d3b(*:35081)' + .'|97f/([^/]++)/([^/]++)/([^/]++)/06997f(*:35128)' + .')' + .'|1412/([^/]++)/([^/]++)/([^/]++)/061412(*:35177)' + .')' + .'|4(?' + .'|ecb1/([^/]++)/([^/]++)/([^/]++)/04ecb1(*:35230)' + .'|3c3d/([^/]++)/([^/]++)/([^/]++)/043c3d(*:35278)' + .')' + .'|0ac8e/([^/]++)/([^/]++)/([^/]++)/00ac8e(*:35328)' + .'|5(?' + .'|1e4e/([^/]++)/([^/]++)/([^/]++)/051e4e(*:35380)' + .'|37fb/([^/]++)/([^/]++)/([^/]++)/0537fb(*:35428)' + .')' + .'|d(?' + .'|7de1/([^/]++)/([^/]++)/([^/]++)/0d7de1(*:35481)' + .'|3180/([^/]++)/([^/]++)/([^/]++)/0d3180(*:35529)' + .'|0871/([^/]++)/([^/]++)/([^/]++)/0d0871(*:35577)' + .')' + .'|cb929/([^/]++)/([^/]++)/([^/]++)/0cb929(*:35627)' + .'|2(?' + .'|a32a/([^/]++)/([^/]++)/([^/]++)/02a32a(*:35679)' + .'|4d7f/([^/]++)/([^/]++)/([^/]++)/024d7f(*:35727)' + .')' + .'|efe32/([^/]++)/([^/]++)/([^/]++)/0efe32(*:35777)' + .'|a113e/([^/]++)/([^/]++)/([^/]++)/0a113e(*:35826)' + .'|b8aff/([^/]++)/([^/]++)/([^/]++)/0b8aff(*:35875)' + .')' + .'|/a(?' + .'|7(?' + .'|6088/([^/]++)/([^/]++)/([^/]++)/a76088(*:35933)' + .'|aeed/([^/]++)/([^/]++)/([^/]++)/a7aeed(*:35981)' + .'|33fa/([^/]++)/([^/]++)/([^/]++)/a733fa(*:36029)' + .')' + .'|9a(?' + .'|665/([^/]++)/([^/]++)/([^/]++)/a9a665(*:36082)' + .'|1d5/([^/]++)/([^/]++)/([^/]++)/a9a1d5(*:36129)' + .')' + .'|8(?' + .'|6c45/([^/]++)/([^/]++)/([^/]++)/a86c45(*:36182)' + .'|849b/([^/]++)/([^/]++)/([^/]++)/a8849b(*:36230)' + .'|e(?' + .'|864/([^/]++)/([^/]++)/([^/]++)/a8e864(*:36281)' + .'|cba/([^/]++)/([^/]++)/([^/]++)/a8ecba(*:36328)' + .')' + .')' + .'|c(?' + .'|c3e0/([^/]++)/([^/]++)/([^/]++)/acc3e0(*:36382)' + .'|f4b8/([^/]++)/([^/]++)/([^/]++)/acf4b8(*:36430)' + .')' + .'|b(?' + .'|d815/([^/]++)/([^/]++)/([^/]++)/abd815(*:36483)' + .'|233b/([^/]++)/([^/]++)/([^/]++)/ab233b(*:36531)' + .'|a3b6/([^/]++)/([^/]++)/([^/]++)/aba3b6(*:36579)' + .'|88b1/([^/]++)/([^/]++)/([^/]++)/ab88b1(*:36627)' + .')' + .'|5(?' + .'|3240/([^/]++)/([^/]++)/([^/]++)/a53240(*:36680)' + .'|cdd4/([^/]++)/([^/]++)/([^/]++)/a5cdd4(*:36728)' + .')' + .'|f(?' + .'|d(?' + .'|483/([^/]++)/([^/]++)/([^/]++)/afd483(*:36784)' + .'|a33/([^/]++)/([^/]++)/([^/]++)/afda33(*:36831)' + .')' + .'|f162/([^/]++)/([^/]++)/([^/]++)/aff162(*:36880)' + .')' + .'|e(?' + .'|0eb3/([^/]++)/([^/]++)/([^/]++)/ae0eb3(*:36933)' + .'|b313/([^/]++)/([^/]++)/([^/]++)/aeb313(*:36981)' + .')' + .'|1(?' + .'|d33d/([^/]++)/([^/]++)/([^/]++)/a1d33d(*:37034)' + .'|140a/([^/]++)/([^/]++)/([^/]++)/a1140a(*:37082)' + .')' + .'|ddfa9/([^/]++)/([^/]++)/([^/]++)/addfa9(*:37132)' + .'|6(?' + .'|7f09/([^/]++)/([^/]++)/([^/]++)/a67f09(*:37184)' + .'|4c94/([^/]++)/([^/]++)/([^/]++)/a64c94(*:37232)' + .')' + .'|a169b/([^/]++)/([^/]++)/([^/]++)/aa169b(*:37282)' + .'|4300b/([^/]++)/([^/]++)/([^/]++)/a4300b(*:37331)' + .'|3d68b/([^/]++)/([^/]++)/([^/]++)/a3d68b(*:37380)' + .')' + .'|/1(?' + .'|0(?' + .'|a(?' + .'|7cd/([^/]++)/([^/]++)/([^/]++)/10a7cd(*:37441)' + .'|5ab/([^/]++)/([^/]++)/([^/]++)/10a5ab(*:37488)' + .')' + .'|9a0c/([^/]++)/([^/]++)/([^/]++)/109a0c(*:37537)' + .')' + .'|3f320/([^/]++)/([^/]++)/([^/]++)/13f320(*:37587)' + .'|6(?' + .'|c222/([^/]++)/([^/]++)/([^/]++)/16c222(*:37639)' + .'|8908/([^/]++)/([^/]++)/([^/]++)/168908(*:37687)' + .')' + .'|5(?' + .'|de21/([^/]++)/([^/]++)/([^/]++)/15de21(*:37740)' + .'|95af/([^/]++)/([^/]++)/([^/]++)/1595af(*:37788)' + .')' + .'|1(?' + .'|b921/([^/]++)/([^/]++)/([^/]++)/11b921(*:37841)' + .'|4193/([^/]++)/([^/]++)/([^/]++)/114193(*:37889)' + .')' + .'|bb91f/([^/]++)/([^/]++)/([^/]++)/1bb91f(*:37939)' + .'|7(?' + .'|28ef/([^/]++)/([^/]++)/([^/]++)/1728ef(*:37991)' + .'|c276/([^/]++)/([^/]++)/([^/]++)/17c276(*:38039)' + .'|0c94/([^/]++)/([^/]++)/([^/]++)/170c94(*:38087)' + .')' + .'|85(?' + .'|c29/([^/]++)/([^/]++)/([^/]++)/185c29(*:38140)' + .'|e65/([^/]++)/([^/]++)/([^/]++)/185e65(*:38187)' + .')' + .'|9(?' + .'|2fc0/([^/]++)/([^/]++)/([^/]++)/192fc0(*:38240)' + .'|b(?' + .'|c91/([^/]++)/([^/]++)/([^/]++)/19bc91(*:38291)' + .'|650/([^/]++)/([^/]++)/([^/]++)/19b650(*:38338)' + .')' + .'|05ae/([^/]++)/([^/]++)/([^/]++)/1905ae(*:38387)' + .')' + .'|e(?' + .'|cfb4/([^/]++)/([^/]++)/([^/]++)/1ecfb4(*:38440)' + .'|fa39/([^/]++)/([^/]++)/([^/]++)/1efa39(*:38488)' + .'|056d/([^/]++)/([^/]++)/([^/]++)/1e056d(*:38536)' + .')' + .'|aa48f/([^/]++)/([^/]++)/([^/]++)/1aa48f(*:38586)' + .'|f(?' + .'|c214/([^/]++)/([^/]++)/([^/]++)/1fc214(*:38638)' + .'|5089/([^/]++)/([^/]++)/([^/]++)/1f5089(*:38686)' + .'|4477/([^/]++)/([^/]++)/([^/]++)/1f4477(*:38734)' + .')' + .'|c(?' + .'|c363/([^/]++)/([^/]++)/([^/]++)/1cc363(*:38787)' + .'|1d4d/([^/]++)/([^/]++)/([^/]++)/1c1d4d(*:38835)' + .'|e927/([^/]++)/([^/]++)/([^/]++)/1ce927(*:38883)' + .')' + .')' + .'|/6(?' + .'|3(?' + .'|538f/([^/]++)/([^/]++)/([^/]++)/63538f(*:38942)' + .'|2cee/([^/]++)/([^/]++)/([^/]++)/632cee(*:38990)' + .'|95eb/([^/]++)/([^/]++)/([^/]++)/6395eb(*:39038)' + .')' + .'|9(?' + .'|421f/([^/]++)/([^/]++)/([^/]++)/69421f(*:39091)' + .'|2f93/([^/]++)/([^/]++)/([^/]++)/692f93(*:39139)' + .')' + .'|5658f/([^/]++)/([^/]++)/([^/]++)/65658f(*:39189)' + .'|4(?' + .'|7bba/([^/]++)/([^/]++)/([^/]++)/647bba(*:39241)' + .'|223c/([^/]++)/([^/]++)/([^/]++)/64223c(*:39289)' + .')' + .'|e(?' + .'|2713/([^/]++)/([^/]++)/([^/]++)/6e2713(*:39342)' + .'|0721/([^/]++)/([^/]++)/([^/]++)/6e0721(*:39390)' + .'|7b33/([^/]++)/([^/]++)/([^/]++)/6e7b33(*:39438)' + .')' + .'|0(?' + .'|5ff7/([^/]++)/([^/]++)/([^/]++)/605ff7(*:39491)' + .'|8159/([^/]++)/([^/]++)/([^/]++)/608159(*:39539)' + .')' + .'|a(?' + .'|ca97/([^/]++)/([^/]++)/([^/]++)/6aca97(*:39592)' + .'|10bb/([^/]++)/([^/]++)/([^/]++)/6a10bb(*:39640)' + .'|ab12/([^/]++)/([^/]++)/([^/]++)/6aab12(*:39688)' + .')' + .'|7(?' + .'|66aa/([^/]++)/([^/]++)/([^/]++)/6766aa(*:39741)' + .'|e103/([^/]++)/([^/]++)/([^/]++)/67e103(*:39789)' + .'|d(?' + .'|96d/([^/]++)/([^/]++)/([^/]++)/67d96d(*:39840)' + .'|16d/([^/]++)/([^/]++)/([^/]++)/67d16d(*:39887)' + .')' + .'|0e8a/([^/]++)/([^/]++)/([^/]++)/670e8a(*:39936)' + .'|7e09/([^/]++)/([^/]++)/([^/]++)/677e09(*:39984)' + .')' + .'|8(?' + .'|264b/([^/]++)/([^/]++)/([^/]++)/68264b(*:40037)' + .'|053a/([^/]++)/([^/]++)/([^/]++)/68053a(*:40085)' + .')' + .'|c(?' + .'|2979/([^/]++)/([^/]++)/([^/]++)/6c2979(*:40138)' + .'|d67d/([^/]++)/([^/]++)/([^/]++)/6cd67d(*:40186)' + .'|3cf7/([^/]++)/([^/]++)/([^/]++)/6c3cf7(*:40234)' + .'|fe0e/([^/]++)/([^/]++)/([^/]++)/6cfe0e(*:40282)' + .')' + .'|bc24f/([^/]++)/([^/]++)/([^/]++)/6bc24f(*:40332)' + .'|f2268/([^/]++)/([^/]++)/([^/]++)/6f2268(*:40381)' + .'|1b4a6/([^/]++)/([^/]++)/([^/]++)/61b4a6(*:40430)' + .'|21461/([^/]++)/([^/]++)/([^/]++)/621461(*:40479)' + .'|d0f84/([^/]++)/([^/]++)/([^/]++)/6d0f84(*:40528)' + .'|60229/([^/]++)/([^/]++)/([^/]++)/660229(*:40577)' + .')' + .'|/c(?' + .'|f(?' + .'|6735/([^/]++)/([^/]++)/([^/]++)/cf6735(*:40635)' + .'|bce4/([^/]++)/([^/]++)/([^/]++)/cfbce4(*:40683)' + .')' + .'|3(?' + .'|99(?' + .'|86/([^/]++)/([^/]++)/([^/]++)/c39986(*:40739)' + .'|2e/([^/]++)/([^/]++)/([^/]++)/c3992e(*:40785)' + .')' + .'|61bc/([^/]++)/([^/]++)/([^/]++)/c361bc(*:40834)' + .'|2d9b/([^/]++)/([^/]++)/([^/]++)/c32d9b(*:40882)' + .')' + .'|75b6f/([^/]++)/([^/]++)/([^/]++)/c75b6f(*:40932)' + .'|c(?' + .'|b(?' + .'|1d4/([^/]++)/([^/]++)/([^/]++)/ccb1d4(*:40987)' + .'|098/([^/]++)/([^/]++)/([^/]++)/ccb098(*:41034)' + .')' + .'|c0aa/([^/]++)/([^/]++)/([^/]++)/ccc0aa(*:41083)' + .'|1aa4/([^/]++)/([^/]++)/([^/]++)/cc1aa4(*:41131)' + .')' + .'|b(?' + .'|cb58/([^/]++)/([^/]++)/([^/]++)/cbcb58(*:41184)' + .'|b6a3/([^/]++)/([^/]++)/([^/]++)/cbb6a3(*:41232)' + .')' + .'|9892a/([^/]++)/([^/]++)/([^/]++)/c9892a(*:41282)' + .'|6e19e/([^/]++)/([^/]++)/([^/]++)/c6e19e(*:41331)' + .'|dc0d6/([^/]++)/([^/]++)/([^/]++)/cdc0d6(*:41380)' + .'|5ab0b/([^/]++)/([^/]++)/([^/]++)/c5ab0b(*:41429)' + .'|a(?' + .'|9c26/([^/]++)/([^/]++)/([^/]++)/ca9c26(*:41481)' + .'|8155/([^/]++)/([^/]++)/([^/]++)/ca8155(*:41529)' + .'|7591/([^/]++)/([^/]++)/([^/]++)/ca7591(*:41577)' + .')' + .'|0(?' + .'|6d06/([^/]++)/([^/]++)/([^/]++)/c06d06(*:41630)' + .'|f168/([^/]++)/([^/]++)/([^/]++)/c0f168(*:41678)' + .')' + .'|8(?' + .'|ed21/([^/]++)/([^/]++)/([^/]++)/c8ed21(*:41731)' + .'|fbbc/([^/]++)/([^/]++)/([^/]++)/c8fbbc(*:41779)' + .'|c41c/([^/]++)/([^/]++)/([^/]++)/c8c41c(*:41827)' + .')' + .'|15da1/([^/]++)/([^/]++)/([^/]++)/c15da1(*:41877)' + .'|2(?' + .'|626d/([^/]++)/([^/]++)/([^/]++)/c2626d(*:41929)' + .'|aee8/([^/]++)/([^/]++)/([^/]++)/c2aee8(*:41977)' + .'|2abf/([^/]++)/([^/]++)/([^/]++)/c22abf(*:42025)' + .')' + .'|e78d1/([^/]++)/([^/]++)/([^/]++)/ce78d1(*:42075)' + .'|4(?' + .'|015b/([^/]++)/([^/]++)/([^/]++)/c4015b(*:42127)' + .'|b31c/([^/]++)/([^/]++)/([^/]++)/c4b31c(*:42175)' + .')' + .')' + .'|/8(?' + .'|5(?' + .'|422a/([^/]++)/([^/]++)/([^/]++)/85422a(*:42234)' + .'|1ddf/([^/]++)/([^/]++)/([^/]++)/851ddf(*:42282)' + .'|fc37/([^/]++)/([^/]++)/([^/]++)/85fc37(*:42330)' + .')' + .'|1(?' + .'|4481/([^/]++)/([^/]++)/([^/]++)/814481(*:42383)' + .'|e74d/([^/]++)/([^/]++)/([^/]++)/81e74d(*:42431)' + .')' + .'|d(?' + .'|3(?' + .'|420/([^/]++)/([^/]++)/([^/]++)/8d3420(*:42487)' + .'|17b/([^/]++)/([^/]++)/([^/]++)/8d317b(*:42534)' + .')' + .'|f707/([^/]++)/([^/]++)/([^/]++)/8df707(*:42583)' + .'|6dc3/([^/]++)/([^/]++)/([^/]++)/8d6dc3(*:42631)' + .')' + .'|e(?' + .'|efcf/([^/]++)/([^/]++)/([^/]++)/8eefcf(*:42684)' + .'|bda5/([^/]++)/([^/]++)/([^/]++)/8ebda5(*:42732)' + .'|82ab/([^/]++)/([^/]++)/([^/]++)/8e82ab(*:42780)' + .')' + .'|b(?' + .'|16eb/([^/]++)/([^/]++)/([^/]++)/8b16eb(*:42833)' + .'|6dd7/([^/]++)/([^/]++)/([^/]++)/8b6dd7(*:42881)' + .'|5040/([^/]++)/([^/]++)/([^/]++)/8b5040(*:42929)' + .')' + .'|c(?' + .'|7bbb/([^/]++)/([^/]++)/([^/]++)/8c7bbb(*:42982)' + .'|6744/([^/]++)/([^/]++)/([^/]++)/8c6744(*:43030)' + .'|235f/([^/]++)/([^/]++)/([^/]++)/8c235f(*:43078)' + .')' + .'|8(?' + .'|4d24/([^/]++)/([^/]++)/([^/]++)/884d24(*:43131)' + .'|ae63/([^/]++)/([^/]++)/([^/]++)/88ae63(*:43179)' + .')' + .'|7(?' + .'|5715/([^/]++)/([^/]++)/([^/]++)/875715(*:43232)' + .'|2488/([^/]++)/([^/]++)/([^/]++)/872488(*:43280)' + .')' + .'|4(?' + .'|1172/([^/]++)/([^/]++)/([^/]++)/841172(*:43333)' + .'|6c26/([^/]++)/([^/]++)/([^/]++)/846c26(*:43381)' + .'|f7e6/([^/]++)/([^/]++)/([^/]++)/84f7e6(*:43429)' + .'|7cc5/([^/]++)/([^/]++)/([^/]++)/847cc5(*:43477)' + .')' + .'|f(?' + .'|ecb2/([^/]++)/([^/]++)/([^/]++)/8fecb2(*:43530)' + .'|7d80/([^/]++)/([^/]++)/([^/]++)/8f7d80(*:43578)' + .'|468c/([^/]++)/([^/]++)/([^/]++)/8f468c(*:43626)' + .')' + .'|a0e11/([^/]++)/([^/]++)/([^/]++)/8a0e11(*:43676)' + .'|2(?' + .'|f2b3/([^/]++)/([^/]++)/([^/]++)/82f2b3(*:43728)' + .'|489c/([^/]++)/([^/]++)/([^/]++)/82489c(*:43776)' + .')' + .'|6(?' + .'|b122/([^/]++)/([^/]++)/([^/]++)/86b122(*:43829)' + .'|0320/([^/]++)/([^/]++)/([^/]++)/860320(*:43877)' + .')' + .'|9(?' + .'|2c91/([^/]++)/([^/]++)/([^/]++)/892c91(*:43930)' + .'|fcd0/([^/]++)/([^/]++)/([^/]++)/89fcd0(*:43978)' + .')' + .'|065d0/([^/]++)/([^/]++)/([^/]++)/8065d0(*:44028)' + .')' + .'|/d(?' + .'|6(?' + .'|4a34/([^/]++)/([^/]++)/([^/]++)/d64a34(*:44086)' + .'|c651/([^/]++)/([^/]++)/([^/]++)/d6c651(*:44134)' + .')' + .'|f(?' + .'|877f/([^/]++)/([^/]++)/([^/]++)/df877f(*:44187)' + .'|263d/([^/]++)/([^/]++)/([^/]++)/df263d(*:44235)' + .'|7f28/([^/]++)/([^/]++)/([^/]++)/df7f28(*:44283)' + .'|6d23/([^/]++)/([^/]++)/([^/]++)/df6d23(*:44331)' + .')' + .'|b(?' + .'|85e2/([^/]++)/([^/]++)/([^/]++)/db85e2(*:44384)' + .'|e272/([^/]++)/([^/]++)/([^/]++)/dbe272(*:44432)' + .')' + .'|d(?' + .'|45(?' + .'|85/([^/]++)/([^/]++)/([^/]++)/dd4585(*:44488)' + .'|04/([^/]++)/([^/]++)/([^/]++)/dd4504(*:44534)' + .')' + .'|8eb9/([^/]++)/([^/]++)/([^/]++)/dd8eb9(*:44583)' + .')' + .'|a(?' + .'|ca41/([^/]++)/([^/]++)/([^/]++)/daca41(*:44636)' + .'|8ce5/([^/]++)/([^/]++)/([^/]++)/da8ce5(*:44684)' + .'|0d11/([^/]++)/([^/]++)/([^/]++)/da0d11(*:44732)' + .')' + .'|4(?' + .'|90d7/([^/]++)/([^/]++)/([^/]++)/d490d7(*:44785)' + .'|c2e4/([^/]++)/([^/]++)/([^/]++)/d4c2e4(*:44833)' + .')' + .'|8(?' + .'|6ea6/([^/]++)/([^/]++)/([^/]++)/d86ea6(*:44886)' + .'|40cc/([^/]++)/([^/]++)/([^/]++)/d840cc(*:44934)' + .')' + .'|c(?' + .'|82d6/([^/]++)/([^/]++)/([^/]++)/dc82d6(*:44987)' + .'|6a70/([^/]++)/([^/]++)/([^/]++)/dc6a70(*:45035)' + .'|5689/([^/]++)/([^/]++)/([^/]++)/dc5689(*:45083)' + .')' + .'|7(?' + .'|a728/([^/]++)/([^/]++)/([^/]++)/d7a728(*:45136)' + .'|0732/([^/]++)/([^/]++)/([^/]++)/d70732(*:45184)' + .'|9aac/([^/]++)/([^/]++)/([^/]++)/d79aac(*:45232)' + .')' + .'|14220/([^/]++)/([^/]++)/([^/]++)/d14220(*:45282)' + .'|5(?' + .'|cfea/([^/]++)/([^/]++)/([^/]++)/d5cfea(*:45334)' + .'|8072/([^/]++)/([^/]++)/([^/]++)/d58072(*:45382)' + .'|54f7/([^/]++)/([^/]++)/([^/]++)/d554f7(*:45430)' + .'|16b1/([^/]++)/([^/]++)/([^/]++)/d516b1(*:45478)' + .'|6b9f/([^/]++)/([^/]++)/([^/]++)/d56b9f(*:45526)' + .')' + .'|045c5/([^/]++)/([^/]++)/([^/]++)/d045c5(*:45576)' + .'|2(?' + .'|ed45/([^/]++)/([^/]++)/([^/]++)/d2ed45(*:45628)' + .'|40e3/([^/]++)/([^/]++)/([^/]++)/d240e3(*:45676)' + .')' + .'|93ed5/([^/]++)/([^/]++)/([^/]++)/d93ed5(*:45726)' + .')' + .'|/7(?' + .'|b(?' + .'|cdf7/([^/]++)/([^/]++)/([^/]++)/7bcdf7(*:45784)' + .'|13b2/([^/]++)/([^/]++)/([^/]++)/7b13b2(*:45832)' + .')' + .'|dcd34/([^/]++)/([^/]++)/([^/]++)/7dcd34(*:45882)' + .'|f(?' + .'|24d2/([^/]++)/([^/]++)/([^/]++)/7f24d2(*:45934)' + .'|5d04/([^/]++)/([^/]++)/([^/]++)/7f5d04(*:45982)' + .'|1171/([^/]++)/([^/]++)/([^/]++)/7f1171(*:46030)' + .'|a732/([^/]++)/([^/]++)/([^/]++)/7fa732(*:46078)' + .')' + .'|6(?' + .'|6ebc/([^/]++)/([^/]++)/([^/]++)/766ebc(*:46131)' + .'|34ea/([^/]++)/([^/]++)/([^/]++)/7634ea(*:46179)' + .')' + .'|750ca/([^/]++)/([^/]++)/([^/]++)/7750ca(*:46229)' + .'|1(?' + .'|a(?' + .'|3cb/([^/]++)/([^/]++)/([^/]++)/71a3cb(*:46284)' + .'|d16/([^/]++)/([^/]++)/([^/]++)/71ad16(*:46331)' + .')' + .'|43d7/([^/]++)/([^/]++)/([^/]++)/7143d7(*:46380)' + .')' + .'|88d98/([^/]++)/([^/]++)/([^/]++)/788d98(*:46430)' + .'|2(?' + .'|da7f/([^/]++)/([^/]++)/([^/]++)/72da7f(*:46482)' + .'|50eb/([^/]++)/([^/]++)/([^/]++)/7250eb(*:46530)' + .')' + .'|c(?' + .'|590f/([^/]++)/([^/]++)/([^/]++)/7c590f(*:46583)' + .'|e328/([^/]++)/([^/]++)/([^/]++)/7ce328(*:46631)' + .')' + .'|a5392/([^/]++)/([^/]++)/([^/]++)/7a5392(*:46681)' + .'|95c7a/([^/]++)/([^/]++)/([^/]++)/795c7a(*:46730)' + .'|504ad/([^/]++)/([^/]++)/([^/]++)/7504ad(*:46779)' + .'|04afe/([^/]++)/([^/]++)/([^/]++)/704afe(*:46828)' + .'|4bba2/([^/]++)/([^/]++)/([^/]++)/74bba2(*:46877)' + .')' + .'|/9(?' + .'|b(?' + .'|72e3/([^/]++)/([^/]++)/([^/]++)/9b72e3(*:46935)' + .'|698e/([^/]++)/([^/]++)/([^/]++)/9b698e(*:46983)' + .')' + .'|7e852/([^/]++)/([^/]++)/([^/]++)/97e852(*:47033)' + .'|4c7bb/([^/]++)/([^/]++)/([^/]++)/94c7bb(*:47082)' + .'|9(?' + .'|c5e0/([^/]++)/([^/]++)/([^/]++)/99c5e0(*:47134)' + .'|6a7f/([^/]++)/([^/]++)/([^/]++)/996a7f(*:47182)' + .'|bcfc/([^/]++)/([^/]++)/([^/]++)/99bcfc(*:47230)' + .'|0827/([^/]++)/([^/]++)/([^/]++)/990827(*:47278)' + .')' + .'|a(?' + .'|d6aa/([^/]++)/([^/]++)/([^/]++)/9ad6aa(*:47331)' + .'|b0d8/([^/]++)/([^/]++)/([^/]++)/9ab0d8(*:47379)' + .')' + .'|c(?' + .'|f81d/([^/]++)/([^/]++)/([^/]++)/9cf81d(*:47432)' + .'|c138/([^/]++)/([^/]++)/([^/]++)/9cc138(*:47480)' + .'|82c7/([^/]++)/([^/]++)/([^/]++)/9c82c7(*:47528)' + .'|0180/([^/]++)/([^/]++)/([^/]++)/9c0180(*:47576)' + .')' + .'|f(?' + .'|396f/([^/]++)/([^/]++)/([^/]++)/9f396f(*:47629)' + .'|e859/([^/]++)/([^/]++)/([^/]++)/9fe859(*:47677)' + .'|53d8/([^/]++)/([^/]++)/([^/]++)/9f53d8(*:47725)' + .')' + .'|12d2b/([^/]++)/([^/]++)/([^/]++)/912d2b(*:47775)' + .'|59a55/([^/]++)/([^/]++)/([^/]++)/959a55(*:47824)' + .'|6(?' + .'|ea64/([^/]++)/([^/]++)/([^/]++)/96ea64(*:47876)' + .'|b9bf/([^/]++)/([^/]++)/([^/]++)/96b9bf(*:47924)' + .')' + .'|e3cfc/([^/]++)/([^/]++)/([^/]++)/9e3cfc(*:47974)' + .'|2(?' + .'|fb0c/([^/]++)/([^/]++)/([^/]++)/92fb0c(*:48026)' + .'|262b/([^/]++)/([^/]++)/([^/]++)/92262b(*:48074)' + .'|32fe/([^/]++)/([^/]++)/([^/]++)/9232fe(*:48122)' + .'|977a/([^/]++)/([^/]++)/([^/]++)/92977a(*:48170)' + .')' + .'|8d6f5/([^/]++)/([^/]++)/([^/]++)/98d6f5(*:48220)' + .'|0794e/([^/]++)/([^/]++)/([^/]++)/90794e(*:48269)' + .'|34815/([^/]++)/([^/]++)/([^/]++)/934815(*:48318)' + .')' + .'|/4(?' + .'|e(?' + .'|4b5f/([^/]++)/([^/]++)/([^/]++)/4e4b5f(*:48376)' + .'|a06f/([^/]++)/([^/]++)/([^/]++)/4ea06f(*:48424)' + .'|0(?' + .'|928/([^/]++)/([^/]++)/([^/]++)/4e0928(*:48475)' + .'|cb6/([^/]++)/([^/]++)/([^/]++)/4e0cb6(*:48522)' + .')' + .')' + .'|6922a/([^/]++)/([^/]++)/([^/]++)/46922a(*:48573)' + .'|4(?' + .'|c4c1/([^/]++)/([^/]++)/([^/]++)/44c4c1(*:48625)' + .'|3cb0/([^/]++)/([^/]++)/([^/]++)/443cb0(*:48673)' + .')' + .'|8ab2f/([^/]++)/([^/]++)/([^/]++)/48ab2f(*:48723)' + .'|5(?' + .'|645a/([^/]++)/([^/]++)/([^/]++)/45645a(*:48775)' + .'|58db/([^/]++)/([^/]++)/([^/]++)/4558db(*:48823)' + .')' + .'|2e77b/([^/]++)/([^/]++)/([^/]++)/42e77b(*:48873)' + .'|c27ce/([^/]++)/([^/]++)/([^/]++)/4c27ce(*:48922)' + .'|f(?' + .'|fce0/([^/]++)/([^/]++)/([^/]++)/4ffce0(*:48974)' + .'|ac9b/([^/]++)/([^/]++)/([^/]++)/4fac9b(*:49022)' + .')' + .'|a47d2/([^/]++)/([^/]++)/([^/]++)/4a47d2(*:49072)' + .'|70e7a/([^/]++)/([^/]++)/([^/]++)/470e7a(*:49121)' + .'|b(?' + .'|0(?' + .'|4a6/([^/]++)/([^/]++)/([^/]++)/4b04a6(*:49176)' + .'|a59/([^/]++)/([^/]++)/([^/]++)/4b0a59(*:49223)' + .'|250/([^/]++)/([^/]++)/([^/]++)/4b0250(*:49270)' + .')' + .'|6538/([^/]++)/([^/]++)/([^/]++)/4b6538(*:49319)' + .')' + .'|3(?' + .'|f(?' + .'|a7f/([^/]++)/([^/]++)/([^/]++)/43fa7f(*:49375)' + .'|eae/([^/]++)/([^/]++)/([^/]++)/43feae(*:49422)' + .')' + .'|0c36/([^/]++)/([^/]++)/([^/]++)/430c36(*:49471)' + .'|7d7d/([^/]++)/([^/]++)/([^/]++)/437d7d(*:49519)' + .'|1135/([^/]++)/([^/]++)/([^/]++)/431135(*:49567)' + .')' + .'|d(?' + .'|5b99/([^/]++)/([^/]++)/([^/]++)/4d5b99(*:49620)' + .'|aa3d/([^/]++)/([^/]++)/([^/]++)/4daa3d(*:49668)' + .')' + .'|9c9ad/([^/]++)/([^/]++)/([^/]++)/49c9ad(*:49718)' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 54 => array(array('_route' => '_0'), array('a', 'b', 'c'), null, null), + 102 => array(array('_route' => '_190'), array('a', 'b', 'c'), null, null), + 147 => array(array('_route' => '_478'), array('a', 'b', 'c'), null, null), + 194 => array(array('_route' => '_259'), array('a', 'b', 'c'), null, null), + 240 => array(array('_route' => '_368'), array('a', 'b', 'c'), null, null), + 291 => array(array('_route' => '_1'), array('a', 'b', 'c'), null, null), + 337 => array(array('_route' => '_116'), array('a', 'b', 'c'), null, null), + 383 => array(array('_route' => '_490'), array('a', 'b', 'c'), null, null), + 434 => array(array('_route' => '_2'), array('a', 'b', 'c'), null, null), + 480 => array(array('_route' => '_124'), array('a', 'b', 'c'), null, null), + 526 => array(array('_route' => '_389'), array('a', 'b', 'c'), null, null), + 577 => array(array('_route' => '_8'), array('a', 'b', 'c'), null, null), + 623 => array(array('_route' => '_104'), array('a', 'b', 'c'), null, null), + 677 => array(array('_route' => '_12'), array('a', 'b', 'c'), null, null), + 722 => array(array('_route' => '_442'), array('a', 'b', 'c'), null, null), + 769 => array(array('_route' => '_253'), array('a', 'b', 'c'), null, null), + 820 => array(array('_route' => '_13'), array('a', 'b', 'c'), null, null), + 866 => array(array('_route' => '_254'), array('a', 'b', 'c'), null, null), + 912 => array(array('_route' => '_347'), array('a', 'b', 'c'), null, null), + 963 => array(array('_route' => '_16'), array('a', 'b', 'c'), null, null), + 1009 => array(array('_route' => '_87'), array('a', 'b', 'c'), null, null), + 1058 => array(array('_route' => '_31'), array('a', 'b', 'c'), null, null), + 1109 => array(array('_route' => '_50'), array('a', 'b', 'c'), null, null), + 1156 => array(array('_route' => '_219'), array('a', 'b', 'c'), null, null), + 1203 => array(array('_route' => '_332'), array('a', 'b', 'c'), null, null), + 1250 => array(array('_route' => '_359'), array('a', 'b', 'c'), null, null), + 1302 => array(array('_route' => '_183'), array('a', 'b', 'c'), null, null), + 1349 => array(array('_route' => '_500'), array('a', 'b', 'c'), null, null), + 1401 => array(array('_route' => '_214'), array('a', 'b', 'c'), null, null), + 1448 => array(array('_route' => '_321'), array('a', 'b', 'c'), null, null), + 1497 => array(array('_route' => '_243'), array('a', 'b', 'c'), null, null), + 1545 => array(array('_route' => '_328'), array('a', 'b', 'c'), null, null), + 1596 => array(array('_route' => '_362'), array('a', 'b', 'c'), null, null), + 1643 => array(array('_route' => '_488'), array('a', 'b', 'c'), null, null), + 1701 => array(array('_route' => '_3'), array('a', 'b', 'c'), null, null), + 1751 => array(array('_route' => '_102'), array('a', 'b', 'c'), null, null), + 1797 => array(array('_route' => '_220'), array('a', 'b', 'c'), null, null), + 1845 => array(array('_route' => '_127'), array('a', 'b', 'c'), null, null), + 1897 => array(array('_route' => '_5'), array('a', 'b', 'c'), null, null), + 1944 => array(array('_route' => '_242'), array('a', 'b', 'c'), null, null), + 1991 => array(array('_route' => '_397'), array('a', 'b', 'c'), null, null), + 2038 => array(array('_route' => '_454'), array('a', 'b', 'c'), null, null), + 2090 => array(array('_route' => '_34'), array('a', 'b', 'c'), null, null), + 2137 => array(array('_route' => '_281'), array('a', 'b', 'c'), null, null), + 2189 => array(array('_route' => '_64'), array('a', 'b', 'c'), null, null), + 2236 => array(array('_route' => '_205'), array('a', 'b', 'c'), null, null), + 2291 => array(array('_route' => '_71'), array('a', 'b', 'c'), null, null), + 2337 => array(array('_route' => '_203'), array('a', 'b', 'c'), null, null), + 2385 => array(array('_route' => '_97'), array('a', 'b', 'c'), null, null), + 2437 => array(array('_route' => '_98'), array('a', 'b', 'c'), null, null), + 2484 => array(array('_route' => '_267'), array('a', 'b', 'c'), null, null), + 2531 => array(array('_route' => '_309'), array('a', 'b', 'c'), null, null), + 2586 => array(array('_route' => '_117'), array('a', 'b', 'c'), null, null), + 2631 => array(array('_route' => '_211'), array('a', 'b', 'c'), null, null), + 2679 => array(array('_route' => '_484'), array('a', 'b', 'c'), null, null), + 2731 => array(array('_route' => '_139'), array('a', 'b', 'c'), null, null), + 2778 => array(array('_route' => '_421'), array('a', 'b', 'c'), null, null), + 2830 => array(array('_route' => '_185'), array('a', 'b', 'c'), null, null), + 2877 => array(array('_route' => '_439'), array('a', 'b', 'c'), null, null), + 2926 => array(array('_route' => '_218'), array('a', 'b', 'c'), null, null), + 2977 => array(array('_route' => '_233'), array('a', 'b', 'c'), null, null), + 3024 => array(array('_route' => '_483'), array('a', 'b', 'c'), null, null), + 3073 => array(array('_route' => '_265'), array('a', 'b', 'c'), null, null), + 3124 => array(array('_route' => '_299'), array('a', 'b', 'c'), null, null), + 3171 => array(array('_route' => '_351'), array('a', 'b', 'c'), null, null), + 3218 => array(array('_route' => '_472'), array('a', 'b', 'c'), null, null), + 3267 => array(array('_route' => '_360'), array('a', 'b', 'c'), null, null), + 3315 => array(array('_route' => '_466'), array('a', 'b', 'c'), null, null), + 3372 => array(array('_route' => '_4'), array('a', 'b', 'c'), null, null), + 3419 => array(array('_route' => '_142'), array('a', 'b', 'c'), null, null), + 3466 => array(array('_route' => '_151'), array('a', 'b', 'c'), null, null), + 3513 => array(array('_route' => '_308'), array('a', 'b', 'c'), null, null), + 3560 => array(array('_route' => '_440'), array('a', 'b', 'c'), null, null), + 3612 => array(array('_route' => '_14'), array('a', 'b', 'c'), null, null), + 3659 => array(array('_route' => '_358'), array('a', 'b', 'c'), null, null), + 3711 => array(array('_route' => '_37'), array('a', 'b', 'c'), null, null), + 3758 => array(array('_route' => '_38'), array('a', 'b', 'c'), null, null), + 3805 => array(array('_route' => '_146'), array('a', 'b', 'c'), null, null), + 3852 => array(array('_route' => '_194'), array('a', 'b', 'c'), null, null), + 3899 => array(array('_route' => '_487'), array('a', 'b', 'c'), null, null), + 3948 => array(array('_route' => '_42'), array('a', 'b', 'c'), null, null), + 3999 => array(array('_route' => '_54'), array('a', 'b', 'c'), null, null), + 4046 => array(array('_route' => '_326'), array('a', 'b', 'c'), null, null), + 4098 => array(array('_route' => '_68'), array('a', 'b', 'c'), null, null), + 4145 => array(array('_route' => '_108'), array('a', 'b', 'c'), null, null), + 4197 => array(array('_route' => '_74'), array('a', 'b', 'c'), null, null), + 4244 => array(array('_route' => '_315'), array('a', 'b', 'c'), null, null), + 4291 => array(array('_route' => '_374'), array('a', 'b', 'c'), null, null), + 4343 => array(array('_route' => '_99'), array('a', 'b', 'c'), null, null), + 4390 => array(array('_route' => '_238'), array('a', 'b', 'c'), null, null), + 4442 => array(array('_route' => '_107'), array('a', 'b', 'c'), null, null), + 4489 => array(array('_route' => '_409'), array('a', 'b', 'c'), null, null), + 4541 => array(array('_route' => '_122'), array('a', 'b', 'c'), null, null), + 4588 => array(array('_route' => '_379'), array('a', 'b', 'c'), null, null), + 4635 => array(array('_route' => '_390'), array('a', 'b', 'c'), null, null), + 4687 => array(array('_route' => '_171'), array('a', 'b', 'c'), null, null), + 4734 => array(array('_route' => '_260'), array('a', 'b', 'c'), null, null), + 4781 => array(array('_route' => '_434'), array('a', 'b', 'c'), null, null), + 4830 => array(array('_route' => '_189'), array('a', 'b', 'c'), null, null), + 4878 => array(array('_route' => '_467'), array('a', 'b', 'c'), null, null), + 4935 => array(array('_route' => '_6'), array('a', 'b', 'c'), null, null), + 4982 => array(array('_route' => '_286'), array('a', 'b', 'c'), null, null), + 5029 => array(array('_route' => '_438'), array('a', 'b', 'c'), null, null), + 5081 => array(array('_route' => '_19'), array('a', 'b', 'c'), null, null), + 5131 => array(array('_route' => '_24'), array('a', 'b', 'c'), null, null), + 5177 => array(array('_route' => '_172'), array('a', 'b', 'c'), null, null), + 5230 => array(array('_route' => '_33'), array('a', 'b', 'c'), null, null), + 5277 => array(array('_route' => '_400'), array('a', 'b', 'c'), null, null), + 5324 => array(array('_route' => '_427'), array('a', 'b', 'c'), null, null), + 5376 => array(array('_route' => '_35'), array('a', 'b', 'c'), null, null), + 5423 => array(array('_route' => '_156'), array('a', 'b', 'c'), null, null), + 5475 => array(array('_route' => '_36'), array('a', 'b', 'c'), null, null), + 5522 => array(array('_route' => '_251'), array('a', 'b', 'c'), null, null), + 5574 => array(array('_route' => '_43'), array('a', 'b', 'c'), null, null), + 5621 => array(array('_route' => '_292'), array('a', 'b', 'c'), null, null), + 5668 => array(array('_route' => '_411'), array('a', 'b', 'c'), null, null), + 5720 => array(array('_route' => '_69'), array('a', 'b', 'c'), null, null), + 5767 => array(array('_route' => '_159'), array('a', 'b', 'c'), null, null), + 5814 => array(array('_route' => '_170'), array('a', 'b', 'c'), null, null), + 5861 => array(array('_route' => '_376'), array('a', 'b', 'c'), null, null), + 5913 => array(array('_route' => '_131'), array('a', 'b', 'c'), null, null), + 5960 => array(array('_route' => '_446'), array('a', 'b', 'c'), null, null), + 6015 => array(array('_route' => '_140'), array('a', 'b', 'c'), null, null), + 6061 => array(array('_route' => '_353'), array('a', 'b', 'c'), null, null), + 6112 => array(array('_route' => '_224'), array('a', 'b', 'c'), null, null), + 6158 => array(array('_route' => '_346'), array('a', 'b', 'c'), null, null), + 6204 => array(array('_route' => '_443'), array('a', 'b', 'c'), null, null), + 6254 => array(array('_route' => '_154'), array('a', 'b', 'c'), null, null), + 6305 => array(array('_route' => '_212'), array('a', 'b', 'c'), null, null), + 6352 => array(array('_route' => '_313'), array('a', 'b', 'c'), null, null), + 6399 => array(array('_route' => '_395'), array('a', 'b', 'c'), null, null), + 6446 => array(array('_route' => '_441'), array('a', 'b', 'c'), null, null), + 6498 => array(array('_route' => '_223'), array('a', 'b', 'c'), null, null), + 6545 => array(array('_route' => '_303'), array('a', 'b', 'c'), null, null), + 6594 => array(array('_route' => '_410'), array('a', 'b', 'c'), null, null), + 6642 => array(array('_route' => '_494'), array('a', 'b', 'c'), null, null), + 6702 => array(array('_route' => '_7'), array('a', 'b', 'c'), null, null), + 6748 => array(array('_route' => '_268'), array('a', 'b', 'c'), null, null), + 6796 => array(array('_route' => '_178'), array('a', 'b', 'c'), null, null), + 6843 => array(array('_route' => '_179'), array('a', 'b', 'c'), null, null), + 6890 => array(array('_route' => '_416'), array('a', 'b', 'c'), null, null), + 6942 => array(array('_route' => '_25'), array('a', 'b', 'c'), null, null), + 6989 => array(array('_route' => '_307'), array('a', 'b', 'c'), null, null), + 7036 => array(array('_route' => '_387'), array('a', 'b', 'c'), null, null), + 7083 => array(array('_route' => '_471'), array('a', 'b', 'c'), null, null), + 7132 => array(array('_route' => '_90'), array('a', 'b', 'c'), null, null), + 7183 => array(array('_route' => '_95'), array('a', 'b', 'c'), null, null), + 7230 => array(array('_route' => '_338'), array('a', 'b', 'c'), null, null), + 7277 => array(array('_route' => '_401'), array('a', 'b', 'c'), null, null), + 7329 => array(array('_route' => '_147'), array('a', 'b', 'c'), null, null), + 7376 => array(array('_route' => '_319'), array('a', 'b', 'c'), null, null), + 7423 => array(array('_route' => '_354'), array('a', 'b', 'c'), null, null), + 7470 => array(array('_route' => '_428'), array('a', 'b', 'c'), null, null), + 7522 => array(array('_route' => '_162'), array('a', 'b', 'c'), null, null), + 7572 => array(array('_route' => '_175'), array('a', 'b', 'c'), null, null), + 7618 => array(array('_route' => '_455'), array('a', 'b', 'c'), null, null), + 7666 => array(array('_route' => '_355'), array('a', 'b', 'c'), null, null), + 7718 => array(array('_route' => '_197'), array('a', 'b', 'c'), null, null), + 7768 => array(array('_route' => '_202'), array('a', 'b', 'c'), null, null), + 7813 => array(array('_route' => '_489'), array('a', 'b', 'c'), null, null), + 7863 => array(array('_route' => '_199'), array('a', 'b', 'c'), null, null), + 7914 => array(array('_route' => '_263'), array('a', 'b', 'c'), null, null), + 7961 => array(array('_route' => '_406'), array('a', 'b', 'c'), null, null), + 8010 => array(array('_route' => '_289'), array('a', 'b', 'c'), null, null), + 8058 => array(array('_route' => '_325'), array('a', 'b', 'c'), null, null), + 8106 => array(array('_route' => '_378'), array('a', 'b', 'c'), null, null), + 8154 => array(array('_route' => '_468'), array('a', 'b', 'c'), null, null), + 8211 => array(array('_route' => '_9'), array('a', 'b', 'c'), null, null), + 8258 => array(array('_route' => '_216'), array('a', 'b', 'c'), null, null), + 8307 => array(array('_route' => '_26'), array('a', 'b', 'c'), null, null), + 8355 => array(array('_route' => '_62'), array('a', 'b', 'c'), null, null), + 8406 => array(array('_route' => '_81'), array('a', 'b', 'c'), null, null), + 8453 => array(array('_route' => '_318'), array('a', 'b', 'c'), null, null), + 8505 => array(array('_route' => '_121'), array('a', 'b', 'c'), null, null), + 8551 => array(array('_route' => '_182'), array('a', 'b', 'c'), null, null), + 8603 => array(array('_route' => '_136'), array('a', 'b', 'c'), null, null), + 8650 => array(array('_route' => '_415'), array('a', 'b', 'c'), null, null), + 8697 => array(array('_route' => '_457'), array('a', 'b', 'c'), null, null), + 8744 => array(array('_route' => '_463'), array('a', 'b', 'c'), null, null), + 8796 => array(array('_route' => '_148'), array('a', 'b', 'c'), null, null), + 8843 => array(array('_route' => '_273'), array('a', 'b', 'c'), null, null), + 8892 => array(array('_route' => '_284'), array('a', 'b', 'c'), null, null), + 8940 => array(array('_route' => '_288'), array('a', 'b', 'c'), null, null), + 8991 => array(array('_route' => '_295'), array('a', 'b', 'c'), null, null), + 9038 => array(array('_route' => '_305'), array('a', 'b', 'c'), null, null), + 9085 => array(array('_route' => '_453'), array('a', 'b', 'c'), null, null), + 9134 => array(array('_route' => '_340'), array('a', 'b', 'c'), null, null), + 9185 => array(array('_route' => '_371'), array('a', 'b', 'c'), null, null), + 9232 => array(array('_route' => '_417'), array('a', 'b', 'c'), null, null), + 9284 => array(array('_route' => '_382'), array('a', 'b', 'c'), null, null), + 9331 => array(array('_route' => '_404'), array('a', 'b', 'c'), null, null), + 9389 => array(array('_route' => '_10'), array('a', 'b', 'c'), null, null), + 9436 => array(array('_route' => '_279'), array('a', 'b', 'c'), null, null), + 9483 => array(array('_route' => '_377'), array('a', 'b', 'c'), null, null), + 9535 => array(array('_route' => '_39'), array('a', 'b', 'c'), null, null), + 9582 => array(array('_route' => '_40'), array('a', 'b', 'c'), null, null), + 9629 => array(array('_route' => '_264'), array('a', 'b', 'c'), null, null), + 9676 => array(array('_route' => '_449'), array('a', 'b', 'c'), null, null), + 9728 => array(array('_route' => '_46'), array('a', 'b', 'c'), null, null), + 9775 => array(array('_route' => '_257'), array('a', 'b', 'c'), null, null), + 9822 => array(array('_route' => '_274'), array('a', 'b', 'c'), null, null), + 9869 => array(array('_route' => '_388'), array('a', 'b', 'c'), null, null), + 9921 => array(array('_route' => '_53'), array('a', 'b', 'c'), null, null), + 9968 => array(array('_route' => '_345'), array('a', 'b', 'c'), null, null), + 10020 => array(array('_route' => '_73'), array('a', 'b', 'c'), null, null), + 10068 => array(array('_route' => '_296'), array('a', 'b', 'c'), null, null), + 10121 => array(array('_route' => '_75'), array('a', 'b', 'c'), null, null), + 10169 => array(array('_route' => '_458'), array('a', 'b', 'c'), null, null), + 10225 => array(array('_route' => '_79'), array('a', 'b', 'c'), null, null), + 10272 => array(array('_route' => '_129'), array('a', 'b', 'c'), null, null), + 10319 => array(array('_route' => '_418'), array('a', 'b', 'c'), null, null), + 10368 => array(array('_route' => '_225'), array('a', 'b', 'c'), null, null), + 10416 => array(array('_route' => '_479'), array('a', 'b', 'c'), null, null), + 10466 => array(array('_route' => '_120'), array('a', 'b', 'c'), null, null), + 10515 => array(array('_route' => '_276'), array('a', 'b', 'c'), null, null), + 10564 => array(array('_route' => '_370'), array('a', 'b', 'c'), null, null), + 10616 => array(array('_route' => '_385'), array('a', 'b', 'c'), null, null), + 10664 => array(array('_route' => '_469'), array('a', 'b', 'c'), null, null), + 10714 => array(array('_route' => '_435'), array('a', 'b', 'c'), null, null), + 10772 => array(array('_route' => '_11'), array('a', 'b', 'c'), null, null), + 10820 => array(array('_route' => '_105'), array('a', 'b', 'c'), null, null), + 10868 => array(array('_route' => '_132'), array('a', 'b', 'c'), null, null), + 10921 => array(array('_route' => '_18'), array('a', 'b', 'c'), null, null), + 10969 => array(array('_route' => '_210'), array('a', 'b', 'c'), null, null), + 11017 => array(array('_route' => '_329'), array('a', 'b', 'c'), null, null), + 11073 => array(array('_route' => '_29'), array('a', 'b', 'c'), null, null), + 11120 => array(array('_route' => '_480'), array('a', 'b', 'c'), null, null), + 11169 => array(array('_route' => '_426'), array('a', 'b', 'c'), null, null), + 11222 => array(array('_route' => '_32'), array('a', 'b', 'c'), null, null), + 11270 => array(array('_route' => '_217'), array('a', 'b', 'c'), null, null), + 11318 => array(array('_route' => '_275'), array('a', 'b', 'c'), null, null), + 11371 => array(array('_route' => '_45'), array('a', 'b', 'c'), null, null), + 11419 => array(array('_route' => '_157'), array('a', 'b', 'c'), null, null), + 11467 => array(array('_route' => '_184'), array('a', 'b', 'c'), null, null), + 11515 => array(array('_route' => '_250'), array('a', 'b', 'c'), null, null), + 11563 => array(array('_route' => '_356'), array('a', 'b', 'c'), null, null), + 11616 => array(array('_route' => '_47'), array('a', 'b', 'c'), null, null), + 11664 => array(array('_route' => '_445'), array('a', 'b', 'c'), null, null), + 11714 => array(array('_route' => '_48'), array('a', 'b', 'c'), null, null), + 11766 => array(array('_route' => '_58'), array('a', 'b', 'c'), null, null), + 11814 => array(array('_route' => '_414'), array('a', 'b', 'c'), null, null), + 11862 => array(array('_route' => '_431'), array('a', 'b', 'c'), null, null), + 11915 => array(array('_route' => '_84'), array('a', 'b', 'c'), null, null), + 11963 => array(array('_route' => '_294'), array('a', 'b', 'c'), null, null), + 12011 => array(array('_route' => '_336'), array('a', 'b', 'c'), null, null), + 12059 => array(array('_route' => '_465'), array('a', 'b', 'c'), null, null), + 12112 => array(array('_route' => '_103'), array('a', 'b', 'c'), null, null), + 12160 => array(array('_route' => '_111'), array('a', 'b', 'c'), null, null), + 12208 => array(array('_route' => '_207'), array('a', 'b', 'c'), null, null), + 12256 => array(array('_route' => '_402'), array('a', 'b', 'c'), null, null), + 12309 => array(array('_route' => '_230'), array('a', 'b', 'c'), null, null), + 12356 => array(array('_route' => '_331'), array('a', 'b', 'c'), null, null), + 12406 => array(array('_route' => '_248'), array('a', 'b', 'c'), null, null), + 12455 => array(array('_route' => '_282'), array('a', 'b', 'c'), null, null), + 12513 => array(array('_route' => '_15'), array('a', 'b', 'c'), null, null), + 12561 => array(array('_route' => '_130'), array('a', 'b', 'c'), null, null), + 12609 => array(array('_route' => '_231'), array('a', 'b', 'c'), null, null), + 12657 => array(array('_route' => '_365'), array('a', 'b', 'c'), null, null), + 12705 => array(array('_route' => '_448'), array('a', 'b', 'c'), null, null), + 12758 => array(array('_route' => '_20'), array('a', 'b', 'c'), null, null), + 12806 => array(array('_route' => '_93'), array('a', 'b', 'c'), null, null), + 12854 => array(array('_route' => '_186'), array('a', 'b', 'c'), null, null), + 12902 => array(array('_route' => '_460'), array('a', 'b', 'c'), null, null), + 12955 => array(array('_route' => '_52'), array('a', 'b', 'c'), null, null), + 13003 => array(array('_route' => '_447'), array('a', 'b', 'c'), null, null), + 13056 => array(array('_route' => '_56'), array('a', 'b', 'c'), null, null), + 13104 => array(array('_route' => '_133'), array('a', 'b', 'c'), null, null), + 13152 => array(array('_route' => '_297'), array('a', 'b', 'c'), null, null), + 13205 => array(array('_route' => '_82'), array('a', 'b', 'c'), null, null), + 13253 => array(array('_route' => '_165'), array('a', 'b', 'c'), null, null), + 13301 => array(array('_route' => '_213'), array('a', 'b', 'c'), null, null), + 13351 => array(array('_route' => '_86'), array('a', 'b', 'c'), null, null), + 13403 => array(array('_route' => '_92'), array('a', 'b', 'c'), null, null), + 13450 => array(array('_route' => '_280'), array('a', 'b', 'c'), null, null), + 13500 => array(array('_route' => '_143'), array('a', 'b', 'c'), null, null), + 13549 => array(array('_route' => '_177'), array('a', 'b', 'c'), null, null), + 13601 => array(array('_route' => '_188'), array('a', 'b', 'c'), null, null), + 13649 => array(array('_route' => '_311'), array('a', 'b', 'c'), null, null), + 13697 => array(array('_route' => '_350'), array('a', 'b', 'c'), null, null), + 13750 => array(array('_route' => '_226'), array('a', 'b', 'c'), null, null), + 13798 => array(array('_route' => '_291'), array('a', 'b', 'c'), null, null), + 13851 => array(array('_route' => '_244'), array('a', 'b', 'c'), null, null), + 13898 => array(array('_route' => '_287'), array('a', 'b', 'c'), null, null), + 13951 => array(array('_route' => '_300'), array('a', 'b', 'c'), null, null), + 13999 => array(array('_route' => '_451'), array('a', 'b', 'c'), null, null), + 14047 => array(array('_route' => '_452'), array('a', 'b', 'c'), null, null), + 14095 => array(array('_route' => '_481'), array('a', 'b', 'c'), null, null), + 14145 => array(array('_route' => '_312'), array('a', 'b', 'c'), null, null), + 14203 => array(array('_route' => '_17'), array('a', 'b', 'c'), null, null), + 14251 => array(array('_route' => '_227'), array('a', 'b', 'c'), null, null), + 14299 => array(array('_route' => '_393'), array('a', 'b', 'c'), null, null), + 14349 => array(array('_route' => '_57'), array('a', 'b', 'c'), null, null), + 14401 => array(array('_route' => '_61'), array('a', 'b', 'c'), null, null), + 14449 => array(array('_route' => '_112'), array('a', 'b', 'c'), null, null), + 14500 => array(array('_route' => '_135'), array('a', 'b', 'c'), null, null), + 14547 => array(array('_route' => '_271'), array('a', 'b', 'c'), null, null), + 14596 => array(array('_route' => '_459'), array('a', 'b', 'c'), null, null), + 14649 => array(array('_route' => '_67'), array('a', 'b', 'c'), null, null), + 14697 => array(array('_route' => '_113'), array('a', 'b', 'c'), null, null), + 14745 => array(array('_route' => '_497'), array('a', 'b', 'c'), null, null), + 14795 => array(array('_route' => '_70'), array('a', 'b', 'c'), null, null), + 14847 => array(array('_route' => '_89'), array('a', 'b', 'c'), null, null), + 14895 => array(array('_route' => '_128'), array('a', 'b', 'c'), null, null), + 14948 => array(array('_route' => '_150'), array('a', 'b', 'c'), null, null), + 14996 => array(array('_route' => '_166'), array('a', 'b', 'c'), null, null), + 15047 => array(array('_route' => '_206'), array('a', 'b', 'c'), null, null), + 15094 => array(array('_route' => '_419'), array('a', 'b', 'c'), null, null), + 15148 => array(array('_route' => '_201'), array('a', 'b', 'c'), null, null), + 15196 => array(array('_route' => '_314'), array('a', 'b', 'c'), null, null), + 15244 => array(array('_route' => '_429'), array('a', 'b', 'c'), null, null), + 15297 => array(array('_route' => '_228'), array('a', 'b', 'c'), null, null), + 15345 => array(array('_route' => '_477'), array('a', 'b', 'c'), null, null), + 15395 => array(array('_route' => '_272'), array('a', 'b', 'c'), null, null), + 15444 => array(array('_route' => '_486'), array('a', 'b', 'c'), null, null), + 15502 => array(array('_route' => '_21'), array('a', 'b', 'c'), null, null), + 15550 => array(array('_route' => '_247'), array('a', 'b', 'c'), null, null), + 15598 => array(array('_route' => '_424'), array('a', 'b', 'c'), null, null), + 15646 => array(array('_route' => '_499'), array('a', 'b', 'c'), null, null), + 15699 => array(array('_route' => '_23'), array('a', 'b', 'c'), null, null), + 15747 => array(array('_route' => '_152'), array('a', 'b', 'c'), null, null), + 15795 => array(array('_route' => '_304'), array('a', 'b', 'c'), null, null), + 15843 => array(array('_route' => '_352'), array('a', 'b', 'c'), null, null), + 15896 => array(array('_route' => '_28'), array('a', 'b', 'c'), null, null), + 15944 => array(array('_route' => '_240'), array('a', 'b', 'c'), null, null), + 16000 => array(array('_route' => '_30'), array('a', 'b', 'c'), null, null), + 16047 => array(array('_route' => '_41'), array('a', 'b', 'c'), null, null), + 16096 => array(array('_route' => '_301'), array('a', 'b', 'c'), null, null), + 16149 => array(array('_route' => '_66'), array('a', 'b', 'c'), null, null), + 16197 => array(array('_route' => '_72'), array('a', 'b', 'c'), null, null), + 16245 => array(array('_route' => '_320'), array('a', 'b', 'c'), null, null), + 16298 => array(array('_route' => '_78'), array('a', 'b', 'c'), null, null), + 16346 => array(array('_route' => '_337'), array('a', 'b', 'c'), null, null), + 16394 => array(array('_route' => '_399'), array('a', 'b', 'c'), null, null), + 16442 => array(array('_route' => '_495'), array('a', 'b', 'c'), null, null), + 16492 => array(array('_route' => '_85'), array('a', 'b', 'c'), null, null), + 16544 => array(array('_route' => '_101'), array('a', 'b', 'c'), null, null), + 16592 => array(array('_route' => '_176'), array('a', 'b', 'c'), null, null), + 16640 => array(array('_route' => '_246'), array('a', 'b', 'c'), null, null), + 16693 => array(array('_route' => '_125'), array('a', 'b', 'c'), null, null), + 16741 => array(array('_route' => '_341'), array('a', 'b', 'c'), null, null), + 16794 => array(array('_route' => '_137'), array('a', 'b', 'c'), null, null), + 16842 => array(array('_route' => '_270'), array('a', 'b', 'c'), null, null), + 16890 => array(array('_route' => '_386'), array('a', 'b', 'c'), null, null), + 16943 => array(array('_route' => '_169'), array('a', 'b', 'c'), null, null), + 16991 => array(array('_route' => '_200'), array('a', 'b', 'c'), null, null), + 17039 => array(array('_route' => '_262'), array('a', 'b', 'c'), null, null), + 17092 => array(array('_route' => '_187'), array('a', 'b', 'c'), null, null), + 17140 => array(array('_route' => '_333'), array('a', 'b', 'c'), null, null), + 17190 => array(array('_route' => '_215'), array('a', 'b', 'c'), null, null), + 17239 => array(array('_route' => '_316'), array('a', 'b', 'c'), null, null), + 17288 => array(array('_route' => '_343'), array('a', 'b', 'c'), null, null), + 17346 => array(array('_route' => '_22'), array('a', 'b', 'c'), null, null), + 17394 => array(array('_route' => '_420'), array('a', 'b', 'c'), null, null), + 17447 => array(array('_route' => '_55'), array('a', 'b', 'c'), null, null), + 17494 => array(array('_route' => '_496'), array('a', 'b', 'c'), null, null), + 17547 => array(array('_route' => '_153'), array('a', 'b', 'c'), null, null), + 17595 => array(array('_route' => '_344'), array('a', 'b', 'c'), null, null), + 17648 => array(array('_route' => '_160'), array('a', 'b', 'c'), null, null), + 17696 => array(array('_route' => '_398'), array('a', 'b', 'c'), null, null), + 17749 => array(array('_route' => '_161'), array('a', 'b', 'c'), null, null), + 17797 => array(array('_route' => '_193'), array('a', 'b', 'c'), null, null), + 17847 => array(array('_route' => '_174'), array('a', 'b', 'c'), null, null), + 17899 => array(array('_route' => '_209'), array('a', 'b', 'c'), null, null), + 17947 => array(array('_route' => '_261'), array('a', 'b', 'c'), null, null), + 18000 => array(array('_route' => '_222'), array('a', 'b', 'c'), null, null), + 18048 => array(array('_route' => '_323'), array('a', 'b', 'c'), null, null), + 18096 => array(array('_route' => '_380'), array('a', 'b', 'c'), null, null), + 18149 => array(array('_route' => '_232'), array('a', 'b', 'c'), null, null), + 18197 => array(array('_route' => '_383'), array('a', 'b', 'c'), null, null), + 18247 => array(array('_route' => '_306'), array('a', 'b', 'c'), null, null), + 18296 => array(array('_route' => '_327'), array('a', 'b', 'c'), null, null), + 18345 => array(array('_route' => '_364'), array('a', 'b', 'c'), null, null), + 18397 => array(array('_route' => '_403'), array('a', 'b', 'c'), null, null), + 18445 => array(array('_route' => '_405'), array('a', 'b', 'c'), null, null), + 18495 => array(array('_route' => '_412'), array('a', 'b', 'c'), null, null), + 18553 => array(array('_route' => '_27'), array('a', 'b', 'c'), null, null), + 18601 => array(array('_route' => '_134'), array('a', 'b', 'c'), null, null), + 18649 => array(array('_route' => '_245'), array('a', 'b', 'c'), null, null), + 18702 => array(array('_route' => '_59'), array('a', 'b', 'c'), null, null), + 18750 => array(array('_route' => '_208'), array('a', 'b', 'c'), null, null), + 18803 => array(array('_route' => '_60'), array('a', 'b', 'c'), null, null), + 18851 => array(array('_route' => '_119'), array('a', 'b', 'c'), null, null), + 18902 => array(array('_route' => '_163'), array('a', 'b', 'c'), null, null), + 18949 => array(array('_route' => '_249'), array('a', 'b', 'c'), null, null), + 18998 => array(array('_route' => '_278'), array('a', 'b', 'c'), null, null), + 19051 => array(array('_route' => '_63'), array('a', 'b', 'c'), null, null), + 19099 => array(array('_route' => '_195'), array('a', 'b', 'c'), null, null), + 19147 => array(array('_route' => '_252'), array('a', 'b', 'c'), null, null), + 19195 => array(array('_route' => '_461'), array('a', 'b', 'c'), null, null), + 19248 => array(array('_route' => '_126'), array('a', 'b', 'c'), null, null), + 19296 => array(array('_route' => '_158'), array('a', 'b', 'c'), null, null), + 19344 => array(array('_route' => '_221'), array('a', 'b', 'c'), null, null), + 19392 => array(array('_route' => '_269'), array('a', 'b', 'c'), null, null), + 19440 => array(array('_route' => '_310'), array('a', 'b', 'c'), null, null), + 19496 => array(array('_route' => '_138'), array('a', 'b', 'c'), null, null), + 19543 => array(array('_route' => '_348'), array('a', 'b', 'c'), null, null), + 19592 => array(array('_route' => '_236'), array('a', 'b', 'c'), null, null), + 19640 => array(array('_route' => '_433'), array('a', 'b', 'c'), null, null), + 19693 => array(array('_route' => '_141'), array('a', 'b', 'c'), null, null), + 19741 => array(array('_route' => '_283'), array('a', 'b', 'c'), null, null), + 19794 => array(array('_route' => '_144'), array('a', 'b', 'c'), null, null), + 19842 => array(array('_route' => '_191'), array('a', 'b', 'c'), null, null), + 19895 => array(array('_route' => '_168'), array('a', 'b', 'c'), null, null), + 19943 => array(array('_route' => '_363'), array('a', 'b', 'c'), null, null), + 19991 => array(array('_route' => '_381'), array('a', 'b', 'c'), null, null), + 20044 => array(array('_route' => '_180'), array('a', 'b', 'c'), null, null), + 20092 => array(array('_route' => '_339'), array('a', 'b', 'c'), null, null), + 20142 => array(array('_route' => '_196'), array('a', 'b', 'c'), null, null), + 20194 => array(array('_route' => '_198'), array('a', 'b', 'c'), null, null), + 20242 => array(array('_route' => '_285'), array('a', 'b', 'c'), null, null), + 20292 => array(array('_route' => '_349'), array('a', 'b', 'c'), null, null), + 20344 => array(array('_route' => '_367'), array('a', 'b', 'c'), null, null), + 20392 => array(array('_route' => '_384'), array('a', 'b', 'c'), null, null), + 20440 => array(array('_route' => '_498'), array('a', 'b', 'c'), null, null), + 20490 => array(array('_route' => '_369'), array('a', 'b', 'c'), null, null), + 20542 => array(array('_route' => '_408'), array('a', 'b', 'c'), null, null), + 20590 => array(array('_route' => '_413'), array('a', 'b', 'c'), null, null), + 20652 => array(array('_route' => '_44'), array('a', 'b', 'c'), null, null), + 20699 => array(array('_route' => '_256'), array('a', 'b', 'c'), null, null), + 20748 => array(array('_route' => '_173'), array('a', 'b', 'c'), null, null), + 20796 => array(array('_route' => '_266'), array('a', 'b', 'c'), null, null), + 20844 => array(array('_route' => '_392'), array('a', 'b', 'c'), null, null), + 20892 => array(array('_route' => '_430'), array('a', 'b', 'c'), null, null), + 20940 => array(array('_route' => '_482'), array('a', 'b', 'c'), null, null), + 20993 => array(array('_route' => '_49'), array('a', 'b', 'c'), null, null), + 21041 => array(array('_route' => '_94'), array('a', 'b', 'c'), null, null), + 21089 => array(array('_route' => '_407'), array('a', 'b', 'c'), null, null), + 21142 => array(array('_route' => '_65'), array('a', 'b', 'c'), null, null), + 21190 => array(array('_route' => '_181'), array('a', 'b', 'c'), null, null), + 21238 => array(array('_route' => '_437'), array('a', 'b', 'c'), null, null), + 21291 => array(array('_route' => '_76'), array('a', 'b', 'c'), null, null), + 21339 => array(array('_route' => '_357'), array('a', 'b', 'c'), null, null), + 21392 => array(array('_route' => '_80'), array('a', 'b', 'c'), null, null), + 21440 => array(array('_route' => '_106'), array('a', 'b', 'c'), null, null), + 21493 => array(array('_route' => '_83'), array('a', 'b', 'c'), null, null), + 21541 => array(array('_route' => '_255'), array('a', 'b', 'c'), null, null), + 21589 => array(array('_route' => '_330'), array('a', 'b', 'c'), null, null), + 21642 => array(array('_route' => '_100'), array('a', 'b', 'c'), null, null), + 21690 => array(array('_route' => '_396'), array('a', 'b', 'c'), null, null), + 21738 => array(array('_route' => '_422'), array('a', 'b', 'c'), null, null), + 21791 => array(array('_route' => '_149'), array('a', 'b', 'c'), null, null), + 21839 => array(array('_route' => '_324'), array('a', 'b', 'c'), null, null), + 21892 => array(array('_route' => '_164'), array('a', 'b', 'c'), null, null), + 21940 => array(array('_route' => '_423'), array('a', 'b', 'c'), null, null), + 21990 => array(array('_route' => '_241'), array('a', 'b', 'c'), null, null), + 22042 => array(array('_route' => '_290'), array('a', 'b', 'c'), null, null), + 22090 => array(array('_route' => '_335'), array('a', 'b', 'c'), null, null), + 22140 => array(array('_route' => '_373'), array('a', 'b', 'c'), null, null), + 22189 => array(array('_route' => '_375'), array('a', 'b', 'c'), null, null), + 22238 => array(array('_route' => '_450'), array('a', 'b', 'c'), null, null), + 22287 => array(array('_route' => '_464'), array('a', 'b', 'c'), null, null), + 22345 => array(array('_route' => '_51'), array('a', 'b', 'c'), null, null), + 22393 => array(array('_route' => '_77'), array('a', 'b', 'c'), null, null), + 22441 => array(array('_route' => '_234'), array('a', 'b', 'c'), null, null), + 22489 => array(array('_route' => '_394'), array('a', 'b', 'c'), null, null), + 22542 => array(array('_route' => '_88'), array('a', 'b', 'c'), null, null), + 22590 => array(array('_route' => '_155'), array('a', 'b', 'c'), null, null), + 22643 => array(array('_route' => '_96'), array('a', 'b', 'c'), null, null), + 22691 => array(array('_route' => '_298'), array('a', 'b', 'c'), null, null), + 22739 => array(array('_route' => '_470'), array('a', 'b', 'c'), null, null), + 22792 => array(array('_route' => '_109'), array('a', 'b', 'c'), null, null), + 22840 => array(array('_route' => '_204'), array('a', 'b', 'c'), null, null), + 22893 => array(array('_route' => '_115'), array('a', 'b', 'c'), null, null), + 22941 => array(array('_route' => '_145'), array('a', 'b', 'c'), null, null), + 22994 => array(array('_route' => '_123'), array('a', 'b', 'c'), null, null), + 23042 => array(array('_route' => '_277'), array('a', 'b', 'c'), null, null), + 23090 => array(array('_route' => '_473'), array('a', 'b', 'c'), null, null), + 23143 => array(array('_route' => '_334'), array('a', 'b', 'c'), null, null), + 23191 => array(array('_route' => '_493'), array('a', 'b', 'c'), null, null), + 23244 => array(array('_route' => '_372'), array('a', 'b', 'c'), null, null), + 23292 => array(array('_route' => '_432'), array('a', 'b', 'c'), null, null), + 23340 => array(array('_route' => '_436'), array('a', 'b', 'c'), null, null), + 23393 => array(array('_route' => '_425'), array('a', 'b', 'c'), null, null), + 23441 => array(array('_route' => '_456'), array('a', 'b', 'c'), null, null), + 23489 => array(array('_route' => '_474'), array('a', 'b', 'c'), null, null), + 23539 => array(array('_route' => '_485'), array('a', 'b', 'c'), null, null), + 23594 => array(array('_route' => '_91'), array('a', 'b', 'c'), null, null), + 23646 => array(array('_route' => '_110'), array('a', 'b', 'c'), null, null), + 23694 => array(array('_route' => '_114'), array('a', 'b', 'c'), null, null), + 23750 => array(array('_route' => '_118'), array('a', 'b', 'c'), null, null), + 23796 => array(array('_route' => '_475'), array('a', 'b', 'c'), null, null), + 23844 => array(array('_route' => '_366'), array('a', 'b', 'c'), null, null), + 23897 => array(array('_route' => '_167'), array('a', 'b', 'c'), null, null), + 23945 => array(array('_route' => '_192'), array('a', 'b', 'c'), null, null), + 23993 => array(array('_route' => '_342'), array('a', 'b', 'c'), null, null), + 24046 => array(array('_route' => '_229'), array('a', 'b', 'c'), null, null), + 24097 => array(array('_route' => '_235'), array('a', 'b', 'c'), null, null), + 24144 => array(array('_route' => '_302'), array('a', 'b', 'c'), null, null), + 24193 => array(array('_route' => '_322'), array('a', 'b', 'c'), null, null), + 24246 => array(array('_route' => '_237'), array('a', 'b', 'c'), null, null), + 24294 => array(array('_route' => '_293'), array('a', 'b', 'c'), null, null), + 24347 => array(array('_route' => '_239'), array('a', 'b', 'c'), null, null), + 24395 => array(array('_route' => '_444'), array('a', 'b', 'c'), null, null), + 24443 => array(array('_route' => '_491'), array('a', 'b', 'c'), null, null), + 24491 => array(array('_route' => '_492'), array('a', 'b', 'c'), null, null), + 24541 => array(array('_route' => '_258'), array('a', 'b', 'c'), null, null), + 24590 => array(array('_route' => '_317'), array('a', 'b', 'c'), null, null), + 24639 => array(array('_route' => '_361'), array('a', 'b', 'c'), null, null), + 24688 => array(array('_route' => '_391'), array('a', 'b', 'c'), null, null), + 24737 => array(array('_route' => '_462'), array('a', 'b', 'c'), null, null), + 24786 => array(array('_route' => '_476'), array('a', 'b', 'c'), null, null), + 24837 => array(array('_route' => '_501'), array('a', 'b', 'c'), null, null), + 24889 => array(array('_route' => '_514'), array('a', 'b', 'c'), null, null), + 24937 => array(array('_route' => '_731'), array('a', 'b', 'c'), null, null), + 24990 => array(array('_route' => '_522'), array('a', 'b', 'c'), null, null), + 25038 => array(array('_route' => '_693'), array('a', 'b', 'c'), null, null), + 25091 => array(array('_route' => '_537'), array('a', 'b', 'c'), null, null), + 25139 => array(array('_route' => '_554'), array('a', 'b', 'c'), null, null), + 25187 => array(array('_route' => '_645'), array('a', 'b', 'c'), null, null), + 25235 => array(array('_route' => '_862'), array('a', 'b', 'c'), null, null), + 25288 => array(array('_route' => '_539'), array('a', 'b', 'c'), null, null), + 25336 => array(array('_route' => '_729'), array('a', 'b', 'c'), null, null), + 25384 => array(array('_route' => '_897'), array('a', 'b', 'c'), null, null), + 25437 => array(array('_route' => '_561'), array('a', 'b', 'c'), null, null), + 25485 => array(array('_route' => '_615'), array('a', 'b', 'c'), null, null), + 25533 => array(array('_route' => '_764'), array('a', 'b', 'c'), null, null), + 25581 => array(array('_route' => '_948'), array('a', 'b', 'c'), null, null), + 25634 => array(array('_route' => '_617'), array('a', 'b', 'c'), null, null), + 25682 => array(array('_route' => '_671'), array('a', 'b', 'c'), null, null), + 25735 => array(array('_route' => '_649'), array('a', 'b', 'c'), null, null), + 25783 => array(array('_route' => '_651'), array('a', 'b', 'c'), null, null), + 25831 => array(array('_route' => '_684'), array('a', 'b', 'c'), null, null), + 25884 => array(array('_route' => '_669'), array('a', 'b', 'c'), null, null), + 25932 => array(array('_route' => '_743'), array('a', 'b', 'c'), null, null), + 25980 => array(array('_route' => '_962'), array('a', 'b', 'c'), null, null), + 26033 => array(array('_route' => '_694'), array('a', 'b', 'c'), null, null), + 26081 => array(array('_route' => '_985'), array('a', 'b', 'c'), null, null), + 26134 => array(array('_route' => '_707'), array('a', 'b', 'c'), null, null), + 26182 => array(array('_route' => '_718'), array('a', 'b', 'c'), null, null), + 26235 => array(array('_route' => '_720'), array('a', 'b', 'c'), null, null), + 26283 => array(array('_route' => '_745'), array('a', 'b', 'c'), null, null), + 26333 => array(array('_route' => '_874'), array('a', 'b', 'c'), null, null), + 26391 => array(array('_route' => '_502'), array('a', 'b', 'c'), null, null), + 26439 => array(array('_route' => '_667'), array('a', 'b', 'c'), null, null), + 26487 => array(array('_route' => '_911'), array('a', 'b', 'c'), null, null), + 26535 => array(array('_route' => '_942'), array('a', 'b', 'c'), null, null), + 26585 => array(array('_route' => '_504'), array('a', 'b', 'c'), null, null), + 26637 => array(array('_route' => '_524'), array('a', 'b', 'c'), null, null), + 26685 => array(array('_route' => '_732'), array('a', 'b', 'c'), null, null), + 26738 => array(array('_route' => '_596'), array('a', 'b', 'c'), null, null), + 26786 => array(array('_route' => '_601'), array('a', 'b', 'c'), null, null), + 26839 => array(array('_route' => '_620'), array('a', 'b', 'c'), null, null), + 26887 => array(array('_route' => '_631'), array('a', 'b', 'c'), null, null), + 26935 => array(array('_route' => '_771'), array('a', 'b', 'c'), null, null), + 26983 => array(array('_route' => '_937'), array('a', 'b', 'c'), null, null), + 27031 => array(array('_route' => '_999'), array('a', 'b', 'c'), null, null), + 27084 => array(array('_route' => '_657'), array('a', 'b', 'c'), null, null), + 27132 => array(array('_route' => '_701'), array('a', 'b', 'c'), null, null), + 27185 => array(array('_route' => '_662'), array('a', 'b', 'c'), null, null), + 27233 => array(array('_route' => '_797'), array('a', 'b', 'c'), null, null), + 27281 => array(array('_route' => '_924'), array('a', 'b', 'c'), null, null), + 27334 => array(array('_route' => '_702'), array('a', 'b', 'c'), null, null), + 27382 => array(array('_route' => '_750'), array('a', 'b', 'c'), null, null), + 27435 => array(array('_route' => '_749'), array('a', 'b', 'c'), null, null), + 27483 => array(array('_route' => '_837'), array('a', 'b', 'c'), null, null), + 27533 => array(array('_route' => '_758'), array('a', 'b', 'c'), null, null), + 27585 => array(array('_route' => '_810'), array('a', 'b', 'c'), null, null), + 27633 => array(array('_route' => '_902'), array('a', 'b', 'c'), null, null), + 27683 => array(array('_route' => '_845'), array('a', 'b', 'c'), null, null), + 27741 => array(array('_route' => '_503'), array('a', 'b', 'c'), null, null), + 27792 => array(array('_route' => '_756'), array('a', 'b', 'c'), null, null), + 27839 => array(array('_route' => '_799'), array('a', 'b', 'c'), null, null), + 27888 => array(array('_route' => '_769'), array('a', 'b', 'c'), null, null), + 27936 => array(array('_route' => '_981'), array('a', 'b', 'c'), null, null), + 27989 => array(array('_route' => '_507'), array('a', 'b', 'c'), null, null), + 28037 => array(array('_route' => '_672'), array('a', 'b', 'c'), null, null), + 28085 => array(array('_route' => '_790'), array('a', 'b', 'c'), null, null), + 28138 => array(array('_route' => '_515'), array('a', 'b', 'c'), null, null), + 28186 => array(array('_route' => '_523'), array('a', 'b', 'c'), null, null), + 28234 => array(array('_route' => '_957'), array('a', 'b', 'c'), null, null), + 28282 => array(array('_route' => '_995'), array('a', 'b', 'c'), null, null), + 28335 => array(array('_route' => '_532'), array('a', 'b', 'c'), null, null), + 28383 => array(array('_route' => '_642'), array('a', 'b', 'c'), null, null), + 28433 => array(array('_route' => '_579'), array('a', 'b', 'c'), null, null), + 28485 => array(array('_route' => '_625'), array('a', 'b', 'c'), null, null), + 28533 => array(array('_route' => '_916'), array('a', 'b', 'c'), null, null), + 28586 => array(array('_route' => '_633'), array('a', 'b', 'c'), null, null), + 28634 => array(array('_route' => '_656'), array('a', 'b', 'c'), null, null), + 28687 => array(array('_route' => '_658'), array('a', 'b', 'c'), null, null), + 28735 => array(array('_route' => '_943'), array('a', 'b', 'c'), null, null), + 28788 => array(array('_route' => '_664'), array('a', 'b', 'c'), null, null), + 28836 => array(array('_route' => '_852'), array('a', 'b', 'c'), null, null), + 28884 => array(array('_route' => '_870'), array('a', 'b', 'c'), null, null), + 28937 => array(array('_route' => '_683'), array('a', 'b', 'c'), null, null), + 28985 => array(array('_route' => '_915'), array('a', 'b', 'c'), null, null), + 29038 => array(array('_route' => '_719'), array('a', 'b', 'c'), null, null), + 29086 => array(array('_route' => '_859'), array('a', 'b', 'c'), null, null), + 29134 => array(array('_route' => '_912'), array('a', 'b', 'c'), null, null), + 29182 => array(array('_route' => '_978'), array('a', 'b', 'c'), null, null), + 29235 => array(array('_route' => '_738'), array('a', 'b', 'c'), null, null), + 29283 => array(array('_route' => '_883'), array('a', 'b', 'c'), null, null), + 29333 => array(array('_route' => '_741'), array('a', 'b', 'c'), null, null), + 29382 => array(array('_route' => '_760'), array('a', 'b', 'c'), null, null), + 29431 => array(array('_route' => '_895'), array('a', 'b', 'c'), null, null), + 29489 => array(array('_route' => '_505'), array('a', 'b', 'c'), null, null), + 29537 => array(array('_route' => '_935'), array('a', 'b', 'c'), null, null), + 29590 => array(array('_route' => '_509'), array('a', 'b', 'c'), null, null), + 29638 => array(array('_route' => '_820'), array('a', 'b', 'c'), null, null), + 29686 => array(array('_route' => '_910'), array('a', 'b', 'c'), null, null), + 29739 => array(array('_route' => '_518'), array('a', 'b', 'c'), null, null), + 29787 => array(array('_route' => '_618'), array('a', 'b', 'c'), null, null), + 29840 => array(array('_route' => '_546'), array('a', 'b', 'c'), null, null), + 29888 => array(array('_route' => '_740'), array('a', 'b', 'c'), null, null), + 29936 => array(array('_route' => '_867'), array('a', 'b', 'c'), null, null), + 29989 => array(array('_route' => '_572'), array('a', 'b', 'c'), null, null), + 30037 => array(array('_route' => '_952'), array('a', 'b', 'c'), null, null), + 30090 => array(array('_route' => '_573'), array('a', 'b', 'c'), null, null), + 30138 => array(array('_route' => '_692'), array('a', 'b', 'c'), null, null), + 30186 => array(array('_route' => '_700'), array('a', 'b', 'c'), null, null), + 30234 => array(array('_route' => '_772'), array('a', 'b', 'c'), null, null), + 30284 => array(array('_route' => '_653'), array('a', 'b', 'c'), null, null), + 30336 => array(array('_route' => '_695'), array('a', 'b', 'c'), null, null), + 30384 => array(array('_route' => '_748'), array('a', 'b', 'c'), null, null), + 30437 => array(array('_route' => '_710'), array('a', 'b', 'c'), null, null), + 30485 => array(array('_route' => '_716'), array('a', 'b', 'c'), null, null), + 30533 => array(array('_route' => '_969'), array('a', 'b', 'c'), null, null), + 30586 => array(array('_route' => '_734'), array('a', 'b', 'c'), null, null), + 30634 => array(array('_route' => '_742'), array('a', 'b', 'c'), null, null), + 30682 => array(array('_route' => '_844'), array('a', 'b', 'c'), null, null), + 30735 => array(array('_route' => '_763'), array('a', 'b', 'c'), null, null), + 30783 => array(array('_route' => '_965'), array('a', 'b', 'c'), null, null), + 30836 => array(array('_route' => '_778'), array('a', 'b', 'c'), null, null), + 30884 => array(array('_route' => '_813'), array('a', 'b', 'c'), null, null), + 30932 => array(array('_route' => '_831'), array('a', 'b', 'c'), null, null), + 30982 => array(array('_route' => '_955'), array('a', 'b', 'c'), null, null), + 31031 => array(array('_route' => '_997'), array('a', 'b', 'c'), null, null), + 31089 => array(array('_route' => '_506'), array('a', 'b', 'c'), null, null), + 31137 => array(array('_route' => '_575'), array('a', 'b', 'c'), null, null), + 31190 => array(array('_route' => '_516'), array('a', 'b', 'c'), null, null), + 31238 => array(array('_route' => '_553'), array('a', 'b', 'c'), null, null), + 31291 => array(array('_route' => '_528'), array('a', 'b', 'c'), null, null), + 31339 => array(array('_route' => '_847'), array('a', 'b', 'c'), null, null), + 31387 => array(array('_route' => '_904'), array('a', 'b', 'c'), null, null), + 31440 => array(array('_route' => '_574'), array('a', 'b', 'c'), null, null), + 31488 => array(array('_route' => '_818'), array('a', 'b', 'c'), null, null), + 31538 => array(array('_route' => '_577'), array('a', 'b', 'c'), null, null), + 31590 => array(array('_route' => '_584'), array('a', 'b', 'c'), null, null), + 31638 => array(array('_route' => '_905'), array('a', 'b', 'c'), null, null), + 31691 => array(array('_route' => '_612'), array('a', 'b', 'c'), null, null), + 31739 => array(array('_route' => '_688'), array('a', 'b', 'c'), null, null), + 31787 => array(array('_route' => '_854'), array('a', 'b', 'c'), null, null), + 31840 => array(array('_route' => '_613'), array('a', 'b', 'c'), null, null), + 31888 => array(array('_route' => '_767'), array('a', 'b', 'c'), null, null), + 31941 => array(array('_route' => '_666'), array('a', 'b', 'c'), null, null), + 31989 => array(array('_route' => '_759'), array('a', 'b', 'c'), null, null), + 32037 => array(array('_route' => '_827'), array('a', 'b', 'c'), null, null), + 32085 => array(array('_route' => '_840'), array('a', 'b', 'c'), null, null), + 32138 => array(array('_route' => '_680'), array('a', 'b', 'c'), null, null), + 32186 => array(array('_route' => '_784'), array('a', 'b', 'c'), null, null), + 32234 => array(array('_route' => '_842'), array('a', 'b', 'c'), null, null), + 32282 => array(array('_route' => '_860'), array('a', 'b', 'c'), null, null), + 32332 => array(array('_route' => '_704'), array('a', 'b', 'c'), null, null), + 32381 => array(array('_route' => '_727'), array('a', 'b', 'c'), null, null), + 32430 => array(array('_route' => '_777'), array('a', 'b', 'c'), null, null), + 32482 => array(array('_route' => '_838'), array('a', 'b', 'c'), null, null), + 32530 => array(array('_route' => '_861'), array('a', 'b', 'c'), null, null), + 32583 => array(array('_route' => '_849'), array('a', 'b', 'c'), null, null), + 32631 => array(array('_route' => '_982'), array('a', 'b', 'c'), null, null), + 32679 => array(array('_route' => '_986'), array('a', 'b', 'c'), null, null), + 32741 => array(array('_route' => '_508'), array('a', 'b', 'c'), null, null), + 32788 => array(array('_route' => '_517'), array('a', 'b', 'c'), null, null), + 32837 => array(array('_route' => '_622'), array('a', 'b', 'c'), null, null), + 32890 => array(array('_route' => '_513'), array('a', 'b', 'c'), null, null), + 32938 => array(array('_route' => '_655'), array('a', 'b', 'c'), null, null), + 32986 => array(array('_route' => '_843'), array('a', 'b', 'c'), null, null), + 33034 => array(array('_route' => '_939'), array('a', 'b', 'c'), null, null), + 33084 => array(array('_route' => '_529'), array('a', 'b', 'c'), null, null), + 33136 => array(array('_route' => '_535'), array('a', 'b', 'c'), null, null), + 33184 => array(array('_route' => '_685'), array('a', 'b', 'c'), null, null), + 33240 => array(array('_route' => '_559'), array('a', 'b', 'c'), null, null), + 33287 => array(array('_route' => '_661'), array('a', 'b', 'c'), null, null), + 33336 => array(array('_route' => '_768'), array('a', 'b', 'c'), null, null), + 33389 => array(array('_route' => '_589'), array('a', 'b', 'c'), null, null), + 33437 => array(array('_route' => '_647'), array('a', 'b', 'c'), null, null), + 33485 => array(array('_route' => '_652'), array('a', 'b', 'c'), null, null), + 33533 => array(array('_route' => '_834'), array('a', 'b', 'c'), null, null), + 33586 => array(array('_route' => '_591'), array('a', 'b', 'c'), null, null), + 33634 => array(array('_route' => '_599'), array('a', 'b', 'c'), null, null), + 33687 => array(array('_route' => '_787'), array('a', 'b', 'c'), null, null), + 33734 => array(array('_route' => '_848'), array('a', 'b', 'c'), null, null), + 33787 => array(array('_route' => '_796'), array('a', 'b', 'c'), null, null), + 33835 => array(array('_route' => '_877'), array('a', 'b', 'c'), null, null), + 33885 => array(array('_route' => '_809'), array('a', 'b', 'c'), null, null), + 33934 => array(array('_route' => '_817'), array('a', 'b', 'c'), null, null), + 33986 => array(array('_route' => '_819'), array('a', 'b', 'c'), null, null), + 34034 => array(array('_route' => '_865'), array('a', 'b', 'c'), null, null), + 34084 => array(array('_route' => '_919'), array('a', 'b', 'c'), null, null), + 34133 => array(array('_route' => '_949'), array('a', 'b', 'c'), null, null), + 34191 => array(array('_route' => '_510'), array('a', 'b', 'c'), null, null), + 34239 => array(array('_route' => '_590'), array('a', 'b', 'c'), null, null), + 34287 => array(array('_route' => '_597'), array('a', 'b', 'c'), null, null), + 34335 => array(array('_route' => '_682'), array('a', 'b', 'c'), null, null), + 34383 => array(array('_route' => '_723'), array('a', 'b', 'c'), null, null), + 34436 => array(array('_route' => '_521'), array('a', 'b', 'c'), null, null), + 34484 => array(array('_route' => '_594'), array('a', 'b', 'c'), null, null), + 34532 => array(array('_route' => '_689'), array('a', 'b', 'c'), null, null), + 34580 => array(array('_route' => '_713'), array('a', 'b', 'c'), null, null), + 34628 => array(array('_route' => '_889'), array('a', 'b', 'c'), null, null), + 34681 => array(array('_route' => '_531'), array('a', 'b', 'c'), null, null), + 34729 => array(array('_route' => '_639'), array('a', 'b', 'c'), null, null), + 34780 => array(array('_route' => '_646'), array('a', 'b', 'c'), null, null), + 34827 => array(array('_route' => '_659'), array('a', 'b', 'c'), null, null), + 34876 => array(array('_route' => '_959'), array('a', 'b', 'c'), null, null), + 34929 => array(array('_route' => '_550'), array('a', 'b', 'c'), null, null), + 34977 => array(array('_route' => '_833'), array('a', 'b', 'c'), null, null), + 35025 => array(array('_route' => '_899'), array('a', 'b', 'c'), null, null), + 35081 => array(array('_route' => '_580'), array('a', 'b', 'c'), null, null), + 35128 => array(array('_route' => '_762'), array('a', 'b', 'c'), null, null), + 35177 => array(array('_route' => '_896'), array('a', 'b', 'c'), null, null), + 35230 => array(array('_route' => '_595'), array('a', 'b', 'c'), null, null), + 35278 => array(array('_route' => '_933'), array('a', 'b', 'c'), null, null), + 35328 => array(array('_route' => '_610'), array('a', 'b', 'c'), null, null), + 35380 => array(array('_route' => '_629'), array('a', 'b', 'c'), null, null), + 35428 => array(array('_route' => '_744'), array('a', 'b', 'c'), null, null), + 35481 => array(array('_route' => '_674'), array('a', 'b', 'c'), null, null), + 35529 => array(array('_route' => '_726'), array('a', 'b', 'c'), null, null), + 35577 => array(array('_route' => '_929'), array('a', 'b', 'c'), null, null), + 35627 => array(array('_route' => '_696'), array('a', 'b', 'c'), null, null), + 35679 => array(array('_route' => '_841'), array('a', 'b', 'c'), null, null), + 35727 => array(array('_route' => '_890'), array('a', 'b', 'c'), null, null), + 35777 => array(array('_route' => '_885'), array('a', 'b', 'c'), null, null), + 35826 => array(array('_route' => '_888'), array('a', 'b', 'c'), null, null), + 35875 => array(array('_route' => '_996'), array('a', 'b', 'c'), null, null), + 35933 => array(array('_route' => '_511'), array('a', 'b', 'c'), null, null), + 35981 => array(array('_route' => '_576'), array('a', 'b', 'c'), null, null), + 36029 => array(array('_route' => '_623'), array('a', 'b', 'c'), null, null), + 36082 => array(array('_route' => '_560'), array('a', 'b', 'c'), null, null), + 36129 => array(array('_route' => '_585'), array('a', 'b', 'c'), null, null), + 36182 => array(array('_route' => '_570'), array('a', 'b', 'c'), null, null), + 36230 => array(array('_route' => '_578'), array('a', 'b', 'c'), null, null), + 36281 => array(array('_route' => '_780'), array('a', 'b', 'c'), null, null), + 36328 => array(array('_route' => '_808'), array('a', 'b', 'c'), null, null), + 36382 => array(array('_route' => '_593'), array('a', 'b', 'c'), null, null), + 36430 => array(array('_route' => '_900'), array('a', 'b', 'c'), null, null), + 36483 => array(array('_route' => '_632'), array('a', 'b', 'c'), null, null), + 36531 => array(array('_route' => '_654'), array('a', 'b', 'c'), null, null), + 36579 => array(array('_route' => '_721'), array('a', 'b', 'c'), null, null), + 36627 => array(array('_route' => '_836'), array('a', 'b', 'c'), null, null), + 36680 => array(array('_route' => '_637'), array('a', 'b', 'c'), null, null), + 36728 => array(array('_route' => '_737'), array('a', 'b', 'c'), null, null), + 36784 => array(array('_route' => '_699'), array('a', 'b', 'c'), null, null), + 36831 => array(array('_route' => '_822'), array('a', 'b', 'c'), null, null), + 36880 => array(array('_route' => '_853'), array('a', 'b', 'c'), null, null), + 36933 => array(array('_route' => '_708'), array('a', 'b', 'c'), null, null), + 36981 => array(array('_route' => '_871'), array('a', 'b', 'c'), null, null), + 37034 => array(array('_route' => '_752'), array('a', 'b', 'c'), null, null), + 37082 => array(array('_route' => '_989'), array('a', 'b', 'c'), null, null), + 37132 => array(array('_route' => '_855'), array('a', 'b', 'c'), null, null), + 37184 => array(array('_route' => '_858'), array('a', 'b', 'c'), null, null), + 37232 => array(array('_route' => '_898'), array('a', 'b', 'c'), null, null), + 37282 => array(array('_route' => '_903'), array('a', 'b', 'c'), null, null), + 37331 => array(array('_route' => '_909'), array('a', 'b', 'c'), null, null), + 37380 => array(array('_route' => '_950'), array('a', 'b', 'c'), null, null), + 37441 => array(array('_route' => '_512'), array('a', 'b', 'c'), null, null), + 37488 => array(array('_route' => '_691'), array('a', 'b', 'c'), null, null), + 37537 => array(array('_route' => '_686'), array('a', 'b', 'c'), null, null), + 37587 => array(array('_route' => '_527'), array('a', 'b', 'c'), null, null), + 37639 => array(array('_route' => '_541'), array('a', 'b', 'c'), null, null), + 37687 => array(array('_route' => '_956'), array('a', 'b', 'c'), null, null), + 37740 => array(array('_route' => '_555'), array('a', 'b', 'c'), null, null), + 37788 => array(array('_route' => '_681'), array('a', 'b', 'c'), null, null), + 37841 => array(array('_route' => '_556'), array('a', 'b', 'c'), null, null), + 37889 => array(array('_route' => '_802'), array('a', 'b', 'c'), null, null), + 37939 => array(array('_route' => '_558'), array('a', 'b', 'c'), null, null), + 37991 => array(array('_route' => '_564'), array('a', 'b', 'c'), null, null), + 38039 => array(array('_route' => '_670'), array('a', 'b', 'c'), null, null), + 38087 => array(array('_route' => '_884'), array('a', 'b', 'c'), null, null), + 38140 => array(array('_route' => '_627'), array('a', 'b', 'c'), null, null), + 38187 => array(array('_route' => '_746'), array('a', 'b', 'c'), null, null), + 38240 => array(array('_route' => '_668'), array('a', 'b', 'c'), null, null), + 38291 => array(array('_route' => '_712'), array('a', 'b', 'c'), null, null), + 38338 => array(array('_route' => '_863'), array('a', 'b', 'c'), null, null), + 38387 => array(array('_route' => '_801'), array('a', 'b', 'c'), null, null), + 38440 => array(array('_route' => '_709'), array('a', 'b', 'c'), null, null), + 38488 => array(array('_route' => '_850'), array('a', 'b', 'c'), null, null), + 38536 => array(array('_route' => '_918'), array('a', 'b', 'c'), null, null), + 38586 => array(array('_route' => '_803'), array('a', 'b', 'c'), null, null), + 38638 => array(array('_route' => '_864'), array('a', 'b', 'c'), null, null), + 38686 => array(array('_route' => '_880'), array('a', 'b', 'c'), null, null), + 38734 => array(array('_route' => '_927'), array('a', 'b', 'c'), null, null), + 38787 => array(array('_route' => '_930'), array('a', 'b', 'c'), null, null), + 38835 => array(array('_route' => '_951'), array('a', 'b', 'c'), null, null), + 38883 => array(array('_route' => '_963'), array('a', 'b', 'c'), null, null), + 38942 => array(array('_route' => '_519'), array('a', 'b', 'c'), null, null), + 38990 => array(array('_route' => '_823'), array('a', 'b', 'c'), null, null), + 39038 => array(array('_route' => '_954'), array('a', 'b', 'c'), null, null), + 39091 => array(array('_route' => '_525'), array('a', 'b', 'c'), null, null), + 39139 => array(array('_route' => '_991'), array('a', 'b', 'c'), null, null), + 39189 => array(array('_route' => '_536'), array('a', 'b', 'c'), null, null), + 39241 => array(array('_route' => '_545'), array('a', 'b', 'c'), null, null), + 39289 => array(array('_route' => '_944'), array('a', 'b', 'c'), null, null), + 39342 => array(array('_route' => '_557'), array('a', 'b', 'c'), null, null), + 39390 => array(array('_route' => '_783'), array('a', 'b', 'c'), null, null), + 39438 => array(array('_route' => '_807'), array('a', 'b', 'c'), null, null), + 39491 => array(array('_route' => '_586'), array('a', 'b', 'c'), null, null), + 39539 => array(array('_route' => '_711'), array('a', 'b', 'c'), null, null), + 39592 => array(array('_route' => '_598'), array('a', 'b', 'c'), null, null), + 39640 => array(array('_route' => '_635'), array('a', 'b', 'c'), null, null), + 39688 => array(array('_route' => '_983'), array('a', 'b', 'c'), null, null), + 39741 => array(array('_route' => '_634'), array('a', 'b', 'c'), null, null), + 39789 => array(array('_route' => '_641'), array('a', 'b', 'c'), null, null), + 39840 => array(array('_route' => '_779'), array('a', 'b', 'c'), null, null), + 39887 => array(array('_route' => '_876'), array('a', 'b', 'c'), null, null), + 39936 => array(array('_route' => '_811'), array('a', 'b', 'c'), null, null), + 39984 => array(array('_route' => '_824'), array('a', 'b', 'c'), null, null), + 40037 => array(array('_route' => '_660'), array('a', 'b', 'c'), null, null), + 40085 => array(array('_route' => '_789'), array('a', 'b', 'c'), null, null), + 40138 => array(array('_route' => '_733'), array('a', 'b', 'c'), null, null), + 40186 => array(array('_route' => '_735'), array('a', 'b', 'c'), null, null), + 40234 => array(array('_route' => '_882'), array('a', 'b', 'c'), null, null), + 40282 => array(array('_route' => '_967'), array('a', 'b', 'c'), null, null), + 40332 => array(array('_route' => '_736'), array('a', 'b', 'c'), null, null), + 40381 => array(array('_route' => '_753'), array('a', 'b', 'c'), null, null), + 40430 => array(array('_route' => '_786'), array('a', 'b', 'c'), null, null), + 40479 => array(array('_route' => '_907'), array('a', 'b', 'c'), null, null), + 40528 => array(array('_route' => '_920'), array('a', 'b', 'c'), null, null), + 40577 => array(array('_route' => '_971'), array('a', 'b', 'c'), null, null), + 40635 => array(array('_route' => '_520'), array('a', 'b', 'c'), null, null), + 40683 => array(array('_route' => '_891'), array('a', 'b', 'c'), null, null), + 40739 => array(array('_route' => '_534'), array('a', 'b', 'c'), null, null), + 40785 => array(array('_route' => '_602'), array('a', 'b', 'c'), null, null), + 40834 => array(array('_route' => '_605'), array('a', 'b', 'c'), null, null), + 40882 => array(array('_route' => '_979'), array('a', 'b', 'c'), null, null), + 40932 => array(array('_route' => '_547'), array('a', 'b', 'c'), null, null), + 40987 => array(array('_route' => '_549'), array('a', 'b', 'c'), null, null), + 41034 => array(array('_route' => '_755'), array('a', 'b', 'c'), null, null), + 41083 => array(array('_route' => '_922'), array('a', 'b', 'c'), null, null), + 41131 => array(array('_route' => '_977'), array('a', 'b', 'c'), null, null), + 41184 => array(array('_route' => '_565'), array('a', 'b', 'c'), null, null), + 41232 => array(array('_route' => '_926'), array('a', 'b', 'c'), null, null), + 41282 => array(array('_route' => '_571'), array('a', 'b', 'c'), null, null), + 41331 => array(array('_route' => '_581'), array('a', 'b', 'c'), null, null), + 41380 => array(array('_route' => '_619'), array('a', 'b', 'c'), null, null), + 41429 => array(array('_route' => '_636'), array('a', 'b', 'c'), null, null), + 41481 => array(array('_route' => '_679'), array('a', 'b', 'c'), null, null), + 41529 => array(array('_route' => '_866'), array('a', 'b', 'c'), null, null), + 41577 => array(array('_route' => '_973'), array('a', 'b', 'c'), null, null), + 41630 => array(array('_route' => '_690'), array('a', 'b', 'c'), null, null), + 41678 => array(array('_route' => '_775'), array('a', 'b', 'c'), null, null), + 41731 => array(array('_route' => '_722'), array('a', 'b', 'c'), null, null), + 41779 => array(array('_route' => '_906'), array('a', 'b', 'c'), null, null), + 41827 => array(array('_route' => '_946'), array('a', 'b', 'c'), null, null), + 41877 => array(array('_route' => '_788'), array('a', 'b', 'c'), null, null), + 41929 => array(array('_route' => '_828'), array('a', 'b', 'c'), null, null), + 41977 => array(array('_route' => '_892'), array('a', 'b', 'c'), null, null), + 42025 => array(array('_route' => '_972'), array('a', 'b', 'c'), null, null), + 42075 => array(array('_route' => '_829'), array('a', 'b', 'c'), null, null), + 42127 => array(array('_route' => '_923'), array('a', 'b', 'c'), null, null), + 42175 => array(array('_route' => '_947'), array('a', 'b', 'c'), null, null), + 42234 => array(array('_route' => '_526'), array('a', 'b', 'c'), null, null), + 42282 => array(array('_route' => '_614'), array('a', 'b', 'c'), null, null), + 42330 => array(array('_route' => '_621'), array('a', 'b', 'c'), null, null), + 42383 => array(array('_route' => '_543'), array('a', 'b', 'c'), null, null), + 42431 => array(array('_route' => '_812'), array('a', 'b', 'c'), null, null), + 42487 => array(array('_route' => '_548'), array('a', 'b', 'c'), null, null), + 42534 => array(array('_route' => '_747'), array('a', 'b', 'c'), null, null), + 42583 => array(array('_route' => '_715'), array('a', 'b', 'c'), null, null), + 42631 => array(array('_route' => '_940'), array('a', 'b', 'c'), null, null), + 42684 => array(array('_route' => '_563'), array('a', 'b', 'c'), null, null), + 42732 => array(array('_route' => '_611'), array('a', 'b', 'c'), null, null), + 42780 => array(array('_route' => '_830'), array('a', 'b', 'c'), null, null), + 42833 => array(array('_route' => '_569'), array('a', 'b', 'c'), null, null), + 42881 => array(array('_route' => '_908'), array('a', 'b', 'c'), null, null), + 42929 => array(array('_route' => '_913'), array('a', 'b', 'c'), null, null), + 42982 => array(array('_route' => '_644'), array('a', 'b', 'c'), null, null), + 43030 => array(array('_route' => '_776'), array('a', 'b', 'c'), null, null), + 43078 => array(array('_route' => '_856'), array('a', 'b', 'c'), null, null), + 43131 => array(array('_route' => '_650'), array('a', 'b', 'c'), null, null), + 43179 => array(array('_route' => '_761'), array('a', 'b', 'c'), null, null), + 43232 => array(array('_route' => '_663'), array('a', 'b', 'c'), null, null), + 43280 => array(array('_route' => '_754'), array('a', 'b', 'c'), null, null), + 43333 => array(array('_route' => '_665'), array('a', 'b', 'c'), null, null), + 43381 => array(array('_route' => '_805'), array('a', 'b', 'c'), null, null), + 43429 => array(array('_route' => '_846'), array('a', 'b', 'c'), null, null), + 43477 => array(array('_route' => '_857'), array('a', 'b', 'c'), null, null), + 43530 => array(array('_route' => '_675'), array('a', 'b', 'c'), null, null), + 43578 => array(array('_route' => '_839'), array('a', 'b', 'c'), null, null), + 43626 => array(array('_route' => '_968'), array('a', 'b', 'c'), null, null), + 43676 => array(array('_route' => '_697'), array('a', 'b', 'c'), null, null), + 43728 => array(array('_route' => '_725'), array('a', 'b', 'c'), null, null), + 43776 => array(array('_route' => '_794'), array('a', 'b', 'c'), null, null), + 43829 => array(array('_route' => '_773'), array('a', 'b', 'c'), null, null), + 43877 => array(array('_route' => '_992'), array('a', 'b', 'c'), null, null), + 43930 => array(array('_route' => '_901'), array('a', 'b', 'c'), null, null), + 43978 => array(array('_route' => '_970'), array('a', 'b', 'c'), null, null), + 44028 => array(array('_route' => '_964'), array('a', 'b', 'c'), null, null), + 44086 => array(array('_route' => '_530'), array('a', 'b', 'c'), null, null), + 44134 => array(array('_route' => '_703'), array('a', 'b', 'c'), null, null), + 44187 => array(array('_route' => '_533'), array('a', 'b', 'c'), null, null), + 44235 => array(array('_route' => '_739'), array('a', 'b', 'c'), null, null), + 44283 => array(array('_route' => '_791'), array('a', 'b', 'c'), null, null), + 44331 => array(array('_route' => '_987'), array('a', 'b', 'c'), null, null), + 44384 => array(array('_route' => '_566'), array('a', 'b', 'c'), null, null), + 44432 => array(array('_route' => '_592'), array('a', 'b', 'c'), null, null), + 44488 => array(array('_route' => '_568'), array('a', 'b', 'c'), null, null), + 44534 => array(array('_route' => '_868'), array('a', 'b', 'c'), null, null), + 44583 => array(array('_route' => '_878'), array('a', 'b', 'c'), null, null), + 44636 => array(array('_route' => '_588'), array('a', 'b', 'c'), null, null), + 44684 => array(array('_route' => '_793'), array('a', 'b', 'c'), null, null), + 44732 => array(array('_route' => '_917'), array('a', 'b', 'c'), null, null), + 44785 => array(array('_route' => '_600'), array('a', 'b', 'c'), null, null), + 44833 => array(array('_route' => '_728'), array('a', 'b', 'c'), null, null), + 44886 => array(array('_route' => '_603'), array('a', 'b', 'c'), null, null), + 44934 => array(array('_route' => '_765'), array('a', 'b', 'c'), null, null), + 44987 => array(array('_route' => '_607'), array('a', 'b', 'c'), null, null), + 45035 => array(array('_route' => '_676'), array('a', 'b', 'c'), null, null), + 45083 => array(array('_route' => '_804'), array('a', 'b', 'c'), null, null), + 45136 => array(array('_route' => '_609'), array('a', 'b', 'c'), null, null), + 45184 => array(array('_route' => '_961'), array('a', 'b', 'c'), null, null), + 45232 => array(array('_route' => '_980'), array('a', 'b', 'c'), null, null), + 45282 => array(array('_route' => '_714'), array('a', 'b', 'c'), null, null), + 45334 => array(array('_route' => '_730'), array('a', 'b', 'c'), null, null), + 45382 => array(array('_route' => '_806'), array('a', 'b', 'c'), null, null), + 45430 => array(array('_route' => '_825'), array('a', 'b', 'c'), null, null), + 45478 => array(array('_route' => '_879'), array('a', 'b', 'c'), null, null), + 45526 => array(array('_route' => '_893'), array('a', 'b', 'c'), null, null), + 45576 => array(array('_route' => '_928'), array('a', 'b', 'c'), null, null), + 45628 => array(array('_route' => '_932'), array('a', 'b', 'c'), null, null), + 45676 => array(array('_route' => '_958'), array('a', 'b', 'c'), null, null), + 45726 => array(array('_route' => '_984'), array('a', 'b', 'c'), null, null), + 45784 => array(array('_route' => '_538'), array('a', 'b', 'c'), null, null), + 45832 => array(array('_route' => '_993'), array('a', 'b', 'c'), null, null), + 45882 => array(array('_route' => '_542'), array('a', 'b', 'c'), null, null), + 45934 => array(array('_route' => '_551'), array('a', 'b', 'c'), null, null), + 45982 => array(array('_route' => '_687'), array('a', 'b', 'c'), null, null), + 46030 => array(array('_route' => '_724'), array('a', 'b', 'c'), null, null), + 46078 => array(array('_route' => '_925'), array('a', 'b', 'c'), null, null), + 46131 => array(array('_route' => '_587'), array('a', 'b', 'c'), null, null), + 46179 => array(array('_route' => '_914'), array('a', 'b', 'c'), null, null), + 46229 => array(array('_route' => '_616'), array('a', 'b', 'c'), null, null), + 46284 => array(array('_route' => '_677'), array('a', 'b', 'c'), null, null), + 46331 => array(array('_route' => '_815'), array('a', 'b', 'c'), null, null), + 46380 => array(array('_route' => '_781'), array('a', 'b', 'c'), null, null), + 46430 => array(array('_route' => '_717'), array('a', 'b', 'c'), null, null), + 46482 => array(array('_route' => '_782'), array('a', 'b', 'c'), null, null), + 46530 => array(array('_route' => '_832'), array('a', 'b', 'c'), null, null), + 46583 => array(array('_route' => '_795'), array('a', 'b', 'c'), null, null), + 46631 => array(array('_route' => '_887'), array('a', 'b', 'c'), null, null), + 46681 => array(array('_route' => '_800'), array('a', 'b', 'c'), null, null), + 46730 => array(array('_route' => '_826'), array('a', 'b', 'c'), null, null), + 46779 => array(array('_route' => '_881'), array('a', 'b', 'c'), null, null), + 46828 => array(array('_route' => '_886'), array('a', 'b', 'c'), null, null), + 46877 => array(array('_route' => '_938'), array('a', 'b', 'c'), null, null), + 46935 => array(array('_route' => '_540'), array('a', 'b', 'c'), null, null), + 46983 => array(array('_route' => '_643'), array('a', 'b', 'c'), null, null), + 47033 => array(array('_route' => '_544'), array('a', 'b', 'c'), null, null), + 47082 => array(array('_route' => '_552'), array('a', 'b', 'c'), null, null), + 47134 => array(array('_route' => '_567'), array('a', 'b', 'c'), null, null), + 47182 => array(array('_route' => '_608'), array('a', 'b', 'c'), null, null), + 47230 => array(array('_route' => '_698'), array('a', 'b', 'c'), null, null), + 47278 => array(array('_route' => '_988'), array('a', 'b', 'c'), null, null), + 47331 => array(array('_route' => '_583'), array('a', 'b', 'c'), null, null), + 47379 => array(array('_route' => '_998'), array('a', 'b', 'c'), null, null), + 47432 => array(array('_route' => '_604'), array('a', 'b', 'c'), null, null), + 47480 => array(array('_route' => '_630'), array('a', 'b', 'c'), null, null), + 47528 => array(array('_route' => '_706'), array('a', 'b', 'c'), null, null), + 47576 => array(array('_route' => '_976'), array('a', 'b', 'c'), null, null), + 47629 => array(array('_route' => '_673'), array('a', 'b', 'c'), null, null), + 47677 => array(array('_route' => '_678'), array('a', 'b', 'c'), null, null), + 47725 => array(array('_route' => '_931'), array('a', 'b', 'c'), null, null), + 47775 => array(array('_route' => '_751'), array('a', 'b', 'c'), null, null), + 47824 => array(array('_route' => '_766'), array('a', 'b', 'c'), null, null), + 47876 => array(array('_route' => '_792'), array('a', 'b', 'c'), null, null), + 47924 => array(array('_route' => '_814'), array('a', 'b', 'c'), null, null), + 47974 => array(array('_route' => '_798'), array('a', 'b', 'c'), null, null), + 48026 => array(array('_route' => '_851'), array('a', 'b', 'c'), null, null), + 48074 => array(array('_route' => '_941'), array('a', 'b', 'c'), null, null), + 48122 => array(array('_route' => '_953'), array('a', 'b', 'c'), null, null), + 48170 => array(array('_route' => '_975'), array('a', 'b', 'c'), null, null), + 48220 => array(array('_route' => '_873'), array('a', 'b', 'c'), null, null), + 48269 => array(array('_route' => '_936'), array('a', 'b', 'c'), null, null), + 48318 => array(array('_route' => '_994'), array('a', 'b', 'c'), null, null), + 48376 => array(array('_route' => '_562'), array('a', 'b', 'c'), null, null), + 48424 => array(array('_route' => '_770'), array('a', 'b', 'c'), null, null), + 48475 => array(array('_route' => '_774'), array('a', 'b', 'c'), null, null), + 48522 => array(array('_route' => '_966'), array('a', 'b', 'c'), null, null), + 48573 => array(array('_route' => '_582'), array('a', 'b', 'c'), null, null), + 48625 => array(array('_route' => '_606'), array('a', 'b', 'c'), null, null), + 48673 => array(array('_route' => '_648'), array('a', 'b', 'c'), null, null), + 48723 => array(array('_route' => '_624'), array('a', 'b', 'c'), null, null), + 48775 => array(array('_route' => '_626'), array('a', 'b', 'c'), null, null), + 48823 => array(array('_route' => '_821'), array('a', 'b', 'c'), null, null), + 48873 => array(array('_route' => '_628'), array('a', 'b', 'c'), null, null), + 48922 => array(array('_route' => '_638'), array('a', 'b', 'c'), null, null), + 48974 => array(array('_route' => '_640'), array('a', 'b', 'c'), null, null), + 49022 => array(array('_route' => '_990'), array('a', 'b', 'c'), null, null), + 49072 => array(array('_route' => '_705'), array('a', 'b', 'c'), null, null), + 49121 => array(array('_route' => '_757'), array('a', 'b', 'c'), null, null), + 49176 => array(array('_route' => '_785'), array('a', 'b', 'c'), null, null), + 49223 => array(array('_route' => '_875'), array('a', 'b', 'c'), null, null), + 49270 => array(array('_route' => '_894'), array('a', 'b', 'c'), null, null), + 49319 => array(array('_route' => '_945'), array('a', 'b', 'c'), null, null), + 49375 => array(array('_route' => '_816'), array('a', 'b', 'c'), null, null), + 49422 => array(array('_route' => '_872'), array('a', 'b', 'c'), null, null), + 49471 => array(array('_route' => '_921'), array('a', 'b', 'c'), null, null), + 49519 => array(array('_route' => '_960'), array('a', 'b', 'c'), null, null), + 49567 => array(array('_route' => '_974'), array('a', 'b', 'c'), null, null), + 49620 => array(array('_route' => '_835'), array('a', 'b', 'c'), null, null), + 49668 => array(array('_route' => '_934'), array('a', 'b', 'c'), null, null), + 49718 => array(array('_route' => '_869'), array('a', 'b', 'c'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (49718 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher11.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher11.php new file mode 100644 index 0000000..173cc15 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher11.php @@ -0,0 +1,151 @@ +context = $context; + } + + public function match($pathinfo) + { + $allow = $allowSchemes = array(); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $ret; + } + if ($allow) { + throw new MethodNotAllowedException(array_keys($allow)); + } + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) { + // no-op + } elseif ($allowSchemes) { + redirect_scheme: + $scheme = $this->context->getScheme(); + $this->context->setScheme(key($allowSchemes)); + try { + if ($ret = $this->doMatch($pathinfo)) { + return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret; + } + } finally { + $this->context->setScheme($scheme); + } + } elseif ('/' !== $pathinfo) { + $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $this->redirect($pathinfo, $ret['_route']) + $ret; + } + if ($allowSchemes) { + goto redirect_scheme; + } + } + + throw new ResourceNotFoundException(); + } + + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/(en|fr)/(?' + .'|admin/post/(?' + .'|(*:33)' + .'|new(*:43)' + .'|(\\d+)(*:55)' + .'|(\\d+)/edit(*:72)' + .'|(\\d+)/delete(*:91)' + .')' + .'|blog/(?' + .'|(*:107)' + .'|rss\\.xml(*:123)' + .'|p(?' + .'|age/([^/]++)(*:147)' + .'|osts/([^/]++)(*:168)' + .')' + .'|comments/(\\d+)/new(*:195)' + .'|search(*:209)' + .')' + .'|log(?' + .'|in(*:226)' + .'|out(*:237)' + .')' + .')' + .'|/(en|fr)?(*:256)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 33 => array(array('_route' => 'a', '_locale' => 'en'), array('_locale'), null, null), + 43 => array(array('_route' => 'b', '_locale' => 'en'), array('_locale'), null, null), + 55 => array(array('_route' => 'c', '_locale' => 'en'), array('_locale', 'id'), null, null), + 72 => array(array('_route' => 'd', '_locale' => 'en'), array('_locale', 'id'), null, null), + 91 => array(array('_route' => 'e', '_locale' => 'en'), array('_locale', 'id'), null, null), + 107 => array(array('_route' => 'f', '_locale' => 'en'), array('_locale'), null, null), + 123 => array(array('_route' => 'g', '_locale' => 'en'), array('_locale'), null, null), + 147 => array(array('_route' => 'h', '_locale' => 'en'), array('_locale', 'page'), null, null), + 168 => array(array('_route' => 'i', '_locale' => 'en'), array('_locale', 'page'), null, null), + 195 => array(array('_route' => 'j', '_locale' => 'en'), array('_locale', 'id'), null, null), + 209 => array(array('_route' => 'k', '_locale' => 'en'), array('_locale'), null, null), + 226 => array(array('_route' => 'l', '_locale' => 'en'), array('_locale'), null, null), + 237 => array(array('_route' => 'm', '_locale' => 'en'), array('_locale'), null, null), + 256 => array(array('_route' => 'n', '_locale' => 'en'), array('_locale'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (256 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + return null; + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher12.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher12.php new file mode 100644 index 0000000..eba4c8a --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher12.php @@ -0,0 +1,100 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/abc([^/]++)/(?' + .'|1(?' + .'|(*:27)' + .'|0(?' + .'|(*:38)' + .'|0(*:46)' + .')' + .')' + .'|2(?' + .'|(*:59)' + .'|0(?' + .'|(*:70)' + .'|0(*:78)' + .')' + .')' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 27 => array(array('_route' => 'r1'), array('foo'), null, null), + 38 => array(array('_route' => 'r10'), array('foo'), null, null), + 46 => array(array('_route' => 'r100'), array('foo'), null, null), + 59 => array(array('_route' => 'r2'), array('foo'), null, null), + 70 => array(array('_route' => 'r20'), array('foo'), null, null), + 78 => array(array('_route' => 'r200'), array('foo'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (78 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher13.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher13.php new file mode 100644 index 0000000..5cda775 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher13.php @@ -0,0 +1,69 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + $matchedPathinfo = $host.'.'.$pathinfo; + $regexList = array( + 0 => '{^(?' + .'|(?i:([^\\.]++)\\.exampple\\.com)\\.(?' + .'|/abc([^/]++)(?' + .'|(*:56)' + .')' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + case 56: + $matches = array('foo' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); + + // r1 + return $this->mergeDefaults(array('_route' => 'r1') + $matches, array()); + + // r2 + return $this->mergeDefaults(array('_route' => 'r2') + $matches, array()); + + break; + } + + if (56 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php new file mode 100644 index 0000000..5aec5db --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher2.php @@ -0,0 +1,284 @@ +context = $context; + } + + public function match($pathinfo) + { + $allow = $allowSchemes = array(); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $ret; + } + if ($allow) { + throw new MethodNotAllowedException(array_keys($allow)); + } + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) { + // no-op + } elseif ($allowSchemes) { + redirect_scheme: + $scheme = $this->context->getScheme(); + $this->context->setScheme(key($allowSchemes)); + try { + if ($ret = $this->doMatch($pathinfo)) { + return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret; + } + } finally { + $this->context->setScheme($scheme); + } + } elseif ('/' !== $pathinfo) { + $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $this->redirect($pathinfo, $ret['_route']) + $ret; + } + if ($allowSchemes) { + goto redirect_scheme; + } + } + + throw new ResourceNotFoundException(); + } + + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + default: + $routes = array( + '/test/baz' => array(array('_route' => 'baz'), null, null, null), + '/test/baz.html' => array(array('_route' => 'baz2'), null, null, null), + '/test/baz3/' => array(array('_route' => 'baz3'), null, null, null), + '/foofoo' => array(array('_route' => 'foofoo', 'def' => 'test'), null, null, null), + '/spa ce' => array(array('_route' => 'space'), null, null, null), + '/multi/new' => array(array('_route' => 'overridden2'), null, null, null), + '/multi/hey/' => array(array('_route' => 'hey'), null, null, null), + '/ababa' => array(array('_route' => 'ababa'), null, null, null), + '/route1' => array(array('_route' => 'route1'), 'a.example.com', null, null), + '/c2/route2' => array(array('_route' => 'route2'), 'a.example.com', null, null), + '/route4' => array(array('_route' => 'route4'), 'a.example.com', null, null), + '/c2/route3' => array(array('_route' => 'route3'), 'b.example.com', null, null), + '/route5' => array(array('_route' => 'route5'), 'c.example.com', null, null), + '/route6' => array(array('_route' => 'route6'), null, null, null), + '/route11' => array(array('_route' => 'route11'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route12' => array(array('_route' => 'route12', 'var1' => 'val'), '#^(?P[^\\.]++)\\.example\\.com$#sDi', null, null), + '/route17' => array(array('_route' => 'route17'), null, null, null), + '/secure' => array(array('_route' => 'secure'), null, null, array('https' => 0)), + '/nonsecure' => array(array('_route' => 'nonsecure'), null, null, array('http' => 0)), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + if ($requiredHost) { + if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) { + break; + } + if ('#' === $requiredHost[0] && $hostMatches) { + $hostMatches['_route'] = $ret['_route']; + $ret = $this->mergeDefaults($hostMatches, $ret); + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + $matchedPathinfo = $host.'.'.$pathinfo; + $regexList = array( + 0 => '{^(?' + .'|(?:(?:[^./]*+\\.)++)(?' + .'|/foo/(baz|symfony)(*:47)' + .'|/bar(?' + .'|/([^/]++)(*:70)' + .'|head/([^/]++)(*:90)' + .')' + .'|/test/([^/]++)/(?' + .'|(*:116)' + .')' + .'|/([\']+)(*:132)' + .'|/a/(?' + .'|b\'b/([^/]++)(?' + .'|(*:161)' + .'|(*:169)' + .')' + .'|(.*)(*:182)' + .'|b\'b/([^/]++)(?' + .'|(*:205)' + .'|(*:213)' + .')' + .')' + .'|/multi/hello(?:/([^/]++))?(*:249)' + .'|/([^/]++)/b/([^/]++)(?' + .'|(*:280)' + .'|(*:288)' + .')' + .'|/aba/([^/]++)(*:310)' + .')|(?i:([^\\.]++)\\.example\\.com)\\.(?' + .'|/route1(?' + .'|3/([^/]++)(*:372)' + .'|4/([^/]++)(*:390)' + .')' + .')|(?i:c\\.example\\.com)\\.(?' + .'|/route15/([^/]++)(*:442)' + .')|(?:(?:[^./]*+\\.)++)(?' + .'|/route16/([^/]++)(*:490)' + .'|/a/(?' + .'|a\\.\\.\\.(*:511)' + .'|b/(?' + .'|([^/]++)(*:532)' + .'|c/([^/]++)(*:550)' + .')' + .')' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + case 116: + $matches = array('foo' => $matches[1] ?? null); + + // baz4 + return $this->mergeDefaults(array('_route' => 'baz4') + $matches, array()); + + // baz5 + $ret = $this->mergeDefaults(array('_route' => 'baz5') + $matches, array()); + if (!isset(($a = array('POST' => 0))[$requestMethod])) { + $allow += $a; + goto not_baz5; + } + + return $ret; + not_baz5: + + // baz.baz6 + $ret = $this->mergeDefaults(array('_route' => 'baz.baz6') + $matches, array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_bazbaz6; + } + + return $ret; + not_bazbaz6: + + break; + case 161: + $matches = array('foo' => $matches[1] ?? null); + + // foo1 + $ret = $this->mergeDefaults(array('_route' => 'foo1') + $matches, array()); + if (!isset(($a = array('PUT' => 0))[$requestMethod])) { + $allow += $a; + goto not_foo1; + } + + return $ret; + not_foo1: + + break; + case 205: + $matches = array('foo1' => $matches[1] ?? null); + + // foo2 + return $this->mergeDefaults(array('_route' => 'foo2') + $matches, array()); + + break; + case 280: + $matches = array('_locale' => $matches[1] ?? null, 'foo' => $matches[2] ?? null); + + // foo3 + return $this->mergeDefaults(array('_route' => 'foo3') + $matches, array()); + + break; + default: + $routes = array( + 47 => array(array('_route' => 'foo', 'def' => 'test'), array('bar'), null, null), + 70 => array(array('_route' => 'bar'), array('foo'), array('GET' => 0, 'HEAD' => 1), null), + 90 => array(array('_route' => 'barhead'), array('foo'), array('GET' => 0), null), + 132 => array(array('_route' => 'quoter'), array('quoter'), null, null), + 169 => array(array('_route' => 'bar1'), array('bar'), null, null), + 182 => array(array('_route' => 'overridden'), array('var'), null, null), + 213 => array(array('_route' => 'bar2'), array('bar1'), null, null), + 249 => array(array('_route' => 'helloWorld', 'who' => 'World!'), array('who'), null, null), + 288 => array(array('_route' => 'bar3'), array('_locale', 'bar'), null, null), + 310 => array(array('_route' => 'foo4'), array('foo'), null, null), + 372 => array(array('_route' => 'route13'), array('var1', 'name'), null, null), + 390 => array(array('_route' => 'route14', 'var1' => 'val'), array('var1', 'name'), null, null), + 442 => array(array('_route' => 'route15'), array('name'), null, null), + 490 => array(array('_route' => 'route16', 'var1' => 'val'), array('name'), null, null), + 511 => array(array('_route' => 'a'), array(), null, null), + 532 => array(array('_route' => 'b'), array('var'), null, null), + 550 => array(array('_route' => 'c'), array('var'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (550 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + return null; + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php new file mode 100644 index 0000000..6f1c45a --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher3.php @@ -0,0 +1,112 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + case '/with-condition': + // with-condition + if (($context->getMethod() == "GET")) { + return array('_route' => 'with-condition'); + } + break; + default: + $routes = array( + '/rootprefix/test' => array(array('_route' => 'static'), null, null, null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/rootprefix/([^/]++)(*:27)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 27 => array(array('_route' => 'dynamic'), array('var'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (27 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php new file mode 100644 index 0000000..418f8e0 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher4.php @@ -0,0 +1,84 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + case '/put_and_post': + // put_and_post + $ret = array('_route' => 'put_and_post'); + if (!isset(($a = array('PUT' => 0, 'POST' => 1))[$requestMethod])) { + $allow += $a; + goto not_put_and_post; + } + + return $ret; + not_put_and_post: + // put_and_get_and_head + $ret = array('_route' => 'put_and_get_and_head'); + if (!isset(($a = array('PUT' => 0, 'GET' => 1, 'HEAD' => 2))[$canonicalMethod])) { + $allow += $a; + goto not_put_and_get_and_head; + } + + return $ret; + not_put_and_get_and_head: + break; + default: + $routes = array( + '/just_head' => array(array('_route' => 'just_head'), null, array('HEAD' => 0), null), + '/head_and_get' => array(array('_route' => 'head_and_get'), null, array('HEAD' => 0, 'GET' => 1), null), + '/get_and_head' => array(array('_route' => 'get_and_head'), null, array('GET' => 0, 'HEAD' => 1), null), + '/post_and_head' => array(array('_route' => 'post_and_head'), null, array('POST' => 0, 'HEAD' => 1), null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php new file mode 100644 index 0000000..976a25f --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher5.php @@ -0,0 +1,154 @@ +context = $context; + } + + public function match($pathinfo) + { + $allow = $allowSchemes = array(); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $ret; + } + if ($allow) { + throw new MethodNotAllowedException(array_keys($allow)); + } + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) { + // no-op + } elseif ($allowSchemes) { + redirect_scheme: + $scheme = $this->context->getScheme(); + $this->context->setScheme(key($allowSchemes)); + try { + if ($ret = $this->doMatch($pathinfo)) { + return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret; + } + } finally { + $this->context->setScheme($scheme); + } + } elseif ('/' !== $pathinfo) { + $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $this->redirect($pathinfo, $ret['_route']) + $ret; + } + if ($allowSchemes) { + goto redirect_scheme; + } + } + + throw new ResourceNotFoundException(); + } + + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + default: + $routes = array( + '/a/11' => array(array('_route' => 'a_first'), null, null, null), + '/a/22' => array(array('_route' => 'a_second'), null, null, null), + '/a/333' => array(array('_route' => 'a_third'), null, null, null), + '/a/44/' => array(array('_route' => 'a_fourth'), null, null, null), + '/a/55/' => array(array('_route' => 'a_fifth'), null, null, null), + '/a/66/' => array(array('_route' => 'a_sixth'), null, null, null), + '/nested/group/a/' => array(array('_route' => 'nested_a'), null, null, null), + '/nested/group/b/' => array(array('_route' => 'nested_b'), null, null, null), + '/nested/group/c/' => array(array('_route' => 'nested_c'), null, null, null), + '/slashed/group/' => array(array('_route' => 'slashed_a'), null, null, null), + '/slashed/group/b/' => array(array('_route' => 'slashed_b'), null, null, null), + '/slashed/group/c/' => array(array('_route' => 'slashed_c'), null, null, null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/([^/]++)(*:16)' + .'|/nested/([^/]++)(*:39)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 16 => array(array('_route' => 'a_wildcard'), array('param'), null, null), + 39 => array(array('_route' => 'nested_wildcard'), array('param'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (39 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + return null; + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php new file mode 100644 index 0000000..9cebf2d --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher6.php @@ -0,0 +1,131 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + default: + $routes = array( + '/trailing/simple/no-methods/' => array(array('_route' => 'simple_trailing_slash_no_methods'), null, null, null), + '/trailing/simple/get-method/' => array(array('_route' => 'simple_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/trailing/simple/head-method/' => array(array('_route' => 'simple_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/trailing/simple/post-method/' => array(array('_route' => 'simple_trailing_slash_POST_method'), null, array('POST' => 0), null), + '/not-trailing/simple/no-methods' => array(array('_route' => 'simple_not_trailing_slash_no_methods'), null, null, null), + '/not-trailing/simple/get-method' => array(array('_route' => 'simple_not_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/not-trailing/simple/head-method' => array(array('_route' => 'simple_not_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/not-trailing/simple/post-method' => array(array('_route' => 'simple_not_trailing_slash_POST_method'), null, array('POST' => 0), null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/trailing/regex/(?' + .'|no\\-methods/([^/]++)/(*:47)' + .'|get\\-method/([^/]++)/(*:75)' + .'|head\\-method/([^/]++)/(*:104)' + .'|post\\-method/([^/]++)/(*:134)' + .')' + .'|/not\\-trailing/regex/(?' + .'|no\\-methods/([^/]++)(*:187)' + .'|get\\-method/([^/]++)(*:215)' + .'|head\\-method/([^/]++)(*:244)' + .'|post\\-method/([^/]++)(*:273)' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 47 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null), + 75 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 104 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 134 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + 187 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null), + 215 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 244 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 273 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (273 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php new file mode 100644 index 0000000..b2b7072 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher7.php @@ -0,0 +1,166 @@ +context = $context; + } + + public function match($pathinfo) + { + $allow = $allowSchemes = array(); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $ret; + } + if ($allow) { + throw new MethodNotAllowedException(array_keys($allow)); + } + if (!in_array($this->context->getMethod(), array('HEAD', 'GET'), true)) { + // no-op + } elseif ($allowSchemes) { + redirect_scheme: + $scheme = $this->context->getScheme(); + $this->context->setScheme(key($allowSchemes)); + try { + if ($ret = $this->doMatch($pathinfo)) { + return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret; + } + } finally { + $this->context->setScheme($scheme); + } + } elseif ('/' !== $pathinfo) { + $pathinfo = '/' !== $pathinfo[-1] ? $pathinfo.'/' : substr($pathinfo, 0, -1); + if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { + return $this->redirect($pathinfo, $ret['_route']) + $ret; + } + if ($allowSchemes) { + goto redirect_scheme; + } + } + + throw new ResourceNotFoundException(); + } + + private function doMatch(string $rawPathinfo, array &$allow = array(), array &$allowSchemes = array()): ?array + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + default: + $routes = array( + '/trailing/simple/no-methods/' => array(array('_route' => 'simple_trailing_slash_no_methods'), null, null, null), + '/trailing/simple/get-method/' => array(array('_route' => 'simple_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/trailing/simple/head-method/' => array(array('_route' => 'simple_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/trailing/simple/post-method/' => array(array('_route' => 'simple_trailing_slash_POST_method'), null, array('POST' => 0), null), + '/not-trailing/simple/no-methods' => array(array('_route' => 'simple_not_trailing_slash_no_methods'), null, null, null), + '/not-trailing/simple/get-method' => array(array('_route' => 'simple_not_trailing_slash_GET_method'), null, array('GET' => 0), null), + '/not-trailing/simple/head-method' => array(array('_route' => 'simple_not_trailing_slash_HEAD_method'), null, array('HEAD' => 0), null), + '/not-trailing/simple/post-method' => array(array('_route' => 'simple_not_trailing_slash_POST_method'), null, array('POST' => 0), null), + ); + + if (!isset($routes[$pathinfo])) { + break; + } + list($ret, $requiredHost, $requiredMethods, $requiredSchemes) = $routes[$pathinfo]; + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/trailing/regex/(?' + .'|no\\-methods/([^/]++)/(*:47)' + .'|get\\-method/([^/]++)/(*:75)' + .'|head\\-method/([^/]++)/(*:104)' + .'|post\\-method/([^/]++)/(*:134)' + .')' + .'|/not\\-trailing/regex/(?' + .'|no\\-methods/([^/]++)(*:187)' + .'|get\\-method/([^/]++)(*:215)' + .'|head\\-method/([^/]++)(*:244)' + .'|post\\-method/([^/]++)(*:273)' + .')' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 47 => array(array('_route' => 'regex_trailing_slash_no_methods'), array('param'), null, null), + 75 => array(array('_route' => 'regex_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 104 => array(array('_route' => 'regex_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 134 => array(array('_route' => 'regex_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + 187 => array(array('_route' => 'regex_not_trailing_slash_no_methods'), array('param'), null, null), + 215 => array(array('_route' => 'regex_not_trailing_slash_GET_method'), array('param'), array('GET' => 0), null), + 244 => array(array('_route' => 'regex_not_trailing_slash_HEAD_method'), array('param'), array('HEAD' => 0), null), + 273 => array(array('_route' => 'regex_not_trailing_slash_POST_method'), array('param'), array('POST' => 0), null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (273 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + return null; + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher8.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher8.php new file mode 100644 index 0000000..38bbfa2 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher8.php @@ -0,0 +1,88 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + $matchedPathinfo = $pathinfo; + $regexList = array( + 0 => '{^(?' + .'|/(a)(*:11)' + .')$}sD', + 11 => '{^(?' + .'|/(.)(*:22)' + .')$}sDu', + 22 => '{^(?' + .'|/(.)(*:33)' + .')$}sD', + ); + + foreach ($regexList as $offset => $regex) { + while (preg_match($regex, $matchedPathinfo, $matches)) { + switch ($m = (int) $matches['MARK']) { + default: + $routes = array( + 11 => array(array('_route' => 'a'), array('a'), null, null), + 22 => array(array('_route' => 'b'), array('a'), null, null), + 33 => array(array('_route' => 'c'), array('a'), null, null), + ); + + list($ret, $vars, $requiredMethods, $requiredSchemes) = $routes[$m]; + + foreach ($vars as $i => $v) { + if (isset($matches[1 + $i])) { + $ret[$v] = $matches[1 + $i]; + } + } + + $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); + if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { + if ($hasRequiredScheme) { + $allow += $requiredMethods; + } + break; + } + if (!$hasRequiredScheme) { + $allowSchemes += $requiredSchemes; + break; + } + + return $ret; + } + + if (33 === $m) { + break; + } + $regex = substr_replace($regex, 'F', $m - $offset, 1 + strlen($m)); + $offset += strlen($m); + } + } + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher9.php b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher9.php new file mode 100644 index 0000000..cfcd1d4 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/dumper/url_matcher9.php @@ -0,0 +1,53 @@ +context = $context; + } + + public function match($rawPathinfo) + { + $allow = $allowSchemes = array(); + $pathinfo = rawurldecode($rawPathinfo); + $context = $this->context; + $requestMethod = $canonicalMethod = $context->getMethod(); + $host = strtolower($context->getHost()); + + if ('HEAD' === $requestMethod) { + $canonicalMethod = 'GET'; + } + + switch ($pathinfo) { + case '/': + // a + if (preg_match('#^(?P[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) { + return $this->mergeDefaults(array('_route' => 'a') + $hostMatches, array()); + } + // c + if (preg_match('#^(?P[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', $host, $hostMatches)) { + return $this->mergeDefaults(array('_route' => 'c') + $hostMatches, array()); + } + // b + if ('d.c.b.a' === $host) { + return array('_route' => 'b'); + } + break; + } + + if ('/' === $pathinfo && !$allow && !$allowSchemes) { + throw new Symfony\Component\Routing\Exception\NoConfigurationException(); + } + + throw $allow ? new MethodNotAllowedException(array_keys($allow)) : new ResourceNotFoundException(); + } +} diff --git a/vendor/symfony/routing/Tests/Fixtures/empty.yml b/vendor/symfony/routing/Tests/Fixtures/empty.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/routing/Tests/Fixtures/file_resource.yml b/vendor/symfony/routing/Tests/Fixtures/file_resource.yml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/routing/Tests/Fixtures/foo.xml b/vendor/symfony/routing/Tests/Fixtures/foo.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/routing/Tests/Fixtures/foo1.xml b/vendor/symfony/routing/Tests/Fixtures/foo1.xml new file mode 100644 index 0000000..e69de29 diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/bar.xml b/vendor/symfony/routing/Tests/Fixtures/glob/bar.xml new file mode 100644 index 0000000..0d31eeb --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/bar.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/bar.yml b/vendor/symfony/routing/Tests/Fixtures/glob/bar.yml new file mode 100644 index 0000000..ba3bc22 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/bar.yml @@ -0,0 +1,4 @@ +bar_route: + path: /bar + defaults: + _controller: AppBundle:Bar:view diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/baz.xml b/vendor/symfony/routing/Tests/Fixtures/glob/baz.xml new file mode 100644 index 0000000..3abba1a --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/baz.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/baz.yml b/vendor/symfony/routing/Tests/Fixtures/glob/baz.yml new file mode 100644 index 0000000..f7d8c67 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/baz.yml @@ -0,0 +1,4 @@ +baz_route: + path: /baz + defaults: + _controller: AppBundle:Baz:view diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.xml b/vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.xml new file mode 100644 index 0000000..ca6b1b5 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.yml b/vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.yml new file mode 100644 index 0000000..d1ae585 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/import_multiple.yml @@ -0,0 +1,2 @@ +_static: + resource: ba?.yml diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/import_single.xml b/vendor/symfony/routing/Tests/Fixtures/glob/import_single.xml new file mode 100644 index 0000000..15f5698 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/import_single.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/import_single.yml b/vendor/symfony/routing/Tests/Fixtures/glob/import_single.yml new file mode 100644 index 0000000..f56ddbd --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/import_single.yml @@ -0,0 +1,2 @@ +_static: + resource: b?r.yml diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl.php b/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl.php new file mode 100644 index 0000000..897fa11 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl.php @@ -0,0 +1,7 @@ +import('php_dsl_ba?.php'); +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_bar.php b/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_bar.php new file mode 100644 index 0000000..e2b91b1 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_bar.php @@ -0,0 +1,12 @@ +collection(); + + $collection->add('bar_route', '/bar') + ->defaults(array('_controller' => 'AppBundle:Bar:view')); + + return $collection; +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_baz.php b/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_baz.php new file mode 100644 index 0000000..ca8f188 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/glob/php_dsl_baz.php @@ -0,0 +1,12 @@ +collection(); + + $collection->add('baz_route', '/baz') + ->defaults(array('_controller' => 'AppBundle:Baz:view')); + + return $collection; +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.xml b/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.xml new file mode 100644 index 0000000..b158dad --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.yml b/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.yml new file mode 100644 index 0000000..90dce0e --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/import_with_name_prefix/routing.yml @@ -0,0 +1,7 @@ +app: + resource: ../controller/routing.yml + +api: + resource: ../controller/routing.yml + name_prefix: api_ + prefix: /api diff --git a/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.xml b/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.xml new file mode 100644 index 0000000..f4b23a2 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.yml b/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.yml new file mode 100644 index 0000000..f840ecf --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/import_with_no_trailing_slash/routing.yml @@ -0,0 +1,10 @@ +app: + resource: ../controller/routing.yml + name_prefix: a_ + prefix: /slash + +api: + resource: ../controller/routing.yml + name_prefix: b_ + prefix: /no-slash + trailing_slash_on_root: false diff --git a/vendor/symfony/routing/Tests/Fixtures/incomplete.yml b/vendor/symfony/routing/Tests/Fixtures/incomplete.yml new file mode 100644 index 0000000..df64d32 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/incomplete.yml @@ -0,0 +1,2 @@ +blog_show: + defaults: { _controller: MyBlogBundle:Blog:show } diff --git a/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml new file mode 100644 index 0000000..f93bf9c --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/list_defaults.xml @@ -0,0 +1,20 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + true + 1 + 3.5 + foo + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/list_in_list_defaults.xml new file mode 100644 index 0000000..987086d --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/list_in_map_defaults.xml new file mode 100644 index 0000000..32d393c --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml b/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml new file mode 100644 index 0000000..c70e03c --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/list_null_values.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + + + + + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/localized.xml b/vendor/symfony/routing/Tests/Fixtures/localized.xml new file mode 100644 index 0000000..8146f95 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized.xml @@ -0,0 +1,13 @@ + + + + + + MyBundle:Blog:show + /path + /route + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.xml b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.xml new file mode 100644 index 0000000..aab6a96 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.xml @@ -0,0 +1,9 @@ + + + + MyBundle:Blog:show + + diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.yml b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.yml new file mode 100644 index 0000000..b62b569 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale-but-not-localized.yml @@ -0,0 +1,4 @@ +--- +imported: + controller: ImportedController::someAction + path: /imported diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.xml b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.xml new file mode 100644 index 0000000..7661dbb --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.xml @@ -0,0 +1,11 @@ + + + + MyBundle:Blog:show + /suffix + /le-suffix + + diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.yml b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.yml new file mode 100644 index 0000000..65def8a --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/imported-with-locale.yml @@ -0,0 +1,6 @@ +--- +imported: + controller: ImportedController::someAction + path: + nl: /voorbeeld + en: /example diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-controller-default.yml b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-controller-default.yml new file mode 100644 index 0000000..1d13a06 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-controller-default.yml @@ -0,0 +1,5 @@ +--- +i_need: + defaults: + _controller: DefaultController::defaultAction + resource: ./localized-route.yml diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.xml b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.xml new file mode 100644 index 0000000..dc3ff44 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.xml @@ -0,0 +1,10 @@ + + + + /le-prefix + /the-prefix + + diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.yml b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.yml new file mode 100644 index 0000000..bc33f3f --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale-imports-non-localized-route.yml @@ -0,0 +1,6 @@ +--- +i_need: + resource: ./imported-with-locale-but-not-localized.yml + prefix: + nl: /nl + en: /en diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.xml b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.xml new file mode 100644 index 0000000..c245f62 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.xml @@ -0,0 +1,10 @@ + + + + /le-prefix + /the-prefix + + diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.yml b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.yml new file mode 100644 index 0000000..29d3571 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/importer-with-locale.yml @@ -0,0 +1,6 @@ +--- +i_need: + resource: ./imported-with-locale.yml + prefix: + nl: /nl + en: /en diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/importing-localized-route.yml b/vendor/symfony/routing/Tests/Fixtures/localized/importing-localized-route.yml new file mode 100644 index 0000000..ab54ee4 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/importing-localized-route.yml @@ -0,0 +1,3 @@ +--- +i_need: + resource: ./localized-route.yml diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/localized-route.yml b/vendor/symfony/routing/Tests/Fixtures/localized/localized-route.yml new file mode 100644 index 0000000..351a418 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/localized-route.yml @@ -0,0 +1,9 @@ +--- +home: + path: + nl: /nl + en: /en + +not_localized: + controller: HomeController::otherAction + path: /here diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/missing-locale-in-importer.yml b/vendor/symfony/routing/Tests/Fixtures/localized/missing-locale-in-importer.yml new file mode 100644 index 0000000..b6d3f5e --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/missing-locale-in-importer.yml @@ -0,0 +1,5 @@ +--- +importing_with_missing_prefix: + resource: ./localized-route.yml + prefix: + nl: /prefix diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/not-localized.yml b/vendor/symfony/routing/Tests/Fixtures/localized/not-localized.yml new file mode 100644 index 0000000..4be493d --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/not-localized.yml @@ -0,0 +1,4 @@ +--- +not_localized: + controller: string + path: /here diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/officially_formatted_locales.yml b/vendor/symfony/routing/Tests/Fixtures/localized/officially_formatted_locales.yml new file mode 100644 index 0000000..a125a4e --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/officially_formatted_locales.yml @@ -0,0 +1,7 @@ +--- +official: + controller: HomeController::someAction + path: + fr.UTF-8: /omelette-au-fromage + pt-PT: /eu-não-sou-espanhol + pt_BR: /churrasco diff --git a/vendor/symfony/routing/Tests/Fixtures/localized/route-without-path-or-locales.yml b/vendor/symfony/routing/Tests/Fixtures/localized/route-without-path-or-locales.yml new file mode 100644 index 0000000..4c7c599 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/localized/route-without-path-or-locales.yml @@ -0,0 +1,3 @@ +--- +routename: + controller: Here::here diff --git a/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml new file mode 100644 index 0000000..47feb29 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/map_defaults.xml @@ -0,0 +1,20 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + true + 1 + 3.5 + foo + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/map_in_list_defaults.xml new file mode 100644 index 0000000..6d77065 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/map_in_map_defaults.xml new file mode 100644 index 0000000..2beee61 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml b/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml new file mode 100644 index 0000000..8fd8954 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/map_null_values.xml @@ -0,0 +1,22 @@ + + + + + + AcmeBlogBundle:Blog:index + + + + + + + + + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/missing_id.xml b/vendor/symfony/routing/Tests/Fixtures/missing_id.xml new file mode 100644 index 0000000..4ea4115 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/missing_id.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/missing_path.xml b/vendor/symfony/routing/Tests/Fixtures/missing_path.xml new file mode 100644 index 0000000..ef5bc08 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/missing_path.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml b/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml new file mode 100644 index 0000000..e33955a --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/namespaceprefix.xml @@ -0,0 +1,16 @@ + + + + + + MyBundle:Blog:show + \w+ + en|fr|de + RouteCompiler + + 1 + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml b/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml new file mode 100644 index 0000000..a3e9473 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonesense_resource_plus_path.yml @@ -0,0 +1,3 @@ +blog_show: + resource: validpattern.yml + path: /test diff --git a/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml b/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml new file mode 100644 index 0000000..547cda3 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonesense_type_without_resource.yml @@ -0,0 +1,3 @@ +blog_show: + path: /blog/{slug} + type: custom diff --git a/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml b/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml new file mode 100644 index 0000000..dc147d2 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonvalid.xml @@ -0,0 +1,10 @@ + + + + + + MyBundle:Blog:show + + diff --git a/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml b/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml new file mode 100644 index 0000000..257cc56 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonvalid.yml @@ -0,0 +1 @@ +foo diff --git a/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml b/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml new file mode 100644 index 0000000..cfa9992 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonvalid2.yml @@ -0,0 +1 @@ +route: string diff --git a/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml b/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml new file mode 100644 index 0000000..b01d502 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonvalidkeys.yml @@ -0,0 +1,3 @@ +someroute: + resource: path/to/some.yml + not_valid_key: test_ diff --git a/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml b/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml new file mode 100644 index 0000000..863ef03 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonvalidnode.xml @@ -0,0 +1,8 @@ + + + + + bar + diff --git a/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml b/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml new file mode 100644 index 0000000..908958c --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/nonvalidroute.xml @@ -0,0 +1,12 @@ + + + + + + MyBundle:Blog:show + + baz + + diff --git a/vendor/symfony/routing/Tests/Fixtures/null_values.xml b/vendor/symfony/routing/Tests/Fixtures/null_values.xml new file mode 100644 index 0000000..f9e2aa2 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/null_values.xml @@ -0,0 +1,12 @@ + + + + + + + foo + bar + + diff --git a/vendor/symfony/routing/Tests/Fixtures/php_dsl.php b/vendor/symfony/routing/Tests/Fixtures/php_dsl.php new file mode 100644 index 0000000..fbcab15 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/php_dsl.php @@ -0,0 +1,29 @@ +collection() + ->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->import('php_dsl_sub.php') + ->namePrefix('z_') + ->prefix('/zub'); + + $routes->import('php_dsl_sub_root.php') + ->prefix('/bus', false); + + $routes->add('ouf', '/ouf') + ->schemes(array('https')) + ->methods(array('GET')) + ->defaults(array('id' => 0)); +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/php_dsl_i18n.php b/vendor/symfony/routing/Tests/Fixtures/php_dsl_i18n.php new file mode 100644 index 0000000..ed4a0e2 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/php_dsl_i18n.php @@ -0,0 +1,17 @@ +collection() + ->prefix(array('en' => '/glish')) + ->add('foo', '/foo') + ->add('bar', array('en' => '/bar')); + + $routes + ->add('baz', array('en' => '/baz')); + + $routes->import('php_dsl_sub_i18n.php') + ->prefix(array('fr' => '/ench')); +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php b/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php new file mode 100644 index 0000000..08b0563 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub.php @@ -0,0 +1,15 @@ +collection('c_') + ->prefix('pub'); + + $add('root', '/'); + $add('bar', '/bar'); + + $add->collection('pub_') + ->host('host') + ->add('buz', 'buz'); +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_i18n.php b/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_i18n.php new file mode 100644 index 0000000..c112e71 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_i18n.php @@ -0,0 +1,11 @@ +collection('c_') + ->prefix('pub'); + + $add('foo', array('fr' => '/foo')); + $add('bar', array('fr' => '/bar')); +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_root.php b/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_root.php new file mode 100644 index 0000000..9b309fc --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/php_dsl_sub_root.php @@ -0,0 +1,10 @@ +collection('r_'); + + $add('root', '/'); + $add('bar', '/bar/'); +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/php_object_dsl.php b/vendor/symfony/routing/Tests/Fixtures/php_object_dsl.php new file mode 100644 index 0000000..f21f402 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/php_object_dsl.php @@ -0,0 +1,32 @@ +collection() + ->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->import('php_dsl_sub.php') + ->namePrefix('z_') + ->prefix('/zub'); + + $routes->import('php_dsl_sub_root.php') + ->prefix('/bus', false); + + $routes->add('ouf', '/ouf') + ->schemes(array('https')) + ->methods(array('GET')) + ->defaults(array('id' => 0)); + } +}; diff --git a/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml b/vendor/symfony/routing/Tests/Fixtures/scalar_defaults.xml new file mode 100644 index 0000000..ecfde28 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml b/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml new file mode 100644 index 0000000..78be239 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/special_route_name.yml @@ -0,0 +1,2 @@ +"#$péß^a|": + path: "true" diff --git a/vendor/symfony/routing/Tests/Fixtures/validpattern.php b/vendor/symfony/routing/Tests/Fixtures/validpattern.php new file mode 100644 index 0000000..52ddfde --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/validpattern.xml b/vendor/symfony/routing/Tests/Fixtures/validpattern.xml new file mode 100644 index 0000000..dbc72e4 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/validpattern.xml @@ -0,0 +1,15 @@ + + + + + + MyBundle:Blog:show + \w+ + + context.getMethod() == "GET" + + + + diff --git a/vendor/symfony/routing/Tests/Fixtures/validpattern.yml b/vendor/symfony/routing/Tests/Fixtures/validpattern.yml new file mode 100644 index 0000000..565abaa --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/validresource.php b/vendor/symfony/routing/Tests/Fixtures/validresource.php new file mode 100644 index 0000000..482c80b --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/validresource.xml b/vendor/symfony/routing/Tests/Fixtures/validresource.xml new file mode 100644 index 0000000..b7a15dd --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/validresource.xml @@ -0,0 +1,13 @@ + + + + + + 123 + \d+ + + context.getMethod() == "POST" + + diff --git a/vendor/symfony/routing/Tests/Fixtures/validresource.yml b/vendor/symfony/routing/Tests/Fixtures/validresource.yml new file mode 100644 index 0000000..faf2263 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php b/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php new file mode 100644 index 0000000..5871420 --- /dev/null +++ b/vendor/symfony/routing/Tests/Fixtures/with_define_path_variable.php @@ -0,0 +1,5 @@ + + + diff --git a/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php new file mode 100644 index 0000000..dcfb230 --- /dev/null +++ b/vendor/symfony/routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php @@ -0,0 +1,204 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Tests\Generator\Dumper; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +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 testDumpWithLocalizedRoutes() + { + $this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test')); + $this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl')->setDefault('_canonical_route', 'test')); + + $code = $this->generatorDumper->dump(array( + 'class' => 'LocalizedProjectUrlGenerator', + )); + file_put_contents($this->testTmpFilepath, $code); + include $this->testTmpFilepath; + + $context = new RequestContext('/app.php'); + $projectUrlGenerator = new \LocalizedProjectUrlGenerator($context, null, 'en'); + + $urlWithDefaultLocale = $projectUrlGenerator->generate('test'); + $urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', array('_locale' => 'nl')); + $context->setParameter('_locale', 'en'); + $urlWithEnglishContext = $projectUrlGenerator->generate('test'); + $context->setParameter('_locale', 'nl'); + $urlWithDutchContext = $projectUrlGenerator->generate('test'); + + $this->assertEquals('/app.php/testing/is/fun', $urlWithDefaultLocale); + $this->assertEquals('/app.php/testen/is/leuk', $urlWithSpecifiedLocale); + $this->assertEquals('/app.php/testing/is/fun', $urlWithEnglishContext); + $this->assertEquals('/app.php/testen/is/leuk', $urlWithDutchContext); + } + + public function testDumpWithTooManyRoutes() + { + $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/vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php b/vendor/symfony/routing/Tests/Generator/UrlGeneratorTest.php new file mode 100644 index 0000000..d4bf18c --- /dev/null +++ b/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\Generator\UrlGenerator; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +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/vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php b/vendor/symfony/routing/Tests/Loader/AbstractAnnotationLoaderTest.php new file mode 100644 index 0000000..e8bbe8f --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php b/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php new file mode 100644 index 0000000..dd9af9d --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/AnnotationClassLoaderTest.php @@ -0,0 +1,270 @@ + + * + * 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 Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Component\Routing\Annotation\Route as RouteAnnotation; +use Symfony\Component\Routing\Loader\AnnotationClassLoader; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\AbstractClassController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\ActionPathController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\DefaultValueController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\ExplicitLocalizedActionPathController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\InvokableController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\InvokableLocalizedController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedActionPathController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedMethodActionControllers; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixLocalizedActionController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixMissingLocaleActionController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixMissingRouteLocaleActionController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixWithRouteWithoutLocale; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\MethodActionControllers; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\MissingRouteNameController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\NothingButNameController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\PrefixedActionLocalizedRouteController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\PrefixedActionPathController; +use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\RouteWithPrefixController; + +class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest +{ + /** + * @var AnnotationClassLoader + */ + private $loader; + + protected function setUp() + { + $reader = new AnnotationReader(); + $this->loader = new class($reader) extends AnnotationClassLoader { + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + } + }; + AnnotationRegistry::registerLoader('class_exists'); + } + + /** + * @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 testSimplePathRoute() + { + $routes = $this->loader->load(ActionPathController::class); + $this->assertCount(1, $routes); + $this->assertEquals('/path', $routes->get('action')->getPath()); + } + + public function testInvokableControllerLoader() + { + $routes = $this->loader->load(InvokableController::class); + $this->assertCount(1, $routes); + $this->assertEquals('/here', $routes->get('lol')->getPath()); + } + + public function testInvokableLocalizedControllerLoading() + { + $routes = $this->loader->load(InvokableLocalizedController::class); + $this->assertCount(2, $routes); + $this->assertEquals('/here', $routes->get('action.en')->getPath()); + $this->assertEquals('/hier', $routes->get('action.nl')->getPath()); + } + + public function testLocalizedPathRoutes() + { + $routes = $this->loader->load(LocalizedActionPathController::class); + $this->assertCount(2, $routes); + $this->assertEquals('/path', $routes->get('action.en')->getPath()); + $this->assertEquals('/pad', $routes->get('action.nl')->getPath()); + } + + public function testLocalizedPathRoutesWithExplicitPathPropety() + { + $routes = $this->loader->load(ExplicitLocalizedActionPathController::class); + $this->assertCount(2, $routes); + $this->assertEquals('/path', $routes->get('action.en')->getPath()); + $this->assertEquals('/pad', $routes->get('action.nl')->getPath()); + } + + public function testDefaultValuesForMethods() + { + $routes = $this->loader->load(DefaultValueController::class); + $this->assertCount(1, $routes); + $this->assertEquals('/{default}/path', $routes->get('action')->getPath()); + $this->assertEquals('value', $routes->get('action')->getDefault('default')); + } + + public function testMethodActionControllers() + { + $routes = $this->loader->load(MethodActionControllers::class); + $this->assertCount(2, $routes); + $this->assertEquals('/the/path', $routes->get('put')->getPath()); + $this->assertEquals('/the/path', $routes->get('post')->getPath()); + } + + public function testLocalizedMethodActionControllers() + { + $routes = $this->loader->load(LocalizedMethodActionControllers::class); + $this->assertCount(4, $routes); + $this->assertEquals('/the/path', $routes->get('put.en')->getPath()); + $this->assertEquals('/the/path', $routes->get('post.en')->getPath()); + } + + public function testRouteWithPathWithPrefix() + { + $routes = $this->loader->load(PrefixedActionPathController::class); + $this->assertCount(1, $routes); + $route = $routes->get('action'); + $this->assertEquals('/prefix/path', $route->getPath()); + $this->assertEquals('lol=fun', $route->getCondition()); + $this->assertEquals('frankdejonge.nl', $route->getHost()); + } + + public function testLocalizedRouteWithPathWithPrefix() + { + $routes = $this->loader->load(PrefixedActionLocalizedRouteController::class); + $this->assertCount(2, $routes); + $this->assertEquals('/prefix/path', $routes->get('action.en')->getPath()); + $this->assertEquals('/prefix/pad', $routes->get('action.nl')->getPath()); + } + + public function testLocalizedPrefixLocalizedRoute() + { + $routes = $this->loader->load(LocalizedPrefixLocalizedActionController::class); + $this->assertCount(2, $routes); + $this->assertEquals('/nl/actie', $routes->get('action.nl')->getPath()); + $this->assertEquals('/en/action', $routes->get('action.en')->getPath()); + } + + public function testInvokableClassMultipleRouteLoad() + { + $classRouteData1 = array( + 'name' => 'route1', + 'path' => '/1', + 'schemes' => array('https'), + 'methods' => array('GET'), + ); + + $classRouteData2 = array( + 'name' => 'route2', + 'path' => '/2', + 'schemes' => array('https'), + 'methods' => array('GET'), + ); + + $reader = $this->getReader(); + $reader + ->expects($this->exactly(1)) + ->method('getClassAnnotations') + ->will($this->returnValue(array(new RouteAnnotation($classRouteData1), new RouteAnnotation($classRouteData2)))) + ; + $reader + ->expects($this->once()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + $loader = new class($reader) extends AnnotationClassLoader { + protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) + { + } + }; + + $routeCollection = $loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); + $route = $routeCollection->get($classRouteData1['name']); + + $this->assertSame($classRouteData1['path'], $route->getPath(), '->load preserves class route path'); + $this->assertEquals($classRouteData1['schemes'], $route->getSchemes(), '->load preserves class route schemes'); + $this->assertEquals($classRouteData1['methods'], $route->getMethods(), '->load preserves class route methods'); + + $route = $routeCollection->get($classRouteData2['name']); + + $this->assertSame($classRouteData2['path'], $route->getPath(), '->load preserves class route path'); + $this->assertEquals($classRouteData2['schemes'], $route->getSchemes(), '->load preserves class route schemes'); + $this->assertEquals($classRouteData2['methods'], $route->getMethods(), '->load preserves class route methods'); + } + + public function testMissingPrefixLocale() + { + $this->expectException(\LogicException::class); + $this->loader->load(LocalizedPrefixMissingLocaleActionController::class); + } + + public function testMissingRouteLocale() + { + $this->expectException(\LogicException::class); + $this->loader->load(LocalizedPrefixMissingRouteLocaleActionController::class); + } + + public function testRouteWithoutName() + { + $routes = $this->loader->load(MissingRouteNameController::class)->all(); + $this->assertCount(1, $routes); + $this->assertEquals('/path', reset($routes)->getPath()); + } + + public function testNothingButName() + { + $routes = $this->loader->load(NothingButNameController::class)->all(); + $this->assertCount(1, $routes); + $this->assertEquals('/', reset($routes)->getPath()); + } + + public function testNonExistingClass() + { + $this->expectException(\LogicException::class); + $this->loader->load('ClassThatDoesNotExist'); + } + + public function testLoadingAbstractClass() + { + $this->expectException(\LogicException::class); + $this->loader->load(AbstractClassController::class); + } + + public function testLocalizedPrefixWithoutRouteLocale() + { + $routes = $this->loader->load(LocalizedPrefixWithRouteWithoutLocale::class); + $this->assertCount(2, $routes); + $this->assertEquals('/en/suffix', $routes->get('action.en')->getPath()); + $this->assertEquals('/nl/suffix', $routes->get('action.nl')->getPath()); + } + + public function testLoadingRouteWithPrefix() + { + $routes = $this->loader->load(RouteWithPrefixController::class); + $this->assertCount(1, $routes); + $this->assertEquals('/prefix/path', $routes->get('action')->getPath()); + } +} diff --git a/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php b/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.php new file mode 100644 index 0000000..901b858 --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/AnnotationDirectoryLoaderTest.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\Routing\Tests\Loader; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; + +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(3))->method('getClassAnnotation'); + + $this->reader + ->expects($this->any()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + + $this->reader + ->expects($this->any()) + ->method('getClassAnnotations') + ->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\FooClass', + )); + + $this->reader + ->expects($this->any()) + ->method('getMethodAnnotations') + ->will($this->returnValue(array())) + ; + + $this->reader + ->expects($this->any()) + ->method('getClassAnnotations') + ->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/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php b/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.php new file mode 100644 index 0000000..50e212b --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/AnnotationFileLoaderTest.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\Routing\Tests\Loader; + +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Loader\AnnotationFileLoader; + +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'); + } + + 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'); + } + + 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/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php b/vendor/symfony/routing/Tests/Loader/ClosureLoaderTest.php new file mode 100644 index 0000000..5d963f8 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php b/vendor/symfony/routing/Tests/Loader/DirectoryLoaderTest.php new file mode 100644 index 0000000..4596855 --- /dev/null +++ b/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\Config\FileLocator; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\Routing\Loader\AnnotationFileLoader; +use Symfony\Component\Routing\Loader\DirectoryLoader; +use Symfony\Component\Routing\Loader\YamlFileLoader; +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/vendor/symfony/routing/Tests/Loader/FileLocatorStub.php b/vendor/symfony/routing/Tests/Loader/FileLocatorStub.php new file mode 100644 index 0000000..870c3cf --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/FileLocatorStub.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\Routing\Tests\Loader; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Resource\GlobResource; +use Symfony\Component\Routing\Loader\GlobFileLoader; +use Symfony\Component\Routing\RouteCollection; + +class GlobFileLoaderTest extends TestCase +{ + public function testSupports() + { + $loader = new GlobFileLoader(new FileLocator()); + + $this->assertTrue($loader->supports('any-path', 'glob'), '->supports() returns true if the resource has the glob type'); + $this->assertFalse($loader->supports('any-path'), '->supports() returns false if the resource is not of glob type'); + } + + public function testLoadAddsTheGlobResourceToTheContainer() + { + $loader = new GlobFileLoaderWithoutImport(new FileLocator()); + $collection = $loader->load(__DIR__.'/../Fixtures/directory/*.yml'); + + $this->assertEquals(new GlobResource(__DIR__.'/../Fixtures/directory', '/*.yml', false), $collection->getResources()[0]); + } +} + +class GlobFileLoaderWithoutImport extends GlobFileLoader +{ + public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) + { + return new RouteCollection(); + } +} diff --git a/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php b/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.php new file mode 100644 index 0000000..ed65068 --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/ObjectRouteLoaderTest.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\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 +{ + /** + * @group legacy + * @expectedDeprecation Referencing service route loaders with a single colon is deprecated since Symfony 4.1. Use my_route_provider_service::loadRoutes instead. + */ + public function testLoadCallsServiceAndReturnsCollectionWithLegacyNotation() + { + $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()); + } + + 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('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/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php b/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.php new file mode 100644 index 0000000..a148e9e --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/PhpFileLoaderTest.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\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); + $routeCollectionClosure = $loader->load('php_dsl.php'); + $routeCollectionObject = $loader->load('php_object_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_root', (new Route('/sub/pub/')) + ->setRequirements(array('id' => '\d+')) + ); + $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('z_c_root', new Route('/zub/pub/')); + $expectedCollection->add('z_c_bar', new Route('/zub/pub/bar')); + $expectedCollection->add('z_c_pub_buz', (new Route('/zub/pub/buz'))->setHost('host')); + $expectedCollection->add('r_root', new Route('/bus')); + $expectedCollection->add('r_bar', new Route('/bus/bar/')); + $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_sub_root.php'))); + + $expectedCollectionClosure = $expectedCollection; + $expectedCollectionObject = clone $expectedCollection; + + $expectedCollectionClosure->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl.php'))); + $expectedCollectionObject->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_object_dsl.php'))); + + $this->assertEquals($expectedCollectionClosure, $routeCollectionClosure); + $this->assertEquals($expectedCollectionObject, $routeCollectionObject); + } + + public function testRoutingConfiguratorCanImportGlobPatterns() + { + $locator = new FileLocator(array(__DIR__.'/../Fixtures/glob')); + $loader = new PhpFileLoader($locator); + $routeCollection = $loader->load('php_dsl.php'); + + $route = $routeCollection->get('bar_route'); + $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); + + $route = $routeCollection->get('baz_route'); + $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); + } + + public function testRoutingI18nConfigurator() + { + $locator = new FileLocator(array(__DIR__.'/../Fixtures')); + $loader = new PhpFileLoader($locator); + $routeCollection = $loader->load('php_dsl_i18n.php'); + + $expectedCollection = new RouteCollection(); + + $expectedCollection->add('foo.en', (new Route('/glish/foo'))->setDefaults(array('_locale' => 'en', '_canonical_route' => 'foo'))); + $expectedCollection->add('bar.en', (new Route('/glish/bar'))->setDefaults(array('_locale' => 'en', '_canonical_route' => 'bar'))); + $expectedCollection->add('baz.en', (new Route('/baz'))->setDefaults(array('_locale' => 'en', '_canonical_route' => 'baz'))); + $expectedCollection->add('c_foo.fr', (new Route('/ench/pub/foo'))->setDefaults(array('_locale' => 'fr', '_canonical_route' => 'c_foo'))); + $expectedCollection->add('c_bar.fr', (new Route('/ench/pub/bar'))->setDefaults(array('_locale' => 'fr', '_canonical_route' => 'c_bar'))); + + $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_i18n.php'))); + $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_i18n.php'))); + + $this->assertEquals($expectedCollection, $routeCollection); + } +} diff --git a/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php b/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php new file mode 100644 index 0000000..fff4154 --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/XmlFileLoaderTest.php @@ -0,0 +1,444 @@ + + * + * 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()); + } + } + + public function testLoadLocalized() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures'))); + $routeCollection = $loader->load('localized.xml'); + $routes = $routeCollection->all(); + + $this->assertCount(2, $routes, 'Two routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + $this->assertEquals('/route', $routeCollection->get('localized.fr')->getPath()); + $this->assertEquals('/path', $routeCollection->get('localized.en')->getPath()); + } + + public function testLocalizedImports() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routeCollection = $loader->load('importer-with-locale.xml'); + $routes = $routeCollection->all(); + + $this->assertCount(2, $routes, 'Two routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + $this->assertEquals('/le-prefix/le-suffix', $routeCollection->get('imported.fr')->getPath()); + $this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath()); + } + + public function testLocalizedImportsOfNotLocalizedRoutes() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routeCollection = $loader->load('importer-with-locale-imports-non-localized-route.xml'); + $routes = $routeCollection->all(); + + $this->assertCount(2, $routes, 'Two routes are loaded'); + $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); + + $this->assertEquals('/le-prefix/suffix', $routeCollection->get('imported.fr')->getPath()); + $this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath()); + } + + /** + * @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'); + } + + public function testImportRouteWithGlobMatchingSingleFile() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/glob'))); + $routeCollection = $loader->load('import_single.xml'); + + $route = $routeCollection->get('bar_route'); + $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); + } + + public function testImportRouteWithGlobMatchingMultipleFiles() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/glob'))); + $routeCollection = $loader->load('import_multiple.xml'); + + $route = $routeCollection->get('bar_route'); + $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); + + $route = $routeCollection->get('baz_route'); + $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); + } + + public function testImportRouteWithNamePrefix() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_name_prefix'))); + $routeCollection = $loader->load('routing.xml'); + + $this->assertNotNull($routeCollection->get('app_blog')); + $this->assertEquals('/blog', $routeCollection->get('app_blog')->getPath()); + $this->assertNotNull($routeCollection->get('api_app_blog')); + $this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath()); + } + + public function testImportRouteWithNoTrailingSlash() + { + $loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_no_trailing_slash'))); + $routeCollection = $loader->load('routing.xml'); + + $this->assertEquals('/slash/', $routeCollection->get('a_app_homepage')->getPath()); + $this->assertEquals('/no-slash', $routeCollection->get('b_app_homepage')->getPath()); + } +} diff --git a/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php b/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php new file mode 100644 index 0000000..41f87bb --- /dev/null +++ b/vendor/symfony/routing/Tests/Loader/YamlFileLoaderTest.php @@ -0,0 +1,307 @@ + + * + * 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\YamlFileLoader; + +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'); + } + + public function testImportRouteWithGlobMatchingSingleFile() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/glob'))); + $routeCollection = $loader->load('import_single.yml'); + + $route = $routeCollection->get('bar_route'); + $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); + } + + public function testImportRouteWithGlobMatchingMultipleFiles() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/glob'))); + $routeCollection = $loader->load('import_multiple.yml'); + + $route = $routeCollection->get('bar_route'); + $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); + + $route = $routeCollection->get('baz_route'); + $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); + } + + public function testImportRouteWithNamePrefix() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_name_prefix'))); + $routeCollection = $loader->load('routing.yml'); + + $this->assertNotNull($routeCollection->get('app_blog')); + $this->assertEquals('/blog', $routeCollection->get('app_blog')->getPath()); + $this->assertNotNull($routeCollection->get('api_app_blog')); + $this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath()); + } + + public function testRemoteSourcesAreNotAccepted() + { + $loader = new YamlFileLoader(new FileLocatorStub()); + $this->expectException(\InvalidArgumentException::class); + $loader->load('http://remote.com/here.yml'); + } + + public function testLoadingLocalizedRoute() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routes = $loader->load('localized-route.yml'); + + $this->assertCount(3, $routes); + } + + public function testImportingRoutesFromDefinition() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routes = $loader->load('importing-localized-route.yml'); + + $this->assertCount(3, $routes); + $this->assertEquals('/nl', $routes->get('home.nl')->getPath()); + $this->assertEquals('/en', $routes->get('home.en')->getPath()); + $this->assertEquals('/here', $routes->get('not_localized')->getPath()); + } + + public function testImportingRoutesWithLocales() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routes = $loader->load('importer-with-locale.yml'); + + $this->assertCount(2, $routes); + $this->assertEquals('/nl/voorbeeld', $routes->get('imported.nl')->getPath()); + $this->assertEquals('/en/example', $routes->get('imported.en')->getPath()); + } + + public function testImportingNonLocalizedRoutesWithLocales() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routes = $loader->load('importer-with-locale-imports-non-localized-route.yml'); + + $this->assertCount(2, $routes); + $this->assertEquals('/nl/imported', $routes->get('imported.nl')->getPath()); + $this->assertEquals('/en/imported', $routes->get('imported.en')->getPath()); + } + + public function testImportingRoutesWithOfficialLocales() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routes = $loader->load('officially_formatted_locales.yml'); + + $this->assertCount(3, $routes); + $this->assertEquals('/omelette-au-fromage', $routes->get('official.fr.UTF-8')->getPath()); + $this->assertEquals('/eu-não-sou-espanhol', $routes->get('official.pt-PT')->getPath()); + $this->assertEquals('/churrasco', $routes->get('official.pt_BR')->getPath()); + } + + public function testImportingRoutesFromDefinitionMissingLocalePrefix() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $this->expectException(\InvalidArgumentException::class); + $loader->load('missing-locale-in-importer.yml'); + } + + public function testImportingRouteWithoutPathOrLocales() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $this->expectException(\InvalidArgumentException::class); + $loader->load('route-without-path-or-locales.yml'); + } + + public function testImportingWithControllerDefault() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized'))); + $routes = $loader->load('importer-with-controller-default.yml'); + $this->assertCount(3, $routes); + $this->assertEquals('DefaultController::defaultAction', $routes->get('home.en')->getDefault('_controller')); + $this->assertEquals('DefaultController::defaultAction', $routes->get('home.nl')->getDefault('_controller')); + $this->assertEquals('DefaultController::defaultAction', $routes->get('not_localized')->getDefault('_controller')); + } + + public function testImportRouteWithNoTrailingSlash() + { + $loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_no_trailing_slash'))); + $routeCollection = $loader->load('routing.yml'); + + $this->assertEquals('/slash/', $routeCollection->get('a_app_homepage')->getPath()); + $this->assertEquals('/no-slash', $routeCollection->get('b_app_homepage')->getPath()); + } +} diff --git a/vendor/symfony/routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php b/vendor/symfony/routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.php new file mode 100644 index 0000000..1785c3a --- /dev/null +++ b/vendor/symfony/routing/Tests/Matcher/DumpedRedirectableUrlMatcherTest.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\Routing\Tests\Matcher; + +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\RouteCollection; + +class DumpedRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest +{ + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + static $i = 0; + + $class = 'DumpedRedirectableUrlMatcher'.++$i; + $dumper = new PhpMatcherDumper($routes); + eval('?>'.$dumper->dump(array('class' => $class, 'base_class' => 'Symfony\Component\Routing\Tests\Matcher\TestDumpedRedirectableUrlMatcher'))); + + return $this->getMockBuilder($class) + ->setConstructorArgs(array($context ?: new RequestContext())) + ->setMethods(array('redirect')) + ->getMock(); + } +} + +class TestDumpedRedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + public function redirect($path, $route, $scheme = null) + { + return array(); + } +} diff --git a/vendor/symfony/routing/Tests/Matcher/DumpedUrlMatcherTest.php b/vendor/symfony/routing/Tests/Matcher/DumpedUrlMatcherTest.php new file mode 100644 index 0000000..47e3251 --- /dev/null +++ b/vendor/symfony/routing/Tests/Matcher/DumpedUrlMatcherTest.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\Matcher; + +use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; + +class DumpedUrlMatcherTest extends UrlMatcherTest +{ + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + static $i = 0; + + $class = 'DumpedUrlMatcher'.++$i; + $dumper = new PhpMatcherDumper($routes); + eval('?>'.$dumper->dump(array('class' => $class))); + + return new $class($context ?: new RequestContext()); + } +} diff --git a/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php b/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php new file mode 100644 index 0000000..166129c --- /dev/null +++ b/vendor/symfony/routing/Tests/Matcher/Dumper/PhpMatcherDumperTest.php @@ -0,0 +1,513 @@ + + * + * 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); + } + + 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}'))->setMethods('PUT')); + $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_head', + 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/')); + + /* test case 6 & 7 */ + $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'))); + + /* test case 8 */ + $unicodeCollection = new RouteCollection(); + $unicodeCollection->add('a', new Route('/{a}', array(), array('a' => 'a'), array('utf8' => false))); + $unicodeCollection->add('b', new Route('/{a}', array(), array('a' => '.'), array('utf8' => true))); + $unicodeCollection->add('c', new Route('/{a}', array(), array('a' => '.'), array('utf8' => false))); + + /* test case 9 */ + $hostTreeCollection = new RouteCollection(); + $hostTreeCollection->add('a', (new Route('/'))->setHost('{d}.e.c.b.a')); + $hostTreeCollection->add('b', (new Route('/'))->setHost('d.c.b.a')); + $hostTreeCollection->add('c', (new Route('/'))->setHost('{e}.e.c.b.a')); + + /* test case 10 */ + $chunkedCollection = new RouteCollection(); + for ($i = 0; $i < 1000; ++$i) { + $h = substr(md5($i), 0, 6); + $chunkedCollection->add('_'.$i, new Route('/'.$h.'/{a}/{b}/{c}/'.$h)); + } + + /* test case 11 */ + $demoCollection = new RouteCollection(); + $demoCollection->add('a', new Route('/admin/post/')); + $demoCollection->add('b', new Route('/admin/post/new')); + $demoCollection->add('c', (new Route('/admin/post/{id}'))->setRequirements(array('id' => '\d+'))); + $demoCollection->add('d', (new Route('/admin/post/{id}/edit'))->setRequirements(array('id' => '\d+'))); + $demoCollection->add('e', (new Route('/admin/post/{id}/delete'))->setRequirements(array('id' => '\d+'))); + $demoCollection->add('f', new Route('/blog/')); + $demoCollection->add('g', new Route('/blog/rss.xml')); + $demoCollection->add('h', (new Route('/blog/page/{page}'))->setRequirements(array('id' => '\d+'))); + $demoCollection->add('i', (new Route('/blog/posts/{page}'))->setRequirements(array('id' => '\d+'))); + $demoCollection->add('j', (new Route('/blog/comments/{id}/new'))->setRequirements(array('id' => '\d+'))); + $demoCollection->add('k', new Route('/blog/search')); + $demoCollection->add('l', new Route('/login')); + $demoCollection->add('m', new Route('/logout')); + $demoCollection->addPrefix('/{_locale}'); + $demoCollection->add('n', new Route('/{_locale}')); + $demoCollection->addRequirements(array('_locale' => 'en|fr')); + $demoCollection->addDefaults(array('_locale' => 'en')); + + /* test case 12 */ + $suffixCollection = new RouteCollection(); + $suffixCollection->add('r1', new Route('abc{foo}/1')); + $suffixCollection->add('r2', new Route('abc{foo}/2')); + $suffixCollection->add('r10', new Route('abc{foo}/10')); + $suffixCollection->add('r20', new Route('abc{foo}/20')); + $suffixCollection->add('r100', new Route('abc{foo}/100')); + $suffixCollection->add('r200', new Route('abc{foo}/200')); + + /* test case 13 */ + $hostCollection = new RouteCollection(); + $hostCollection->add('r1', (new Route('abc{foo}'))->setHost('{foo}.exampple.com')); + $hostCollection->add('r2', (new Route('abc{foo}'))->setHost('{foo}.exampple.com')); + + 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')), + array($unicodeCollection, 'url_matcher8.php', array()), + array($hostTreeCollection, 'url_matcher9.php', array()), + array($chunkedCollection, 'url_matcher10.php', array()), + array($demoCollection, 'url_matcher11.php', array('base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher')), + array($suffixCollection, 'url_matcher12.php', array()), + array($hostCollection, 'url_matcher13.php', array()), + ); + } + + /** + * @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; + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Symfony\Component\Routing\Route cannot contain objects + */ + public function testGenerateDumperMatcherWithObject() + { + $routeCollection = new RouteCollection(); + $routeCollection->add('_', new Route('/', array(new \stdClass()))); + $dumper = new PhpMatcherDumper($routeCollection); + $dumper->dump(); + } +} + +abstract class RedirectableUrlMatcherStub extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + public function redirect($path, $route, $scheme = null) + { + } +} diff --git a/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php b/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php new file mode 100644 index 0000000..24e12ac --- /dev/null +++ b/vendor/symfony/routing/Tests/Matcher/Dumper/StaticPrefixCollectionTest.php @@ -0,0 +1,177 @@ +compile()->getStaticPrefix(); + $collection->addRoute($staticPrefix, array($name)); + } + + $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'), + ), + << prefix_segment +-> leading_segment +EOF + ), + 'Nested - contains item at intersection' => array( + array( + array('/', 'root'), + array('/prefix/segment/', 'prefix_segment'), + array('/prefix/segment/bb', 'leading_segment'), + ), + << prefix_segment +-> 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'), + ), + << nested_segment +-> some_segment +-> 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'), + ), + << aa +-> bb +-> cc +root +/group/ +-> dd +-> ee +-> 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/', 'parent'), + array('/prefixed/group/ff/', 'ff'), + array('/aaa/222/', 'second_aaa'), + array('/aaa/333/', 'third_aaa'), + ), + << first_aaa +-> second_aaa +-> third_aaa +/prefixed/ +-> /prefixed/group/ +-> -> aa +-> -> bb +-> -> cc +-> root +-> /prefixed/group/ +-> -> dd +-> -> ee +-> -> ff +-> parent +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'), + ), + << a1 +-> a2 +-> a3 +/group- +-> g1 +-> g2 +-> g3 +EOF + ), + ); + } + + private function dumpCollection(StaticPrefixCollection $collection, $prefix = '') + { + $lines = array(); + + foreach ($collection->getRoutes() 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/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php new file mode 100644 index 0000000..e367962 --- /dev/null +++ b/vendor/symfony/routing/Tests/Matcher/RedirectableUrlMatcherTest.php @@ -0,0 +1,158 @@ + + * + * 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 Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class RedirectableUrlMatcherTest extends UrlMatcherTest +{ + public function testMissingTrailingSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $matcher = $this->getUrlMatcher($coll); + $matcher->expects($this->once())->method('redirect')->will($this->returnValue(array())); + $matcher->match('/foo'); + } + + public function testExtraTrailingSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo')); + + $matcher = $this->getUrlMatcher($coll); + $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->getUrlMatcher($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->getUrlMatcher($coll); + $matcher + ->expects($this->once()) + ->method('redirect') + ->with('/foo', 'foo', 'ftp') + ->will($this->returnValue(array('_route' => 'foo'))) + ; + $matcher->match('/foo'); + } + + public function testNoSchemaRedirectIfOneOfMultipleSchemesMatches() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https', 'http'))); + + $matcher = $this->getUrlMatcher($coll); + $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->getUrlMatcher($coll); + $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 testSchemeRedirectForRoot() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/', array(), array(), array(), '', array('https'))); + + $matcher = $this->getUrlMatcher($coll); + $matcher + ->expects($this->once()) + ->method('redirect') + ->with('/', 'foo', 'https') + ->will($this->returnValue(array('redirect' => 'value'))); + $this->assertEquals(array('_route' => 'foo', 'redirect' => 'value'), $matcher->match('/')); + } + + public function testSlashRedirectWithParams() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/{bar}/')); + + $matcher = $this->getUrlMatcher($coll); + $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->getUrlMatcher($coll); + $matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/')->willReturn(array()); + $matcher->match('/foo%3Abar'); + } + + public function testSchemeRequirement() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + $matcher = $this->getUrlMatcher($coll, new RequestContext()); + $matcher->expects($this->once())->method('redirect')->with('/foo', 'foo', 'https')->willReturn(array()); + $this->assertSame(array('_route' => 'foo'), $matcher->match('/foo')); + } + + public function testMissingTrailingSlashAndScheme() + { + $coll = new RouteCollection(); + $coll->add('foo', (new Route('/foo/'))->setSchemes(array('https'))); + + $matcher = $this->getUrlMatcher($coll); + $matcher->expects($this->once())->method('redirect')->with('/foo/', 'foo', 'https')->will($this->returnValue(array())); + $matcher->match('/foo'); + } + + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + return $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($routes, $context ?: new RequestContext())); + } +} diff --git a/vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php b/vendor/symfony/routing/Tests/Matcher/TraceableUrlMatcherTest.php new file mode 100644 index 0000000..1f026a6 --- /dev/null +++ b/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\Matcher\TraceableUrlMatcher; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +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/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php b/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php new file mode 100644 index 0000000..c1add96 --- /dev/null +++ b/vendor/symfony/routing/Tests/Matcher/UrlMatcherTest.php @@ -0,0 +1,687 @@ + + * + * 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\RequestContext; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +class UrlMatcherTest extends TestCase +{ + public function testNoMethodSoAllowed() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo')); + + $matcher = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($coll); + + try { + $matcher->match('/foo'); + $this->fail(); + } catch (MethodNotAllowedException $e) { + $this->assertEquals(array('POST'), $e->getAllowedMethods()); + } + } + + public function testMethodNotAllowedOnRoot() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/', array(), array(), array(), '', array(), array('GET'))); + + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'POST')); + + try { + $matcher->match('/'); + $this->fail(); + } catch (MethodNotAllowedException $e) { + $this->assertEquals(array('GET'), $e->getAllowedMethods()); + } + } + + public function testHeadAllowedWhenRequirementContainsGet() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array(), array('get'))); + + $matcher = $this->getUrlMatcher($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 = $this->getUrlMatcher($coll); + + 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 = $this->getUrlMatcher($collection); + 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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $this->assertInternalType('array', $matcher->match('/foo')); + + // route does not match with POST method context + $matcher = $this->getUrlMatcher($collection, new RequestContext('', 'post')); + try { + $matcher->match('/foo'); + $this->fail(); + } catch (MethodNotAllowedException $e) { + } + + // route does match with GET or HEAD method context + $matcher = $this->getUrlMatcher($collection); + $this->assertInternalType('array', $matcher->match('/foo')); + $matcher = $this->getUrlMatcher($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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar')); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testTrailingEncodedNewlineIsNotOverlooked() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo')); + + $matcher = $this->getUrlMatcher($collection); + $matcher->match('/foo%0a'); + } + + 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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + $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 = $this->getUrlMatcher($collection); + + $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 = $this->getUrlMatcher($coll); + $this->assertEquals(array('foo' => 'bar', '_route' => 'bar'), $matcher->match('/foo/bar/bar')); + + $collection = new RouteCollection(); + $collection->add('foo', new Route('/{bar}')); + $matcher = $this->getUrlMatcher($collection); + try { + $matcher->match('/'); + $this->fail(); + } catch (ResourceNotFoundException $e) { + } + } + + public function testMultipleParams() + { + $coll = new RouteCollection(); + $coll->add('foo1', new Route('/foo/{a}/{b}')); + $coll->add('foo2', new Route('/foo/{a}/test/test/{b}')); + $coll->add('foo3', new Route('/foo/{a}/{b}/{c}/{d}')); + + $route = $this->getUrlMatcher($coll)->match('/foo/test/test/test/bar')['_route']; + + $this->assertEquals('foo2', $route); + } + + public function testDefaultRequirementForOptionalVariables() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{page}.{_format}', array('page' => 'index', '_format' => 'html'))); + + $matcher = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($coll); + // '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 = $this->getUrlMatcher($coll); + + $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 = $this->getUrlMatcher($coll); + + $this->assertEquals(array('what' => 'Sites', '_route' => 'test'), $matcher->match('/getSitesSuffix')); + } + + public function testDefaultRequirementOfVariable() + { + $coll = new RouteCollection(); + $coll->add('test', new Route('/{page}.{_format}')); + $matcher = $this->getUrlMatcher($coll); + + $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 = $this->getUrlMatcher($coll); + + $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 = $this->getUrlMatcher($coll); + + $matcher->match('/do.t.html'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testMissingTrailingSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $matcher = $this->getUrlMatcher($coll); + $matcher->match('/foo'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testExtraTrailingSlash() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo')); + + $matcher = $this->getUrlMatcher($coll); + $matcher->match('/foo/'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testMissingTrailingSlashForNonSafeMethod() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo/')); + + $context = new RequestContext(); + $context->setMethod('POST'); + $matcher = $this->getUrlMatcher($coll, $context); + $matcher->match('/foo'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testExtraTrailingSlashForNonSafeMethod() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo')); + + $context = new RequestContext(); + $context->setMethod('POST'); + $matcher = $this->getUrlMatcher($coll, $context); + $matcher->match('/foo/'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testSchemeRequirement() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + $matcher = $this->getUrlMatcher($coll); + $matcher->match('/foo'); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testSchemeRequirementForNonSafeMethod() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https'))); + + $context = new RequestContext(); + $context->setMethod('POST'); + $matcher = $this->getUrlMatcher($coll, $context); + $matcher->match('/foo'); + } + + public function testSamePathWithDifferentScheme() + { + $coll = new RouteCollection(); + $coll->add('https_route', new Route('/', array(), array(), array(), '', array('https'))); + $coll->add('http_route', new Route('/', array(), array(), array(), '', array('http'))); + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'http_route'), $matcher->match('/')); + } + + /** + * @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 = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($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 = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($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 = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); + $this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar')); + + $matcher = $this->getUrlMatcher($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 = $this->getUrlMatcher($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 = $this->getUrlMatcher($coll); + $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 = $this->getUrlMatcher($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 = $this->getUrlMatcher($coll); + $matcher->match('/'); + } + + public function testNestedCollections() + { + $coll = new RouteCollection(); + + $subColl = new RouteCollection(); + $subColl->add('a', new Route('/a')); + $subColl->add('b', new Route('/b')); + $subColl->add('c', new Route('/c')); + $subColl->addPrefix('/p'); + $coll->addCollection($subColl); + + $coll->add('baz', new Route('/{baz}')); + + $subColl = new RouteCollection(); + $subColl->add('buz', new Route('/buz')); + $subColl->addPrefix('/prefix'); + $coll->addCollection($subColl); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'a'), $matcher->match('/p/a')); + $this->assertEquals(array('_route' => 'baz', 'baz' => 'p'), $matcher->match('/p')); + $this->assertEquals(array('_route' => 'buz'), $matcher->match('/prefix/buz')); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException + */ + public function testSchemeAndMethodMismatch() + { + $coll = new RouteCollection(); + $coll->add('foo', new Route('/', array(), array(), array(), null, array('https'), array('POST'))); + + $matcher = $this->getUrlMatcher($coll); + $matcher->match('/'); + } + + public function testSiblingRoutes() + { + $coll = new RouteCollection(); + $coll->add('a', (new Route('/a{a}'))->setMethods('POST')); + $coll->add('b', (new Route('/a{a}'))->setMethods('PUT')); + $coll->add('c', new Route('/a{a}')); + $coll->add('d', (new Route('/b{a}'))->setCondition('false')); + $coll->add('e', (new Route('/{b}{a}'))->setCondition('false')); + $coll->add('f', (new Route('/{b}{a}'))->setRequirements(array('b' => 'b'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'c', 'a' => 'a'), $matcher->match('/aa')); + $this->assertEquals(array('_route' => 'f', 'b' => 'b', 'a' => 'a'), $matcher->match('/ba')); + } + + public function testUnicodeRoute() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/{a}', array(), array('a' => '.'), array('utf8' => false))); + $coll->add('b', new Route('/{a}', array(), array('a' => '.'), array('utf8' => true))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'b', 'a' => 'é'), $matcher->match('/é')); + } + + public function testRequirementWithCapturingGroup() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/{a}/{b}', array(), array('a' => '(a|b)'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'a', 'a' => 'a', 'b' => 'b'), $matcher->match('/a/b')); + } + + public function testDotAllWithCatchAll() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/{id}.html', array(), array('id' => '.+'))); + $coll->add('b', new Route('/{all}', array(), array('all' => '.+'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals(array('_route' => 'a', 'id' => 'foo/bar'), $matcher->match('/foo/bar.html')); + } + + public function testHostPattern() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/{app}/{action}/{unused}', array(), array(), array(), '{host}')); + + $expected = array( + '_route' => 'a', + 'app' => 'an_app', + 'action' => 'an_action', + 'unused' => 'unused', + 'host' => 'foo', + ); + $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'foo')); + $this->assertEquals($expected, $matcher->match('/an_app/an_action/unused')); + } + + public function testUtf8Prefix() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/é{foo}', array(), array(), array('utf8' => true))); + $coll->add('b', new Route('/è{bar}', array(), array(), array('utf8' => true))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals('a', $matcher->match('/éo')['_route']); + } + + public function testUtf8AndMethodMatching() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/admin/api/list/{shortClassName}/{id}.{_format}', array(), array(), array('utf8' => true), '', array(), array('PUT'))); + $coll->add('b', new Route('/admin/api/package.{_format}', array(), array(), array(), '', array(), array('POST'))); + $coll->add('c', new Route('/admin/api/package.{_format}', array('_format' => 'json'), array(), array(), '', array(), array('GET'))); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals('c', $matcher->match('/admin/api/package.json')['_route']); + } + + public function testHostWithDot() + { + $coll = new RouteCollection(); + $coll->add('a', new Route('/foo', array(), array(), array(), 'foo.example.com')); + $coll->add('b', new Route('/bar/{baz}')); + + $matcher = $this->getUrlMatcher($coll); + $this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']); + } + + protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) + { + return new UrlMatcher($routes, $context ?: new RequestContext()); + } +} diff --git a/vendor/symfony/routing/Tests/RequestContextTest.php b/vendor/symfony/routing/Tests/RequestContextTest.php new file mode 100644 index 0000000..ffe29d1 --- /dev/null +++ b/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/vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php b/vendor/symfony/routing/Tests/RouteCollectionBuilderTest.php new file mode 100644 index 0000000..76a042d --- /dev/null +++ b/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->assertCount(2, $routes); + $this->assertEquals('/other/a', $routes['a']->getPath()); + $this->assertEquals('/other/b', $routes['b']->getPath()); + } +} diff --git a/vendor/symfony/routing/Tests/RouteCollectionTest.php b/vendor/symfony/routing/Tests/RouteCollectionTest.php new file mode 100644 index 0000000..dd47732 --- /dev/null +++ b/vendor/symfony/routing/Tests/RouteCollectionTest.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\Routing\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +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()); + } + + public function testAddNamePrefix() + { + $collection = new RouteCollection(); + $collection->add('foo', $foo = new Route('/foo')); + $collection->add('bar', $bar = new Route('/bar')); + $collection->add('api_foo', $apiFoo = new Route('/api/foo')); + $collection->addNamePrefix('api_'); + + $this->assertEquals($foo, $collection->get('api_foo')); + $this->assertEquals($bar, $collection->get('api_bar')); + $this->assertEquals($apiFoo, $collection->get('api_api_foo')); + $this->assertNull($collection->get('foo')); + $this->assertNull($collection->get('bar')); + } + + public function testAddNamePrefixCanonicalRouteName() + { + $collection = new RouteCollection(); + $collection->add('foo', new Route('/foo', array('_canonical_route' => 'foo'))); + $collection->add('bar', new Route('/bar', array('_canonical_route' => 'bar'))); + $collection->add('api_foo', new Route('/api/foo', array('_canonical_route' => 'api_foo'))); + $collection->addNamePrefix('api_'); + + $this->assertEquals('api_foo', $collection->get('api_foo')->getDefault('_canonical_route')); + $this->assertEquals('api_bar', $collection->get('api_bar')->getDefault('_canonical_route')); + $this->assertEquals('api_api_foo', $collection->get('api_api_foo')->getDefault('_canonical_route')); + } +} diff --git a/vendor/symfony/routing/Tests/RouteCompilerTest.php b/vendor/symfony/routing/Tests/RouteCompilerTest.php new file mode 100644 index 0000000..04bcdac --- /dev/null +++ b/vendor/symfony/routing/Tests/RouteCompilerTest.php @@ -0,0 +1,408 @@ + + * + * 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$#sD', array(), array( + array('text', '/foo'), + ), + ), + + array( + 'Route with a variable', + array('/foo/{bar}'), + '/foo', '#^/foo/(?P[^/]++)$#sD', 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[^/]++))?$#sD', array('bar'), array( + array('variable', '/', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with several variables', + array('/foo/{bar}/{foobar}'), + '/foo', '#^/foo/(?P[^/]++)/(?P[^/]++)$#sD', 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[^/]++))?)?$#sD', 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[^/]++)$#sD', 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[^/]++)?$#sD', array('bar'), array( + array('variable', '/', '[^/]++', 'bar'), + ), + ), + + array( + 'Route with a requirement of 0', + array('/{bar}', array('bar' => null), array('bar' => '0')), + '', '#^/(?P0)?$#sD', 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))?$#sD', array('bar'), array( + array('variable', '/', '(?:foo|bar)', 'bar'), + ), + ), + + array( + 'Route with only optional variables', + array('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar')), + '', '#^/(?P[^/]++)?(?:/(?P[^/]++))?$#sD', array('foo', 'bar'), array( + array('variable', '/', '[^/]++', 'bar'), + array('variable', '/', '[^/]++', 'foo'), + ), + ), + + array( + 'Route with a variable in last position', + array('/foo-{bar}'), + '/foo-', '#^/foo\-(?P[^/]++)$#sD', array('bar'), array( + array('variable', '-', '[^/]++', 'bar'), + array('text', '/foo'), + ), + ), + + array( + 'Route with nested placeholders', + array('/{static{var}static}'), + '/{static', '#^/\{static(?P[^/]+)static\}$#sD', 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>[^/]++))?)?$#sD', 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>[^/]++)$#sD', 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$#sD", 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.)?$#sDu', array('bar'), array( + array('variable', '/', '.', 'bar', true), + ), + ), + ); + } + + /** + * @dataProvider provideCompileImplicitUtf8Data + * @expectedException \LogicException + */ + 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é$#sDu', array(), array( + array('text', '/foé'), + ), + 'patterns', + ), + + array( + 'Route with an implicit UTF-8 requirement', + array('/{bar}', array('bar' => null), array('bar' => 'é')), + '', '#^/(?Pé)?$#sDu', 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)?$#sDu', 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>[^/]++)$#sDu', 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$#sD', array(), array(), array( + array('text', '/hello'), + ), + '#^www\.example\.com$#sDi', 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[^/]++)$#sD', array('tld', 'name'), array('name'), array( + array('variable', '/', '[^/]++', 'name'), + array('text', '/hello'), + ), + '#^www\.example\.(?P[^\.]++)$#sDi', 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$#sD', array('locale', 'tld'), array(), array( + array('text', '/hello'), + ), + '#^(?P[^\.]++)\.example\.(?P[^\.]++)$#sDi', 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$#sD', array('locale', 'tld'), array(), array( + array('text', '/hello'), + ), + '#^(?P[^\.]++)\.example\.(?P[^\.]++)$#sDi', 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(); + } + + /** + * @dataProvider provideRemoveCapturingGroup + */ + public function testRemoveCapturingGroup($regex, $requirement) + { + $route = new Route('/{foo}', array(), array('foo' => $requirement)); + + $this->assertSame($regex, $route->compile()->getRegex()); + } + + public function provideRemoveCapturingGroup() + { + yield array('#^/(?Pa(?:b|c)(?:d|e)f)$#sD', 'a(b|c)(d|e)f'); + yield array('#^/(?Pa\(b\)c)$#sD', 'a\(b\)c'); + yield array('#^/(?P(?:b))$#sD', '(?:b)'); + yield array('#^/(?P(?(b)b))$#sD', '(?(b)b)'); + yield array('#^/(?P(*F))$#sD', '(*F)'); + yield array('#^/(?P(?:(?:foo)))$#sD', '((foo))'); + } +} + +class Utf8RouteCompiler extends RouteCompiler +{ + const SEPARATORS = '/§'; +} diff --git a/vendor/symfony/routing/Tests/RouteTest.php b/vendor/symfony/routing/Tests/RouteTest.php new file mode 100644 index 0000000..e28cdaf --- /dev/null +++ b/vendor/symfony/routing/Tests/RouteTest.php @@ -0,0 +1,274 @@ + + * + * 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); + } + + public function testInlineDefaultAndRequirement() + { + $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null), new Route('/foo/{bar?}')); + $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}')); + $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?baz}')); + $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', 'baz'), new Route('/foo/{bar?}', array('bar' => 'baz'))); + + $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>}')); + $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '>'), new Route('/foo/{bar<>>}')); + $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '\d+'), new Route('/foo/{bar<.*>}', array(), array('bar' => '\d+'))); + $this->assertEquals((new Route('/foo/{bar}'))->setRequirement('bar', '[a-z]{2}'), new Route('/foo/{bar<[a-z]{2}>}')); + + $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', null)->setRequirement('bar', '.*'), new Route('/foo/{bar<.*>?}')); + $this->assertEquals((new Route('/foo/{bar}'))->setDefault('bar', '<>')->setRequirement('bar', '>'), new Route('/foo/{bar<>>?<>}')); + } + + /** + * 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":936:{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":571:{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:31:"#^/prefix(?:/(?P\d+))?$#sD";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:40:"#^(?P[^\.]++)\.example\.net$#sDi";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/vendor/symfony/routing/Tests/RouterTest.php b/vendor/symfony/routing/Tests/RouterTest.php new file mode 100644 index 0000000..e01bd9c --- /dev/null +++ b/vendor/symfony/routing/Tests/RouterTest.php @@ -0,0 +1,163 @@ + + * + * 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\RouteCollection; +use Symfony\Component\Routing\Router; + +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 = new RouteCollection(); + + $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(new RouteCollection())); + + $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(new RouteCollection())); + + $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/vendor/symfony/routing/composer.json b/vendor/symfony/routing/composer.json new file mode 100644 index 0000000..052b0e1 --- /dev/null +++ b/vendor/symfony/routing/composer.json @@ -0,0 +1,55 @@ +{ + "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": "^7.1.3" + }, + "require-dev": { + "symfony/config": "~3.4|~4.0", + "symfony/http-foundation": "~3.4|~4.0", + "symfony/yaml": "~3.4|~4.0", + "symfony/expression-language": "~3.4|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "doctrine/annotations": "~1.0", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<3.4", + "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": "4.1-dev" + } + } +} diff --git a/vendor/symfony/routing/phpunit.xml.dist b/vendor/symfony/routing/phpunit.xml.dist new file mode 100644 index 0000000..bcc0959 --- /dev/null +++ b/vendor/symfony/routing/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + diff --git a/vendor/webmozart/assert/.composer-auth.json b/vendor/webmozart/assert/.composer-auth.json new file mode 100644 index 0000000..eea8001 --- /dev/null +++ b/vendor/webmozart/assert/.composer-auth.json @@ -0,0 +1,7 @@ +{ + "github-oauth": { + "github.com": "PLEASE DO NOT USE THIS TOKEN IN YOUR OWN PROJECTS/FORKS", + "github.com": "This token is reserved for testing the webmozart/* repositories", + "github.com": "a9debbffdd953ee9b3b82dbc3b807cde2086bb86" + } +} diff --git a/vendor/webmozart/assert/.styleci.yml b/vendor/webmozart/assert/.styleci.yml new file mode 100644 index 0000000..c2ad4a5 --- /dev/null +++ b/vendor/webmozart/assert/.styleci.yml @@ -0,0 +1,11 @@ +preset: symfony + +finder: + exclude: + - "tests" + +enabled: + - ordered_use + +disabled: + - phpdoc_annotation_without_dot # This is still buggy: https://github.com/symfony/symfony/pull/19198 diff --git a/vendor/webmozart/assert/CHANGELOG.md b/vendor/webmozart/assert/CHANGELOG.md new file mode 100644 index 0000000..65fbc8f --- /dev/null +++ b/vendor/webmozart/assert/CHANGELOG.md @@ -0,0 +1,53 @@ +Changelog +========= + +## UNRELEASED + +### Added + +* added `Assert::minCount()` +* added `Assert::maxCount()` +* added `Assert::countBetween()` +* added `Assert::isCountable()` +* added `Assert::notWhitespaceOnly()` +* added `Assert::natural()` +* added `Assert::notContains()` +* added `Assert::isArrayAccessible()` +* added `Assert::isInstanceOfAny()` +* added `Assert::isIterable()` + +### Fixed + +* `stringNotEmpty` will no longer report "0" is an empty string + +## 1.2.0 (2016-11-23) + + * added `Assert::throws()` + * added `Assert::count()` + * added extension point `Assert::reportInvalidArgument()` for custom subclasses + +## 1.1.0 (2016-08-09) + + * added `Assert::object()` + * added `Assert::propertyExists()` + * added `Assert::propertyNotExists()` + * added `Assert::methodExists()` + * added `Assert::methodNotExists()` + * added `Assert::uuid()` + +## 1.0.2 (2015-08-24) + + * integrated Style CI + * add tests for minimum package dependencies on Travis CI + +## 1.0.1 (2015-05-12) + + * added support for PHP 5.3.3 + +## 1.0.0 (2015-05-12) + + * first stable release + +## 1.0.0-beta (2015-03-19) + + * first beta release diff --git a/vendor/webmozart/assert/LICENSE b/vendor/webmozart/assert/LICENSE new file mode 100644 index 0000000..9e2e307 --- /dev/null +++ b/vendor/webmozart/assert/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Bernhard Schussek + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/webmozart/assert/README.md b/vendor/webmozart/assert/README.md new file mode 100644 index 0000000..1c1ede7 --- /dev/null +++ b/vendor/webmozart/assert/README.md @@ -0,0 +1,252 @@ +Webmozart Assert +================ + +[![Build Status](https://travis-ci.org/webmozart/assert.svg?branch=master)](https://travis-ci.org/webmozart/assert) +[![Build status](https://ci.appveyor.com/api/projects/status/lyg83bcsisrr94se/branch/master?svg=true)](https://ci.appveyor.com/project/webmozart/assert/branch/master) +[![Latest Stable Version](https://poser.pugx.org/webmozart/assert/v/stable.svg)](https://packagist.org/packages/webmozart/assert) +[![Total Downloads](https://poser.pugx.org/webmozart/assert/downloads.svg)](https://packagist.org/packages/webmozart/assert) +[![Dependency Status](https://www.versioneye.com/php/webmozart:assert/1.2.0/badge.svg)](https://www.versioneye.com/php/webmozart:assert/1.2.0) + +Latest release: [1.2.0](https://packagist.org/packages/webmozart/assert#1.2.0) + +PHP >= 5.3.9 + +This library contains efficient assertions to test the input and output of +your methods. With these assertions, you can greatly reduce the amount of coding +needed to write a safe implementation. + +All assertions in the [`Assert`] class throw an `\InvalidArgumentException` if +they fail. + +FAQ +--- + +**What's the difference to [beberlei/assert]?** + +This library is heavily inspired by Benjamin Eberlei's wonderful [assert package], +but fixes a usability issue with error messages that can't be fixed there without +breaking backwards compatibility. + +This package features usable error messages by default. However, you can also +easily write custom error messages: + +``` +Assert::string($path, 'The path is expected to be a string. Got: %s'); +``` + +In [beberlei/assert], the ordering of the `%s` placeholders is different for +every assertion. This package, on the contrary, provides consistent placeholder +ordering for all assertions: + +* `%s`: The tested value as string, e.g. `"/foo/bar"`. +* `%2$s`, `%3$s`, ...: Additional assertion-specific values, e.g. the + minimum/maximum length, allowed values, etc. + +Check the source code of the assertions to find out details about the additional +available placeholders. + +Installation +------------ + +Use [Composer] to install the package: + +``` +$ composer require webmozart/assert +``` + +Example +------- + +```php +use Webmozart\Assert\Assert; + +class Employee +{ + public function __construct($id) + { + Assert::integer($id, 'The employee ID must be an integer. Got: %s'); + Assert::greaterThan($id, 0, 'The employee ID must be a positive integer. Got: %s'); + } +} +``` + +If you create an employee with an invalid ID, an exception is thrown: + +```php +new Employee('foobar'); +// => InvalidArgumentException: +// The employee ID must be an integer. Got: string + +new Employee(-10); +// => InvalidArgumentException: +// The employee ID must be a positive integer. Got: -10 +``` + +Assertions +---------- + +The [`Assert`] class provides the following assertions: + +### Type Assertions + +Method | Description +-------------------------------------------------------- | -------------------------------------------------- +`string($value, $message = '')` | Check that a value is a string +`stringNotEmpty($value, $message = '')` | Check that a value is a non-empty string +`integer($value, $message = '')` | Check that a value is an integer +`integerish($value, $message = '')` | Check that a value casts to an integer +`float($value, $message = '')` | Check that a value is a float +`numeric($value, $message = '')` | Check that a value is numeric +`natural($value, $message= ''')` | Check that a value is a non-negative integer +`boolean($value, $message = '')` | Check that a value is a boolean +`scalar($value, $message = '')` | Check that a value is a scalar +`object($value, $message = '')` | Check that a value is an object +`resource($value, $type = null, $message = '')` | Check that a value is a resource +`isCallable($value, $message = '')` | Check that a value is a callable +`isArray($value, $message = '')` | Check that a value is an array +`isTraversable($value, $message = '')` (deprecated) | Check that a value is an array or a `\Traversable` +`isIterable($value, $message = '')` | Check that a value is an array or a `\Traversable` +`isCountable($value, $message = '')` | Check that a value is an array or a `\Countable` +`isInstanceOf($value, $class, $message = '')` | Check that a value is an `instanceof` a class +`isInstanceOfAny($value, array $classes, $message = '')` | Check that a value is an `instanceof` a at least one class on the array of classes +`notInstanceOf($value, $class, $message = '')` | Check that a value is not an `instanceof` a class +`isArrayAccessible($value, $message = '')` | Check that a value can be accessed as an array + +### Comparison Assertions + +Method | Description +----------------------------------------------- | -------------------------------------------------- +`true($value, $message = '')` | Check that a value is `true` +`false($value, $message = '')` | Check that a value is `false` +`null($value, $message = '')` | Check that a value is `null` +`notNull($value, $message = '')` | Check that a value is not `null` +`isEmpty($value, $message = '')` | Check that a value is `empty()` +`notEmpty($value, $message = '')` | Check that a value is not `empty()` +`eq($value, $value2, $message = '')` | Check that a value equals another (`==`) +`notEq($value, $value2, $message = '')` | Check that a value does not equal another (`!=`) +`same($value, $value2, $message = '')` | Check that a value is identical to another (`===`) +`notSame($value, $value2, $message = '')` | Check that a value is not identical to another (`!==`) +`greaterThan($value, $value2, $message = '')` | Check that a value is greater than another +`greaterThanEq($value, $value2, $message = '')` | Check that a value is greater than or equal to another +`lessThan($value, $value2, $message = '')` | Check that a value is less than another +`lessThanEq($value, $value2, $message = '')` | Check that a value is less than or equal to another +`range($value, $min, $max, $message = '')` | Check that a value is within a range +`oneOf($value, array $values, $message = '')` | Check that a value is one of a list of values + +### String Assertions + +You should check that a value is a string with `Assert::string()` before making +any of the following assertions. + +Method | Description +--------------------------------------------------- | ----------------------------------------------------------------- +`contains($value, $subString, $message = '')` | Check that a string contains a substring +`notContains($value, $subString, $message = '')` | Check that a string does not contains a substring +`startsWith($value, $prefix, $message = '')` | Check that a string has a prefix +`startsWithLetter($value, $message = '')` | Check that a string starts with a letter +`endsWith($value, $suffix, $message = '')` | Check that a string has a suffix +`regex($value, $pattern, $message = '')` | Check that a string matches a regular expression +`alpha($value, $message = '')` | Check that a string contains letters only +`digits($value, $message = '')` | Check that a string contains digits only +`alnum($value, $message = '')` | Check that a string contains letters and digits only +`lower($value, $message = '')` | Check that a string contains lowercase characters only +`upper($value, $message = '')` | Check that a string contains uppercase characters only +`length($value, $length, $message = '')` | Check that a string has a certain number of characters +`minLength($value, $min, $message = '')` | Check that a string has at least a certain number of characters +`maxLength($value, $max, $message = '')` | Check that a string has at most a certain number of characters +`lengthBetween($value, $min, $max, $message = '')` | Check that a string has a length in the given range +`uuid($value, $message = '')` | Check that a string is a valid UUID +`notWhitespaceOnly($value, $message = '')` | Check that a string contains at least one non-whitespace character + +### File Assertions + +Method | Description +----------------------------------- | -------------------------------------------------- +`fileExists($value, $message = '')` | Check that a value is an existing path +`file($value, $message = '')` | Check that a value is an existing file +`directory($value, $message = '')` | Check that a value is an existing directory +`readable($value, $message = '')` | Check that a value is a readable path +`writable($value, $message = '')` | Check that a value is a writable path + +### Object Assertions + +Method | Description +----------------------------------------------------- | -------------------------------------------------- +`classExists($value, $message = '')` | Check that a value is an existing class name +`subclassOf($value, $class, $message = '')` | Check that a class is a subclass of another +`implementsInterface($value, $class, $message = '')` | Check that a class implements an interface +`propertyExists($value, $property, $message = '')` | Check that a property exists in a class/object +`propertyNotExists($value, $property, $message = '')` | Check that a property does not exist in a class/object +`methodExists($value, $method, $message = '')` | Check that a method exists in a class/object +`methodNotExists($value, $method, $message = '')` | Check that a method does not exist in a class/object + +### Array Assertions + +Method | Description +-------------------------------------------------- | ------------------------------------------------------------------ +`keyExists($array, $key, $message = '')` | Check that a key exists in an array +`keyNotExists($array, $key, $message = '')` | Check that a key does not exist in an array +`count($array, $number, $message = '')` | Check that an array contains a specific number of elements +`minCount($array, $min, $message = '')` | Check that an array contains at least a certain number of elements +`maxCount($array, $max, $message = '')` | Check that an array contains at most a certain number of elements +`countBetween($array, $min, $max, $message = '')` | Check that an array has a count in the given range + +### Function Assertions + +Method | Description +------------------------------------------- | ----------------------------------------------------------------------------------------------------- +`throws($closure, $class, $message = '')` | Check that a function throws a certain exception. Subclasses of the exception class will be accepted. + +### Collection Assertions + +All of the above assertions can be prefixed with `all*()` to test the contents +of an array or a `\Traversable`: + +```php +Assert::allIsInstanceOf($employees, 'Acme\Employee'); +``` + +### Nullable Assertions + +All of the above assertions can be prefixed with `nullOr*()` to run the +assertion only if it the value is not `null`: + +```php +Assert::nullOrString($middleName, 'The middle name must be a string or null. Got: %s'); +``` + +Authors +------- + +* [Bernhard Schussek] a.k.a. [@webmozart] +* [The Community Contributors] + +Contribute +---------- + +Contributions to the package are always welcome! + +* Report any bugs or issues you find on the [issue tracker]. +* You can grab the source code at the package's [Git repository]. + +Support +------- + +If you are having problems, send a mail to bschussek@gmail.com or shout out to +[@webmozart] on Twitter. + +License +------- + +All contents of this package are licensed under the [MIT license]. + +[beberlei/assert]: https://github.com/beberlei/assert +[assert package]: https://github.com/beberlei/assert +[Composer]: https://getcomposer.org +[Bernhard Schussek]: http://webmozarts.com +[The Community Contributors]: https://github.com/webmozart/assert/graphs/contributors +[issue tracker]: https://github.com/webmozart/assert/issues +[Git repository]: https://github.com/webmozart/assert +[@webmozart]: https://twitter.com/webmozart +[MIT license]: LICENSE +[`Assert`]: src/Assert.php diff --git a/vendor/webmozart/assert/composer.json b/vendor/webmozart/assert/composer.json new file mode 100644 index 0000000..c49e623 --- /dev/null +++ b/vendor/webmozart/assert/composer.json @@ -0,0 +1,34 @@ +{ + "name": "webmozart/assert", + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": ["assert", "check", "validate"], + "license": "MIT", + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "Webmozart\\Assert\\Tests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + } +} diff --git a/vendor/webmozart/assert/src/Assert.php b/vendor/webmozart/assert/src/Assert.php new file mode 100644 index 0000000..e2f89c0 --- /dev/null +++ b/vendor/webmozart/assert/src/Assert.php @@ -0,0 +1,1087 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Webmozart\Assert; + +use ArrayAccess; +use BadMethodCallException; +use Closure; +use Countable; +use Exception; +use InvalidArgumentException; +use Throwable; +use Traversable; + +/** + * Efficient assertions to validate the input/output of your methods. + * + * @method static void nullOrString($value, $message = '') + * @method static void nullOrStringNotEmpty($value, $message = '') + * @method static void nullOrInteger($value, $message = '') + * @method static void nullOrIntegerish($value, $message = '') + * @method static void nullOrFloat($value, $message = '') + * @method static void nullOrNumeric($value, $message = '') + * @method static void nullOrBoolean($value, $message = '') + * @method static void nullOrScalar($value, $message = '') + * @method static void nullOrObject($value, $message = '') + * @method static void nullOrResource($value, $type = null, $message = '') + * @method static void nullOrIsCallable($value, $message = '') + * @method static void nullOrIsArray($value, $message = '') + * @method static void nullOrIsTraversable($value, $message = '') + * @method static void nullOrIsArrayAccessible($value, $message = '') + * @method static void nullOrIsCountable($value, $message = '') + * @method static void nullOrIsInstanceOf($value, $class, $message = '') + * @method static void nullOrNotInstanceOf($value, $class, $message = '') + * @method static void nullOrIsInstanceOfAny($value, $classes, $message = '') + * @method static void nullOrIsEmpty($value, $message = '') + * @method static void nullOrNotEmpty($value, $message = '') + * @method static void nullOrTrue($value, $message = '') + * @method static void nullOrFalse($value, $message = '') + * @method static void nullOrEq($value, $value2, $message = '') + * @method static void nullOrNotEq($value,$value2, $message = '') + * @method static void nullOrSame($value, $value2, $message = '') + * @method static void nullOrNotSame($value, $value2, $message = '') + * @method static void nullOrGreaterThan($value, $value2, $message = '') + * @method static void nullOrGreaterThanEq($value, $value2, $message = '') + * @method static void nullOrLessThan($value, $value2, $message = '') + * @method static void nullOrLessThanEq($value, $value2, $message = '') + * @method static void nullOrRange($value, $min, $max, $message = '') + * @method static void nullOrOneOf($value, $values, $message = '') + * @method static void nullOrContains($value, $subString, $message = '') + * @method static void nullOrNotContains($value, $subString, $message = '') + * @method static void nullOrNotWhitespaceOnly($value, $message = '') + * @method static void nullOrStartsWith($value, $prefix, $message = '') + * @method static void nullOrStartsWithLetter($value, $message = '') + * @method static void nullOrEndsWith($value, $suffix, $message = '') + * @method static void nullOrRegex($value, $pattern, $message = '') + * @method static void nullOrAlpha($value, $message = '') + * @method static void nullOrDigits($value, $message = '') + * @method static void nullOrAlnum($value, $message = '') + * @method static void nullOrLower($value, $message = '') + * @method static void nullOrUpper($value, $message = '') + * @method static void nullOrLength($value, $length, $message = '') + * @method static void nullOrMinLength($value, $min, $message = '') + * @method static void nullOrMaxLength($value, $max, $message = '') + * @method static void nullOrLengthBetween($value, $min, $max, $message = '') + * @method static void nullOrFileExists($value, $message = '') + * @method static void nullOrFile($value, $message = '') + * @method static void nullOrDirectory($value, $message = '') + * @method static void nullOrReadable($value, $message = '') + * @method static void nullOrWritable($value, $message = '') + * @method static void nullOrClassExists($value, $message = '') + * @method static void nullOrSubclassOf($value, $class, $message = '') + * @method static void nullOrImplementsInterface($value, $interface, $message = '') + * @method static void nullOrPropertyExists($value, $property, $message = '') + * @method static void nullOrPropertyNotExists($value, $property, $message = '') + * @method static void nullOrMethodExists($value, $method, $message = '') + * @method static void nullOrMethodNotExists($value, $method, $message = '') + * @method static void nullOrKeyExists($value, $key, $message = '') + * @method static void nullOrKeyNotExists($value, $key, $message = '') + * @method static void nullOrCount($value, $key, $message = '') + * @method static void nullOrMinCount($value, $min, $message = '') + * @method static void nullOrMaxCount($value, $max, $message = '') + * @method static void nullCountBetween($value, $min, $max, $message = '') + * @method static void nullOrUuid($values, $message = '') + * @method static void allString($values, $message = '') + * @method static void allStringNotEmpty($values, $message = '') + * @method static void allInteger($values, $message = '') + * @method static void allIntegerish($values, $message = '') + * @method static void allFloat($values, $message = '') + * @method static void allNumeric($values, $message = '') + * @method static void allBoolean($values, $message = '') + * @method static void allScalar($values, $message = '') + * @method static void allObject($values, $message = '') + * @method static void allResource($values, $type = null, $message = '') + * @method static void allIsCallable($values, $message = '') + * @method static void allIsArray($values, $message = '') + * @method static void allIsTraversable($values, $message = '') + * @method static void allIsArrayAccessible($values, $message = '') + * @method static void allIsCountable($values, $message = '') + * @method static void allIsInstanceOf($values, $class, $message = '') + * @method static void allNotInstanceOf($values, $class, $message = '') + * @method static void allIsInstanceOfAny($values, $classes, $message = '') + * @method static void allNull($values, $message = '') + * @method static void allNotNull($values, $message = '') + * @method static void allIsEmpty($values, $message = '') + * @method static void allNotEmpty($values, $message = '') + * @method static void allTrue($values, $message = '') + * @method static void allFalse($values, $message = '') + * @method static void allEq($values, $value2, $message = '') + * @method static void allNotEq($values,$value2, $message = '') + * @method static void allSame($values, $value2, $message = '') + * @method static void allNotSame($values, $value2, $message = '') + * @method static void allGreaterThan($values, $value2, $message = '') + * @method static void allGreaterThanEq($values, $value2, $message = '') + * @method static void allLessThan($values, $value2, $message = '') + * @method static void allLessThanEq($values, $value2, $message = '') + * @method static void allRange($values, $min, $max, $message = '') + * @method static void allOneOf($values, $values, $message = '') + * @method static void allContains($values, $subString, $message = '') + * @method static void allNotContains($values, $subString, $message = '') + * @method static void allNotWhitespaceOnly($values, $message = '') + * @method static void allStartsWith($values, $prefix, $message = '') + * @method static void allStartsWithLetter($values, $message = '') + * @method static void allEndsWith($values, $suffix, $message = '') + * @method static void allRegex($values, $pattern, $message = '') + * @method static void allAlpha($values, $message = '') + * @method static void allDigits($values, $message = '') + * @method static void allAlnum($values, $message = '') + * @method static void allLower($values, $message = '') + * @method static void allUpper($values, $message = '') + * @method static void allLength($values, $length, $message = '') + * @method static void allMinLength($values, $min, $message = '') + * @method static void allMaxLength($values, $max, $message = '') + * @method static void allLengthBetween($values, $min, $max, $message = '') + * @method static void allFileExists($values, $message = '') + * @method static void allFile($values, $message = '') + * @method static void allDirectory($values, $message = '') + * @method static void allReadable($values, $message = '') + * @method static void allWritable($values, $message = '') + * @method static void allClassExists($values, $message = '') + * @method static void allSubclassOf($values, $class, $message = '') + * @method static void allImplementsInterface($values, $interface, $message = '') + * @method static void allPropertyExists($values, $property, $message = '') + * @method static void allPropertyNotExists($values, $property, $message = '') + * @method static void allMethodExists($values, $method, $message = '') + * @method static void allMethodNotExists($values, $method, $message = '') + * @method static void allKeyExists($values, $key, $message = '') + * @method static void allKeyNotExists($values, $key, $message = '') + * @method static void allCount($values, $key, $message = '') + * @method static void allMinCount($values, $min, $message = '') + * @method static void allMaxCount($values, $max, $message = '') + * @method static void allCountBetween($values, $min, $max, $message = '') + * @method static void allUuid($values, $message = '') + * + * @since 1.0 + * + * @author Bernhard Schussek + */ +class Assert +{ + public static function string($value, $message = '') + { + if (!is_string($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a string. Got: %s', + static::typeToString($value) + )); + } + } + + public static function stringNotEmpty($value, $message = '') + { + static::string($value, $message); + static::notEq($value, '', $message); + } + + public static function integer($value, $message = '') + { + if (!is_int($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an integer. Got: %s', + static::typeToString($value) + )); + } + } + + public static function integerish($value, $message = '') + { + if (!is_numeric($value) || $value != (int) $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an integerish value. Got: %s', + static::typeToString($value) + )); + } + } + + public static function float($value, $message = '') + { + if (!is_float($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a float. Got: %s', + static::typeToString($value) + )); + } + } + + public static function numeric($value, $message = '') + { + if (!is_numeric($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a numeric. Got: %s', + static::typeToString($value) + )); + } + } + + public static function natural($value, $message = '') + { + if (!is_int($value) || $value < 0) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a non-negative integer. Got %s', + static::valueToString($value) + )); + } + } + + public static function boolean($value, $message = '') + { + if (!is_bool($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a boolean. Got: %s', + static::typeToString($value) + )); + } + } + + public static function scalar($value, $message = '') + { + if (!is_scalar($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a scalar. Got: %s', + static::typeToString($value) + )); + } + } + + public static function object($value, $message = '') + { + if (!is_object($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an object. Got: %s', + static::typeToString($value) + )); + } + } + + public static function resource($value, $type = null, $message = '') + { + if (!is_resource($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a resource. Got: %s', + static::typeToString($value) + )); + } + + if ($type && $type !== get_resource_type($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a resource of type %2$s. Got: %s', + static::typeToString($value), + $type + )); + } + } + + public static function isCallable($value, $message = '') + { + if (!is_callable($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a callable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isArray($value, $message = '') + { + if (!is_array($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isTraversable($value, $message = '') + { + @trigger_error( + sprintf( + 'The "%s" assertion is deprecated. You should stop using it, as it will soon be removed in 2.0 version. Use "isIterable" or "isInstanceOf" instead.', + __METHOD__ + ), + E_USER_DEPRECATED + ); + + if (!is_array($value) && !($value instanceof Traversable)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a traversable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isArrayAccessible($value, $message = '') + { + if (!is_array($value) && !($value instanceof ArrayAccess)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array accessible. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isCountable($value, $message = '') + { + if (!is_array($value) && !($value instanceof Countable)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a countable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isIterable($value, $message = '') + { + if (!is_array($value) && !($value instanceof Traversable)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an iterable. Got: %s', + static::typeToString($value) + )); + } + } + + public static function isInstanceOf($value, $class, $message = '') + { + if (!($value instanceof $class)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an instance of %2$s. Got: %s', + static::typeToString($value), + $class + )); + } + } + + public static function notInstanceOf($value, $class, $message = '') + { + if ($value instanceof $class) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an instance other than %2$s. Got: %s', + static::typeToString($value), + $class + )); + } + } + + public static function isInstanceOfAny($value, array $classes, $message = '') + { + foreach ($classes as $class) { + if ($value instanceof $class) { + return; + } + } + + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an instance of any of %2$s. Got: %s', + static::typeToString($value), + implode(', ', array_map(array('static', 'valueToString'), $classes)) + )); + } + + public static function isEmpty($value, $message = '') + { + if (!empty($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an empty value. Got: %s', + static::valueToString($value) + )); + } + } + + public static function notEmpty($value, $message = '') + { + if (empty($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a non-empty value. Got: %s', + static::valueToString($value) + )); + } + } + + public static function null($value, $message = '') + { + if (null !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected null. Got: %s', + static::valueToString($value) + )); + } + } + + public static function notNull($value, $message = '') + { + if (null === $value) { + static::reportInvalidArgument( + $message ?: 'Expected a value other than null.' + ); + } + } + + public static function true($value, $message = '') + { + if (true !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be true. Got: %s', + static::valueToString($value) + )); + } + } + + public static function false($value, $message = '') + { + if (false !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to be false. Got: %s', + static::valueToString($value) + )); + } + } + + public static function eq($value, $value2, $message = '') + { + if ($value2 != $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($value2) + )); + } + } + + public static function notEq($value, $value2, $message = '') + { + if ($value2 == $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a different value than %s.', + static::valueToString($value2) + )); + } + } + + public static function same($value, $value2, $message = '') + { + if ($value2 !== $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value identical to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($value2) + )); + } + } + + public static function notSame($value, $value2, $message = '') + { + if ($value2 === $value) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value not identical to %s.', + static::valueToString($value2) + )); + } + } + + public static function greaterThan($value, $limit, $message = '') + { + if ($value <= $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value greater than %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function greaterThanEq($value, $limit, $message = '') + { + if ($value < $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value greater than or equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function lessThan($value, $limit, $message = '') + { + if ($value >= $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value less than %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function lessThanEq($value, $limit, $message = '') + { + if ($value > $limit) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value less than or equal to %2$s. Got: %s', + static::valueToString($value), + static::valueToString($limit) + )); + } + } + + public static function range($value, $min, $max, $message = '') + { + if ($value < $min || $value > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value between %2$s and %3$s. Got: %s', + static::valueToString($value), + static::valueToString($min), + static::valueToString($max) + )); + } + } + + public static function oneOf($value, array $values, $message = '') + { + if (!in_array($value, $values, true)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected one of: %2$s. Got: %s', + static::valueToString($value), + implode(', ', array_map(array('static', 'valueToString'), $values)) + )); + } + } + + public static function contains($value, $subString, $message = '') + { + if (false === strpos($value, $subString)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain %2$s. Got: %s', + static::valueToString($value), + static::valueToString($subString) + )); + } + } + + public static function notContains($value, $subString, $message = '') + { + if (false !== strpos($value, $subString)) { + static::reportInvalidArgument(sprintf( + $message ?: '%2$s was not expected to be contained in a value. Got: %s', + static::valueToString($value), + static::valueToString($subString) + )); + } + } + + public static function notWhitespaceOnly($value, $message = '') + { + if (preg_match('/^\s*$/', $value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a non-whitespace string. Got: %s', + static::valueToString($value) + )); + } + } + + public static function startsWith($value, $prefix, $message = '') + { + if (0 !== strpos($value, $prefix)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to start with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($prefix) + )); + } + } + + public static function startsWithLetter($value, $message = '') + { + $valid = isset($value[0]); + + if ($valid) { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = ctype_alpha($value[0]); + setlocale(LC_CTYPE, $locale); + } + + if (!$valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to start with a letter. Got: %s', + static::valueToString($value) + )); + } + } + + public static function endsWith($value, $suffix, $message = '') + { + if ($suffix !== substr($value, -static::strlen($suffix))) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to end with %2$s. Got: %s', + static::valueToString($value), + static::valueToString($suffix) + )); + } + } + + public static function regex($value, $pattern, $message = '') + { + if (!preg_match($pattern, $value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The value %s does not match the expected pattern.', + static::valueToString($value) + )); + } + } + + public static function alpha($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_alpha($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain only letters. Got: %s', + static::valueToString($value) + )); + } + } + + public static function digits($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_digit($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain digits only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function alnum($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_alnum($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain letters and digits only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function lower($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_lower($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain lowercase characters only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function upper($value, $message = '') + { + $locale = setlocale(LC_CTYPE, 0); + setlocale(LC_CTYPE, 'C'); + $valid = !ctype_upper($value); + setlocale(LC_CTYPE, $locale); + + if ($valid) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain uppercase characters only. Got: %s', + static::valueToString($value) + )); + } + } + + public static function length($value, $length, $message = '') + { + if ($length !== static::strlen($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain %2$s characters. Got: %s', + static::valueToString($value), + $length + )); + } + } + + public static function minLength($value, $min, $message = '') + { + if (static::strlen($value) < $min) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain at least %2$s characters. Got: %s', + static::valueToString($value), + $min + )); + } + } + + public static function maxLength($value, $max, $message = '') + { + if (static::strlen($value) > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain at most %2$s characters. Got: %s', + static::valueToString($value), + $max + )); + } + } + + public static function lengthBetween($value, $min, $max, $message = '') + { + $length = static::strlen($value); + + if ($length < $min || $length > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a value to contain between %2$s and %3$s characters. Got: %s', + static::valueToString($value), + $min, + $max + )); + } + } + + public static function fileExists($value, $message = '') + { + static::string($value); + + if (!file_exists($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The file %s does not exist.', + static::valueToString($value) + )); + } + } + + public static function file($value, $message = '') + { + static::fileExists($value, $message); + + if (!is_file($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is not a file.', + static::valueToString($value) + )); + } + } + + public static function directory($value, $message = '') + { + static::fileExists($value, $message); + + if (!is_dir($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is no directory.', + static::valueToString($value) + )); + } + } + + public static function readable($value, $message = '') + { + if (!is_readable($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is not readable.', + static::valueToString($value) + )); + } + } + + public static function writable($value, $message = '') + { + if (!is_writable($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'The path %s is not writable.', + static::valueToString($value) + )); + } + } + + public static function classExists($value, $message = '') + { + if (!class_exists($value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an existing class name. Got: %s', + static::valueToString($value) + )); + } + } + + public static function subclassOf($value, $class, $message = '') + { + if (!is_subclass_of($value, $class)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected a sub-class of %2$s. Got: %s', + static::valueToString($value), + static::valueToString($class) + )); + } + } + + public static function implementsInterface($value, $interface, $message = '') + { + if (!in_array($interface, class_implements($value))) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an implementation of %2$s. Got: %s', + static::valueToString($value), + static::valueToString($interface) + )); + } + } + + public static function propertyExists($classOrObject, $property, $message = '') + { + if (!property_exists($classOrObject, $property)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the property %s to exist.', + static::valueToString($property) + )); + } + } + + public static function propertyNotExists($classOrObject, $property, $message = '') + { + if (property_exists($classOrObject, $property)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the property %s to not exist.', + static::valueToString($property) + )); + } + } + + public static function methodExists($classOrObject, $method, $message = '') + { + if (!method_exists($classOrObject, $method)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the method %s to exist.', + static::valueToString($method) + )); + } + } + + public static function methodNotExists($classOrObject, $method, $message = '') + { + if (method_exists($classOrObject, $method)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the method %s to not exist.', + static::valueToString($method) + )); + } + } + + public static function keyExists($array, $key, $message = '') + { + if (!array_key_exists($key, $array)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the key %s to exist.', + static::valueToString($key) + )); + } + } + + public static function keyNotExists($array, $key, $message = '') + { + if (array_key_exists($key, $array)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected the key %s to not exist.', + static::valueToString($key) + )); + } + } + + public static function count($array, $number, $message = '') + { + static::eq( + count($array), + $number, + $message ?: sprintf('Expected an array to contain %d elements. Got: %d.', $number, count($array)) + ); + } + + public static function minCount($array, $min, $message = '') + { + if (count($array) < $min) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array to contain at least %2$d elements. Got: %d', + count($array), + $min + )); + } + } + + public static function maxCount($array, $max, $message = '') + { + if (count($array) > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array to contain at most %2$d elements. Got: %d', + count($array), + $max + )); + } + } + + public static function countBetween($array, $min, $max, $message = '') + { + $count = count($array); + + if ($count < $min || $count > $max) { + static::reportInvalidArgument(sprintf( + $message ?: 'Expected an array to contain between %2$d and %3$d elements. Got: %d', + $count, + $min, + $max + )); + } + } + + public static function uuid($value, $message = '') + { + $value = str_replace(array('urn:', 'uuid:', '{', '}'), '', $value); + + // The nil UUID is special form of UUID that is specified to have all + // 128 bits set to zero. + if ('00000000-0000-0000-0000-000000000000' === $value) { + return; + } + + if (!preg_match('/^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$/', $value)) { + static::reportInvalidArgument(sprintf( + $message ?: 'Value %s is not a valid UUID.', + static::valueToString($value) + )); + } + } + + public static function throws(Closure $expression, $class = 'Exception', $message = '') + { + static::string($class); + + $actual = 'none'; + + try { + $expression(); + } catch (Exception $e) { + $actual = get_class($e); + if ($e instanceof $class) { + return; + } + } catch (Throwable $e) { + $actual = get_class($e); + if ($e instanceof $class) { + return; + } + } + + static::reportInvalidArgument($message ?: sprintf( + 'Expected to throw "%s", got "%s"', + $class, + $actual + )); + } + + public static function __callStatic($name, $arguments) + { + if ('nullOr' === substr($name, 0, 6)) { + if (null !== $arguments[0]) { + $method = lcfirst(substr($name, 6)); + call_user_func_array(array('static', $method), $arguments); + } + + return; + } + + if ('all' === substr($name, 0, 3)) { + static::isIterable($arguments[0]); + + $method = lcfirst(substr($name, 3)); + $args = $arguments; + + foreach ($arguments[0] as $entry) { + $args[0] = $entry; + + call_user_func_array(array('static', $method), $args); + } + + return; + } + + throw new BadMethodCallException('No such method: '.$name); + } + + protected static function valueToString($value) + { + if (null === $value) { + return 'null'; + } + + if (true === $value) { + return 'true'; + } + + if (false === $value) { + return 'false'; + } + + if (is_array($value)) { + return 'array'; + } + + if (is_object($value)) { + return get_class($value); + } + + if (is_resource($value)) { + return 'resource'; + } + + if (is_string($value)) { + return '"'.$value.'"'; + } + + return (string) $value; + } + + protected static function typeToString($value) + { + return is_object($value) ? get_class($value) : gettype($value); + } + + protected static function strlen($value) + { + if (!function_exists('mb_detect_encoding')) { + return strlen($value); + } + + if (false === $encoding = mb_detect_encoding($value)) { + return strlen($value); + } + + return mb_strwidth($value, $encoding); + } + + protected static function reportInvalidArgument($message) + { + throw new InvalidArgumentException($message); + } + + private function __construct() + { + } +} diff --git a/vendor/winzou/state-machine/.gitignore b/vendor/winzou/state-machine/.gitignore new file mode 100644 index 0000000..4520b24 --- /dev/null +++ b/vendor/winzou/state-machine/.gitignore @@ -0,0 +1,3 @@ +vendor +bin +composer.lock diff --git a/vendor/winzou/state-machine/.travis.yml b/vendor/winzou/state-machine/.travis.yml new file mode 100644 index 0000000..b0b4612 --- /dev/null +++ b/vendor/winzou/state-machine/.travis.yml @@ -0,0 +1,35 @@ +sudo: false + +branches: + only: + - master + +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - 7.1 + - 7.2 + - hhvm + +matrix: + fast_finish: true + include: + - php: 5.3 + dist: precise + allow_failures: + - php: 5.3 + dist: xenial + - php: 5.3 + dist: trusty + +before_script: + - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi; + - composer --no-interaction --prefer-source install + +script: + - bin/phpspec run -f dot diff --git a/vendor/winzou/state-machine/LICENSE b/vendor/winzou/state-machine/LICENSE new file mode 100644 index 0000000..19b5288 --- /dev/null +++ b/vendor/winzou/state-machine/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Alexandre Bacco + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/winzou/state-machine/README.md b/vendor/winzou/state-machine/README.md new file mode 100644 index 0000000..085025d --- /dev/null +++ b/vendor/winzou/state-machine/README.md @@ -0,0 +1,107 @@ +A very lightweight yet powerful PHP state machine +================================================= + +Define your states, define your transitions and your callbacks: we do the rest. +The era of hard-coded states is over! + +[![Build Status](https://travis-ci.org/winzou/state-machine.svg?branch=master)](https://travis-ci.org/winzou/state-machine) + +Installation (via composer) +--------------- + +```js +{ + "require": { + "winzou/state-machine": "~0.1" + } +} +``` + +Usage +----- + +### Configure a state machine graph + +In order to use the state machine, you first need to define a graph. A graph is a definition of states, transitions and optionnally callbacks ; all attached on an object from your domain. Multiple graphes can be attached to the same object. + +Let's define a graph called *myGraphA* for our `DomainObject` object: + +```php +$config = array( + 'graph' => 'myGraphA', // Name of the current graph - there can be many of them attached to the same object + 'property_path' => 'stateA', // Property path of the object actually holding the state + 'states' => array( + 'checkout', + 'pending', + 'confirmed', + 'cancelled' + ), + 'transitions' => array( + 'create' => array( + 'from' => array('checkout'), + 'to' => 'pending' + ), + 'confirm' => array( + 'from' => array('checkout', 'pending'), + 'to' => 'confirmed' + ), + 'cancel' => array( + 'from' => array('confirmed'), + 'to' => 'cancelled' + ) + ), + 'callbacks' => array( + 'guard' => array( + 'guard-cancel' => array( + 'to' => array('cancelled'), // Will be called only for transitions going to this state + 'do' => function() { var_dump('guarding to cancelled state'); return false; } + ) + ), + 'before' => array( + 'from-checkout' => array( + 'from' => array('checkout'), // Will be called only for transitions coming from this state + 'do' => function() { var_dump('from checkout transition'); } + ) + ), + 'after' => array( + 'on-confirm' => array( + 'on' => array('confirm'), // Will be called only on this transition + 'do' => function() { var_dump('on confirm transition'); } + ), + 'to-cancelled' => array( + 'to' => array('cancelled'), // Will be called only for transitions going to this state + 'do' => function() { var_dump('to cancel transition'); } + ), + 'cancel-date' => array( + 'to' => array('cancelled'), + 'do' => array('object', 'setCancelled'), + ), + ) + ) +); +``` + +So, in the previous example, the graph has 6 possible states, and those can be achieved by applying some transitions to the object. For example, when creating a new `DomainObject`, you would apply the 'create' transition to the object, and after that the state of it would become *pending*. + +### Using the state machine + +#### Definitions + +The state machine is the object actually manipulating your object. By using the state machine you can test if a transition can be applied, actually apply a transition, retrieve the current state, etc. *A state machine is specific to a couple object + graph.* It means that if you want to manipulate another object, or the same object with another graph, *you need another state machine*. + +The factory helps you to get the state machine for these couples object + graph. You give an object and a graph name to it, and it will return you the state machine for this couple. If you want to have this factory as a service in your Symfony2 application, please see the [corresponding StateMachineBundle](https://github.com/winzou/StateMachineBundle). + +#### Usage + +Please refer to the several examples in the `examples` folder. + +#### Callbacks + +Callbacks are used to guard transitions or execute some code before or after applying transitions. + +Guarding callbacks must return a `bool`. If a guard returns `false`, a transition cannot be performed. + + +##### Credits + +This library has been highly inspired by [https://github.com/yohang/Finite](https://github.com/yohang/Finite), but has taken another direction. diff --git a/vendor/winzou/state-machine/composer.json b/vendor/winzou/state-machine/composer.json new file mode 100644 index 0000000..4f9514b --- /dev/null +++ b/vendor/winzou/state-machine/composer.json @@ -0,0 +1,37 @@ +{ + "name": "winzou/state-machine", + "description": "A very lightweight yet powerful PHP state machine", + "keywords": ["statemachine", "state", "event", "callback"], + "homepage": "https://github.com/winzou/StateMachine", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Alexandre Bacco", + "email": "alexandre.bacco@gmail.com", + "homepage": "http://alex.bacco.fr" + } + ], + "require": { + "php": ">=5.3.0", + "symfony/event-dispatcher": "~2.1|~3.0|~4.0", + "symfony/property-access": "~2.1|~3.0|~4.0", + "symfony/expression-language": "~2.4|~3.0|~4.0" + }, + "suggest": { + "twig/twig": "Access the state machine in your twig templates (~1.0)" + }, + "require-dev": { + "phpspec/phpspec": "~2.0", + "twig/twig": "~1.0" + }, + "autoload": { + "psr-0": { "SM": "src/" } + }, + "autoload-dev": { + "psr-4": { "spec\\": "spec/" } + }, + "config": { + "bin-dir": "bin" + } +} diff --git a/vendor/winzou/state-machine/examples/DomainObject.php b/vendor/winzou/state-machine/examples/DomainObject.php new file mode 100644 index 0000000..4707375 --- /dev/null +++ b/vendor/winzou/state-machine/examples/DomainObject.php @@ -0,0 +1,41 @@ +stateA; + } + + public function setStateA($state) + { + $this->stateA = $state; + } + + public function getStateB() + { + return $this->stateB; + } + + public function setStateB($state) + { + $this->stateB = $state; + } + + public function setConfirmedNow() + { + var_dump('I (the object) am set confirmed at '.date('Y-m-d').'.'); + } +} diff --git a/vendor/winzou/state-machine/examples/simple.php b/vendor/winzou/state-machine/examples/simple.php new file mode 100644 index 0000000..db5346c --- /dev/null +++ b/vendor/winzou/state-machine/examples/simple.php @@ -0,0 +1,109 @@ + 'myGraphA', // Name of the current graph - there can be many of them attached to the same object + 'property_path' => 'stateA', // Property path of the object actually holding the state + 'states' => array( + 'checkout', + 'pending', + 'confirmed', + 'cancelled' + ), + 'transitions' => array( + 'create' => array( + 'from' => array('checkout'), + 'to' => 'pending' + ), + 'confirm' => array( + 'from' => array('checkout', 'pending'), + 'to' => 'confirmed' + ), + 'cancel' => array( + 'from' => array('confirmed'), + 'to' => 'cancelled' + ) + ), + 'callbacks' => array( + 'guard' => array( + 'guard-cancel' => array( + 'to' => array('cancelled'), // Will be called only for transitions going to this state + 'do' => function() { var_dump('guarding to cancelled state'); return false; } + ) + ), + 'before' => array( + 'from-checkout' => array( + 'from' => array('checkout'), // Will be called only for transitions coming from this state + 'do' => function() { var_dump('from checkout transition'); } + ) + ), + 'after' => array( + 'on-confirm' => array( + 'on' => array('confirm'), // Will be called only on this transition + 'do' => function() { var_dump('on confirm transition'); } + ), + 'to-cancelled' => array( + 'to' => array('cancelled'), // Will be called only for transitions going to this state + 'do' => function() { var_dump('to cancel transition'); } + ), + 'confirm-date' => array( + 'on' => array('confirm'), + 'do' => array('object', 'setConfirmedNow'), // 'object' will be replaced by the object undergoing the transition + ), + ) + ) +); + +// Our object +$object = new DomainObject; + +// State machine is created being given an object and a config +$stateMachine = new \SM\StateMachine\StateMachine($object, $config); + +// Current state is checkout +var_dump($stateMachine->getState()); + +// Return true, we can apply this transition +var_dump($stateMachine->can('create')); + +// Return true, this transitions is applied +// In addition, callback 'from-checkout' is called +var_dump($stateMachine->apply('create')); + +// Current state is pending +var_dump($stateMachine->getState()); + +// All possible transitions for pending state are just "confirm" +var_dump($stateMachine->getPossibleTransitions()); + +// Return false, this transition is not applied +// 2nd argument is soft mode: it returns false instead of throwing an exception +var_dump($stateMachine->apply('cancel', true)); + +// Current state is still pending +var_dump($stateMachine->getState()); + +// Return true, this transition is applied +// In addition, callback 'on-confirm' is called +// And callback 'confirm-date' calls the method 'setConfirmedNow' on the object itself +var_dump($stateMachine->apply('confirm')); + +// Current state is confirmed +var_dump($stateMachine->getState()); + +// Returns false, as it is guarded +var_dump($stateMachine->can('cancel')); + +// Current state is still confirmed +var_dump($stateMachine->getState()); diff --git a/vendor/winzou/state-machine/spec/SM/Callback/CallbackSpec.php b/vendor/winzou/state-machine/spec/SM/Callback/CallbackSpec.php new file mode 100644 index 0000000..96f693d --- /dev/null +++ b/vendor/winzou/state-machine/spec/SM/Callback/CallbackSpec.php @@ -0,0 +1,163 @@ +getState()->willReturn('checkout'); + + $this->beConstructedWith($this->specs, $this->callable); + } + + function it_is_initializable() + { + $this->shouldHaveType('SM\Callback\Callback'); + } + + function it_satisfies_simple_on(TransitionEvent $event) + { + $specs = array('on' => 'tested-transition'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('dummy'), 'dummy')); + $event->getTransition()->willReturn('tested-transition'); + $event->getState()->willReturn('dummy'); + + $this->isSatisfiedBy($event)->shouldReturn(true); + } + + function it_doesnt_satisfies_simple_on(TransitionEvent $event) + { + $specs = array('on' => 'tested-transition'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('dummy'), 'dummy')); + $event->getTransition()->willReturn('tested-transition-not-matching'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + function it_satisfies_simple_from(TransitionEvent $event) + { + $specs = array('from' => 'tested-state'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state'), 'dummy')); + $event->getTransition()->willReturn('dummy'); + $event->getState()->willReturn('tested-state'); + + $this->isSatisfiedBy($event)->shouldReturn(true); + } + + function it_doesnt_satisfies_simple_from(TransitionEvent $event) + { + $specs = array('from' => 'tested-state'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state-not-matching'), 'dummy')); + $event->getTransition()->willReturn('dummy'); + $event->getState()->willReturn('tested-state-not-matching'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + function it_satisfies_simple_to(TransitionEvent $event) + { + $specs = array('to' => 'tested-state'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('dummy'), 'tested-state')); + $event->getTransition()->willReturn('dummy'); + $event->getState()->willReturn('dummy'); + + $this->isSatisfiedBy($event)->shouldReturn(true); + } + + function it_doesnt_satisfies_simple_to(TransitionEvent $event) + { + $specs = array('from' => 'tested-state'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state-not-matching'), 'dummy')); + $event->getTransition()->willReturn('dummy'); + $event->getState()->willReturn('dummy'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + function it_satisfies_complex_specs(TransitionEvent $event) + { + $specs = array('to' => 'tested-state-to', 'from' => 'tested-state-from', 'on' => 'tested-transition'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state-from'), 'tested-state-to')); + $event->getTransition()->willReturn('tested-transition'); + $event->getState()->willReturn('tested-state-from'); + + $this->isSatisfiedBy($event)->shouldReturn(true); + } + + function it_doesnt_satisfies_wrong_from(TransitionEvent $event) + { + $specs = array('to' => 'tested-state-to', 'from' => 'tested-wrong', 'on' => 'tested-transition'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('dummy'), 'tested-state-to')); + $event->getTransition()->willReturn('tested-transition'); + $event->getState()->willReturn('dummy'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + function it_doesnt_satisfies_wrong_to(TransitionEvent $event) + { + $specs = array('to' => 'tested-wrong', 'from' => 'tested-state-from', 'on' => 'tested-transition'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state-from'), 'dummy')); + $event->getTransition()->willReturn('tested-transition'); + $event->getState()->willReturn('tested-state-from'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + function it_doesnt_satisfies_wrong_on(TransitionEvent $event) + { + $specs = array('to' => 'tested-state-to', 'from' => 'tested-state-from', 'on' => 'tested-wrong'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state-from'), 'tested-state-to')); + $event->getTransition()->willReturn('dummy'); + $event->getState()->willReturn('tested-state-from'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + function it_doesnt_satisfies_excluded_from(TransitionEvent $event) + { + $specs = array('to' => 'tested-state-to', 'excluded_from' => 'tested-state-from'); + $this->beConstructedWith($specs, $this->callable); + + $event->getConfig()->willReturn($this->getConfig(array('tested-state-from'), 'tested-state-to')); + $event->getTransition()->willReturn('dummy'); + $event->getState()->willReturn('tested-state-from'); + + $this->isSatisfiedBy($event)->shouldReturn(false); + } + + protected function getConfig($from = array(), $to) + { + return array('from' => $from, 'to' => $to); + } +} diff --git a/vendor/winzou/state-machine/spec/SM/Callback/CascadeTransitionCallbackSpec.php b/vendor/winzou/state-machine/spec/SM/Callback/CascadeTransitionCallbackSpec.php new file mode 100644 index 0000000..101839c --- /dev/null +++ b/vendor/winzou/state-machine/spec/SM/Callback/CascadeTransitionCallbackSpec.php @@ -0,0 +1,72 @@ +beConstructedWith($factory); + } + + function it_is_initializable() + { + $this->shouldHaveType('SM\Callback\CascadeTransitionCallback'); + } + + function it_applies($factory, TransitionEvent $event, DummyObject $object, StateMachineInterface $sm) + { + $factory->get($object, 'graph')->willReturn($sm); + + $sm->can('transition')->willReturn(true); + $sm->apply('transition', true)->shouldBeCalled(); + + $this->apply($object, $event, 'transition', 'graph'); + } + + function it_applies_with_default_graph( + $factory, + TransitionEvent $event, + DummyObject $object, + StateMachineInterface $sm1, + StateMachineInterface $sm2 + ) { + $event->getStateMachine()->willReturn($sm2); + + $sm2->getGraph()->willReturn('graph'); + + $factory->get($object, 'graph')->willReturn($sm1); + + $sm1->can('transition')->willReturn(true); + $sm1->apply('transition', true)->shouldBeCalled(); + + $this->apply($object, $event, 'transition'); + } + + function it_applies_with_default_graph_and_default_transition( + $factory, + TransitionEvent $event, + DummyObject $object, + StateMachineInterface $sm1, + StateMachineInterface $sm2 + ) { + $event->getStateMachine()->willReturn($sm2); + $event->getTransition()->willReturn('transition'); + + $sm2->getGraph()->willReturn('graph'); + + $factory->get($object, 'graph')->willReturn($sm1); + + $sm1->can('transition')->willReturn(true); + $sm1->apply('transition', true)->shouldBeCalled(); + + $this->apply($object, $event); + } +} diff --git a/vendor/winzou/state-machine/spec/SM/DummyObject.php b/vendor/winzou/state-machine/spec/SM/DummyObject.php new file mode 100644 index 0000000..63b35b6 --- /dev/null +++ b/vendor/winzou/state-machine/spec/SM/DummyObject.php @@ -0,0 +1,16 @@ +beConstructedWith($factory); + $factory->get(new DummyObject(), 'simple')->willReturn($stateMachine); + } + + function it_is_initializable() + { + $this->shouldHaveType('SM\Extension\Twig\SMExtension'); + } + + function it_is_a_twig_extension() + { + $this->shouldBeAnInstanceOf('\Twig_Extension'); + } + + function it_should_have_a_name() + { + $this->getName()->shouldReturn('sm'); + } + + function it_provide_sm_can_function(FactoryInterface $factory, StateMachineInterface $stateMachine) + { + $this->can($object = new DummyObject(), 'new', 'simple'); + + $factory->get($object, 'simple')->shouldHaveBeenCalled(); + $stateMachine->can('new')->shouldHaveBeenCalled(); + } + + function it_provide_sm_getState_function(FactoryInterface $factory, StateMachineInterface $stateMachine) + { + $this->getState($object = new DummyObject(), 'simple'); + + $factory->get($object, 'simple')->shouldHaveBeenCalled(); + $stateMachine->getState()->shouldHaveBeenCalled(); + } + + function it_provide_sm_getPossibleTransitions_function(FactoryInterface $factory, StateMachineInterface $stateMachine) + { + $this->getPossibleTransitions($object = new DummyObject(), 'simple'); + + $factory->get($object, 'simple')->shouldHaveBeenCalled(); + $stateMachine->getPossibleTransitions()->shouldHaveBeenCalled(); + } +} diff --git a/vendor/winzou/state-machine/spec/SM/Factory/FactorySpec.php b/vendor/winzou/state-machine/spec/SM/Factory/FactorySpec.php new file mode 100644 index 0000000..36816d4 --- /dev/null +++ b/vendor/winzou/state-machine/spec/SM/Factory/FactorySpec.php @@ -0,0 +1,44 @@ + array('state_machine_class' => 'SM\\StateMachine\\StateMachine', 'class' => 'spec\\SM\\DummyObject'), + 'graph2' => array('class' => 'spec\\SM\\DummyObject'), + ); + + function let(EventDispatcherInterface $dispatcher, CallbackFactoryInterface $callbackFactory) + { + $this->beConstructedWith($this->configs, $dispatcher, $callbackFactory); + } + + function it_is_initializable() + { + $this->shouldHaveType('SM\Factory\Factory'); + } + + function it_creates_statemachine(DummyObject $object) + { + $graph = 'graph1'; + + $this->get($object, $graph)->shouldReturnAnInstanceOf($this->configs[$graph]['state_machine_class']); + } + + function it_creates_statemachine_with_default_class(DummyObject $object) + { + $this->get($object, 'graph2')->shouldReturnAnInstanceOf('SM\\StateMachine\\StateMachine'); + } + + function it_throws_exception_when_configuration_doesnt_exist(DummyObject $object) + { + $this->shouldThrow('SM\\SMException')->during('get', array($object, 'non-existing-graph')); + } +} diff --git a/vendor/winzou/state-machine/spec/SM/StateMachine/StateMachineSpec.php b/vendor/winzou/state-machine/spec/SM/StateMachine/StateMachineSpec.php new file mode 100644 index 0000000..5324cd0 --- /dev/null +++ b/vendor/winzou/state-machine/spec/SM/StateMachine/StateMachineSpec.php @@ -0,0 +1,207 @@ + 'graph1', + 'property_path' => 'state', + 'states' => array('checkout', 'pending', 'confirmed', 'cancelled'), + 'transitions' => array( + 'create' => array( + 'from' => array('checkout'), + 'to' => 'pending' + ), + 'confirm' => array( + 'from' => array('checkout', 'pending'), + 'to' => 'confirmed' + ), + 'cancel' => array( + 'from' => array('confirmed'), + 'to' => 'cancelled' + ) + ), + 'callbacks' => array( + 'guard' => array( + 'guard-confirm' => array( + 'from' => array('pending'), + 'do' => 'dummy' + ) + ), + 'before' => array( + 'from-checkout' => array( + 'from' => array('checkout'), + 'do' => 'dummy' + ) + ), + 'after' => array( + 'on-confirm' => array( + 'on' => array('confirm'), + 'do' => 'dummy' + ), + 'to-cancelled' => array( + 'to' => array('cancelled'), + 'do' => 'dummy' + ) + ) + ) + ); + + function let(DummyObject $object, EventDispatcherInterface $dispatcher, CallbackFactoryInterface $callbackFactory) + { + $this->beConstructedWith($object, $this->config, $dispatcher, $callbackFactory); + } + + function it_is_initializable() + { + $this->shouldHaveType('SM\StateMachine\StateMachine'); + } + + function it_can($object, $dispatcher, $callbackFactory, CallbackInterface $guard) + { + $object->getState()->shouldBeCalled()->willReturn('checkout'); + $object->setState(Argument::any())->shouldNotBeCalled(); + + $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + + $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard); + + $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true); + + $this->can('create')->shouldReturn(true); + } + + function it_cannot($object, $dispatcher) + { + $object->getState()->shouldBeCalled()->willReturn('cancel'); + $object->setState(Argument::any())->shouldNotBeCalled(); + + $dispatcher->dispatch(Argument::any())->shouldNotBeCalled(); + + $this->can('create')->shouldReturn(false); + } + + function it_is_guarded_and_can($object, $dispatcher, $callbackFactory, CallbackInterface $guard) + { + $object->getState()->shouldBeCalled()->willReturn('pending'); + $object->setState(Argument::any())->shouldNotBeCalled(); + + $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + + $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard); + + $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true); + + $this->can('confirm')->shouldReturn(true); + } + + function it_is_guarded_and_cannot($object, $dispatcher, $callbackFactory, CallbackInterface $guard) + { + $object->getState()->shouldBeCalled()->willReturn('pending'); + $object->setState(Argument::any())->shouldNotBeCalled(); + + $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + + $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard); + + $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(false); + + $this->can('confirm')->shouldReturn(false); + } + + function it_throws_an_exception_if_transition_doesnt_exist_on_can() + { + $this->shouldThrow('SM\\SMException')->during('can', array('non-existing-transition')); + } + + function it_applies_transition( + $object, + $dispatcher, + $callbackFactory, + CallbackInterface $guard, + CallbackInterface $callback1, + CallbackInterface $callback2, + CallbackInterface $callback3 + ) { + $object->getState()->shouldBeCalled()->willReturn('checkout'); + $object->setState('confirmed')->shouldBeCalled(); + + $dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + $dispatcher->dispatch(SMEvents::PRE_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + $dispatcher->dispatch(SMEvents::POST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + + $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard); + $callbackFactory->get($this->config['callbacks']['before']['from-checkout'])->shouldBeCalled()->willReturn($callback1); + $callbackFactory->get($this->config['callbacks']['after']['on-confirm'])->shouldBeCalled()->willReturn($callback2); + $callbackFactory->get($this->config['callbacks']['after']['to-cancelled'])->shouldBeCalled()->willReturn($callback3); + + $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true); + $callback1->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + $callback2->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + $callback3->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled(); + + $this->apply('confirm'); + } + + function it_throws_an_exception_if_transition_cannot_be_applied($object, $dispatcher) + { + $object->getState()->shouldBeCalled()->willReturn('cancel'); + $object->setState(Argument::any())->shouldNotBeCalled(); + + $dispatcher->dispatch(Argument::any())->shouldNotBeCalled(); + + $this->shouldThrow('SM\\SMException')->during('apply', array('confirm')); + } + + function it_does_nothing_if_transition_cannot_be_applied_in_soft_mode($object, $dispatcher) + { + $object->getState()->shouldBeCalled()->willReturn('cancel'); + $object->setState(Argument::any())->shouldNotBeCalled(); + + $dispatcher->dispatch(Argument::any())->shouldNotBeCalled(); + + $this->apply('confirm', true); + } + + function it_throws_an_exception_if_transition_doesnt_exist_on_apply() + { + $this->shouldThrow('SM\\SMException')->during('apply', array('non-existing-transition')); + } + + function it_returns_current_state($object) + { + $object->getState()->shouldBeCalled()->willReturn('my-state'); + + $this->getState()->shouldReturn('my-state'); + } + + function it_returns_current_graph() + { + $this->getGraph()->shouldReturn($this->config['graph']); + } + + function it_returns_current_object($object) + { + $this->getObject()->shouldReturn($object); + } + + function it_returns_possible_transitions($object, $callbackFactory, CallbackInterface $guard) + { + $object->getState()->shouldBeCalled()->willReturn('checkout'); + + $callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard); + + $guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true); + + $this->getPossibleTransitions()->shouldReturn(array('create', 'confirm')); + } +} diff --git a/vendor/winzou/state-machine/src/SM/Callback/Callback.php b/vendor/winzou/state-machine/src/SM/Callback/Callback.php new file mode 100644 index 0000000..dc749cf --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Callback/Callback.php @@ -0,0 +1,144 @@ +specs = $specs; + $this->callable = $callable; + } + + /** + * @param TransitionEvent $event + * + * @return mixed The returned value from the callback + */ + public function call(TransitionEvent $event) + { + if (!isset($this->specs['args'])) { + $args = array($event); + } else { + $expr = new ExpressionLanguage(); + $args = array_map( + function($arg) use($expr, $event) { + if (!is_string($arg)) { + return $arg; + } + + return $expr->evaluate($arg, array( + 'object' => $event->getStateMachine()->getObject(), + 'event' => $event + )); + }, $this->specs['args'] + ); + } + + $callable = $this->filterCallable($this->callable, $event); + + return call_user_func_array($callable, $args); + } + + /** + * {@inheritDoc} + */ + public function __invoke(TransitionEvent $event) + { + if ($this->isSatisfiedBy($event)) { + return $this->call($event); + } + return true; + } + + /** + * {@inheritDoc} + */ + public function isSatisfiedBy(TransitionEvent $event) + { + $config = $event->getConfig(); + + return + $this->isSatisfiedByClause('on', $event->getTransition()) + && $this->isSatisfiedByClause('from', $event->getState()) + && $this->isSatisfiedByClause('to', $config['to']) + ; + } + + /** + * @param string $clause The clause to check (on, from or to) + * @param string $value The value to check the clause against + * + * @return bool + */ + protected function isSatisfiedByClause($clause, $value) + { + if (0 < count($this->specs[$clause]) && !in_array($value, $this->specs[$clause])) { + return false; + } + + if (0 < count($this->specs['excluded_'.$clause]) && in_array($value, $this->specs['excluded_'.$clause])) { + return false; + } + + return true; + } + + /** + * @param callable|array $callable A callable or array with index 0 starting with "object" that will evaluated as a property path with "object" being the object undergoing the transition + * @param TransitionEvent $event + * + * @return callable + */ + protected function filterCallable($callable, TransitionEvent $event) + { + if (is_array($callable) && isset($callable[0]) && is_string($callable[0]) && 'object' === substr($callable[0], 0, 6)) { + $object = $event->getStateMachine()->getObject(); + + // callable could be "object.property" and not just "object", so we evaluate the "property" path + if ('object' !== $callable[0]) { + $accessor = new PropertyAccessor(); + $object = $accessor->getValue($object, substr($callable[0], 7)); + } + + return array($object, $callable[1]); + } + + return $callable; + } +} diff --git a/vendor/winzou/state-machine/src/SM/Callback/CallbackFactory.php b/vendor/winzou/state-machine/src/SM/Callback/CallbackFactory.php new file mode 100644 index 0000000..6e58ac1 --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Callback/CallbackFactory.php @@ -0,0 +1,50 @@ +class = $class; + } + + /** + * {@inheritDoc} + */ + public function get(array $specs) + { + if (!isset($specs['do'])) { + throw new SMException(sprintf( + 'CallbackFactory::get needs the index "do" to be able to build a callback, array %s given.', + json_encode($specs) + )); + } + + $class = $this->class; + return new $class($specs, $specs['do']); + } +} diff --git a/vendor/winzou/state-machine/src/SM/Callback/CallbackFactoryInterface.php b/vendor/winzou/state-machine/src/SM/Callback/CallbackFactoryInterface.php new file mode 100644 index 0000000..7add7b3 --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Callback/CallbackFactoryInterface.php @@ -0,0 +1,24 @@ + + */ +class CascadeTransitionCallback +{ + /** + * @var FactoryInterface + */ + protected $factory; + + /** + * @param FactoryInterface $factory + */ + public function __construct(FactoryInterface $factory) + { + $this->factory = $factory; + } + + /** + * Apply a transition to the object that has just undergone a transition + * + * @param \Traversable|array $objects Object or array|traversable of objects to apply the transition on + * @param TransitionEvent $event Transition event + * @param string|null $transition Transition that is to be applied (if null, same as the trigger) + * @param string|null $graph Graph on which the new transition will apply (if null, same as the trigger) + * @param bool $soft If true, check if it can apply the transition first (no Exception thrown) + */ + public function apply($objects, TransitionEvent $event, $transition = null, $graph = null, $soft = true) + { + if (!is_array($objects) && !$objects instanceof \Traversable) { + $objects = array($objects); + } + + if (null === $transition) { + $transition = $event->getTransition(); + } + + if (null === $graph) { + $graph = $event->getStateMachine()->getGraph(); + } + + foreach ($objects as $object) { + $this->factory->get($object, $graph)->apply($transition, $soft); + } + } +} diff --git a/vendor/winzou/state-machine/src/SM/Event/SMEvents.php b/vendor/winzou/state-machine/src/SM/Event/SMEvents.php new file mode 100644 index 0000000..9b7ac95 --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Event/SMEvents.php @@ -0,0 +1,19 @@ +transition = $transition; + $this->fromState = $fromState; + $this->config = $config; + $this->stateMachine = $stateMachine; + } + + /** + * @return string + */ + public function getTransition() + { + return $this->transition; + } + + /** + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return StateMachineInterface + */ + public function getStateMachine() + { + return $this->stateMachine; + } + + /** + * @return string + */ + public function getState() + { + return $this->fromState; + } + + /** + * @param bool $reject + */ + public function setRejected($reject = true) + { + $this->rejected = (bool) $reject; + } + + /** + * @return bool + */ + public function isRejected() + { + return $this->rejected; + } +} diff --git a/vendor/winzou/state-machine/src/SM/Extension/Twig/SMExtension.php b/vendor/winzou/state-machine/src/SM/Extension/Twig/SMExtension.php new file mode 100644 index 0000000..cb0800a --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Extension/Twig/SMExtension.php @@ -0,0 +1,84 @@ +factory = $factory; + } + + /** + * @{inheritDoc} + */ + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('sm_can', array($this, 'can')), + new \Twig_SimpleFunction('sm_state', array($this, 'getState')), + new \Twig_SimpleFunction('sm_possible_transitions', array($this, 'getPossibleTransitions')), + ); + } + + /** + * @param object $object + * @param string $transition + * @param string $graph + * + * @return bool + */ + public function can($object, $transition, $graph = 'default') + { + return $this->factory->get($object, $graph)->can($transition); + } + + /** + * @param object $object + * @param string $graph + * + * @return string + */ + public function getState($object, $graph = 'default') + { + return $this->factory->get($object, $graph)->getState(); + } + + /** + * @param object $object + * @param string $graph + * + * @return array + */ + public function getPossibleTransitions($object, $graph = 'default') + { + return $this->factory->get($object, $graph)->getPossibleTransitions(); + } + + /** + * @{inheritDoc} + */ + public function getName() + { + return 'sm'; + } +} diff --git a/vendor/winzou/state-machine/src/SM/Factory/AbstractFactory.php b/vendor/winzou/state-machine/src/SM/Factory/AbstractFactory.php new file mode 100644 index 0000000..37248c7 --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Factory/AbstractFactory.php @@ -0,0 +1,104 @@ + $config) { + $this->addConfig($config, $graph); + } + } + + /** + * {@inheritDoc} + */ + public function get($object, $graph = 'default') + { + $hash = spl_object_hash($object); + + if (isset($this->stateMachines[$hash][$graph])) { + return $this->stateMachines[$hash][$graph]; + } + + foreach ($this->configs as $config) { + if ($config['graph'] === $graph && $object instanceof $config['class']) { + return $this->stateMachines[$hash][$graph] = $this->createStateMachine($object, $config); + } + } + + throw new SMException(sprintf( + 'Cannot create a state machine because the configuration for object "%s" with graph "%s" does not exist.', + get_class($object), + $graph + )); + } + + /** + * {@inheritDoc} + */ + public function clear() + { + $this->stateMachines = array(); + } + + /** + * Adds a new config + * + * @param array $config + * @param string $graph + * + * @throws SMException If the index "class" is not configured + */ + public function addConfig(array $config, $graph = 'default') + { + if (!isset($config['graph'])) { + $config['graph'] = $graph; + } + + if (!isset($config['class'])) { + throw new SMException(sprintf( + 'Index "class" needed for the state machine configuration of graph "%s"', + $config['graph'] + )); + } + + $this->configs[] = $config; + } + + /** + * Create a state machine for the given object and config + * + * @param $object + * @param array $config + * + * @return StateMachineInterface + */ + abstract protected function createStateMachine($object, array $config); +} diff --git a/vendor/winzou/state-machine/src/SM/Factory/ClearableFactoryInterface.php b/vendor/winzou/state-machine/src/SM/Factory/ClearableFactoryInterface.php new file mode 100644 index 0000000..94953c3 --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Factory/ClearableFactoryInterface.php @@ -0,0 +1,20 @@ +dispatcher = $dispatcher; + $this->callbackFactory = $callbackFactory; + } + + /** + * {@inheritDoc} + */ + protected function createStateMachine($object, array $config) + { + if (!isset($config['state_machine_class'])) { + $class = 'SM\\StateMachine\\StateMachine'; + } elseif (class_exists($config['state_machine_class'])) { + $class = $config['state_machine_class']; + } else { + throw new SMException(sprintf( + 'Class "%s" for creating a new state machine does not exist.', + $config['state_machine_class'] + )); + } + + return new $class($object, $config, $this->dispatcher, $this->callbackFactory); + } +} diff --git a/vendor/winzou/state-machine/src/SM/Factory/FactoryInterface.php b/vendor/winzou/state-machine/src/SM/Factory/FactoryInterface.php new file mode 100644 index 0000000..3dd21d0 --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/Factory/FactoryInterface.php @@ -0,0 +1,27 @@ +object = $object; + $this->dispatcher = $dispatcher; + $this->callbackFactory = $callbackFactory ?: new CallbackFactory('SM\Callback\Callback'); + + if (!isset($config['property_path'])) { + $config['property_path'] = 'state'; + } + + $this->config = $config; + + // Test if the given object has the given state property path + try { + $this->getState(); + } catch (NoSuchPropertyException $e) { + throw new SMException(sprintf( + 'Cannot access to configured property path "%s" on object %s with graph "%s"', + $config['property_path'], + get_class($object), + $config['graph'] + )); + } + } + + /** + * {@inheritDoc} + */ + public function can($transition) + { + if (!isset($this->config['transitions'][$transition])) { + throw new SMException(sprintf( + 'Transition "%s" does not exist on object "%s" with graph "%s"', + $transition, + get_class($this->object), + $this->config['graph'] + )); + } + + if (!in_array($this->getState(), $this->config['transitions'][$transition]['from'])) { + return false; + } + + $can = true; + $event = new TransitionEvent($transition, $this->getState(), $this->config['transitions'][$transition], $this); + if (null !== $this->dispatcher) { + $this->dispatcher->dispatch(SMEvents::TEST_TRANSITION, $event); + + $can = !$event->isRejected(); + } + + return $can && $this->callCallbacks($event, 'guard'); + } + + /** + * {@inheritDoc} + */ + public function apply($transition, $soft = false) + { + if (!$this->can($transition)) { + if ($soft) { + return false; + } + + throw new SMException(sprintf( + 'Transition "%s" cannot be applied on state "%s" of object "%s" with graph "%s"', + $transition, + $this->getState(), + get_class($this->object), + $this->config['graph'] + )); + } + + $event = new TransitionEvent($transition, $this->getState(), $this->config['transitions'][$transition], $this); + + if (null !== $this->dispatcher) { + $this->dispatcher->dispatch(SMEvents::PRE_TRANSITION, $event); + + if ($event->isRejected()) { + return false; + } + } + + $this->callCallbacks($event, 'before'); + + $this->setState($this->config['transitions'][$transition]['to']); + + $this->callCallbacks($event, 'after'); + + if (null !== $this->dispatcher) { + $this->dispatcher->dispatch(SMEvents::POST_TRANSITION, $event); + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function getState() + { + $accessor = new PropertyAccessor(); + return $accessor->getValue($this->object, $this->config['property_path']); + } + + /** + * {@inheritDoc} + */ + public function getObject() + { + return $this->object; + } + + /** + * {@inheritDoc} + */ + public function getGraph() + { + return $this->config['graph']; + } + + /** + * {@inheritDoc} + */ + public function getPossibleTransitions() + { + return array_filter( + array_keys($this->config['transitions']), + array($this, 'can') + ); + } + + /** + * Set a new state to the underlying object + * + * @param string $state + * + * @throws SMException + */ + protected function setState($state) + { + if (!in_array($state, $this->config['states'])) { + throw new SMException(sprintf( + 'Cannot set the state to "%s" to object "%s" with graph %s because it is not pre-defined.', + $state, + get_class($this->object), + $this->config['graph'] + )); + } + + $accessor = new PropertyAccessor(); + $accessor->setValue($this->object, $this->config['property_path'], $state); + } + + /** + * Builds and calls the defined callbacks + * + * @param TransitionEvent $event + * @param string $position + * @return bool + */ + protected function callCallbacks(TransitionEvent $event, $position) + { + if (!isset($this->config['callbacks'][$position])) { + return true; + } + + $result = true; + foreach ($this->config['callbacks'][$position] as &$callback) { + if (!$callback instanceof CallbackInterface) { + $callback = $this->callbackFactory->get($callback); + } + + $result = call_user_func($callback, $event) && $result; + } + return $result; + } +} diff --git a/vendor/winzou/state-machine/src/SM/StateMachine/StateMachineInterface.php b/vendor/winzou/state-machine/src/SM/StateMachine/StateMachineInterface.php new file mode 100644 index 0000000..b09da5f --- /dev/null +++ b/vendor/winzou/state-machine/src/SM/StateMachine/StateMachineInterface.php @@ -0,0 +1,68 @@ +setFromArray($options); + } + } + + /** + * Set one or more configuration properties + * + * @param array|Traversable|AbstractOptions $options + * @throws Exception\InvalidArgumentException + * @return AbstractOptions Provides fluent interface + */ + public function setFromArray($options) + { + if ($options instanceof self) { + $options = $options->toArray(); + } + + if (! is_array($options) && ! $options instanceof Traversable) { + throw new Exception\InvalidArgumentException( + sprintf( + 'Parameter provided to %s must be an %s, %s or %s', + __METHOD__, + 'array', + 'Traversable', + 'Zend\Stdlib\AbstractOptions' + ) + ); + } + + foreach ($options as $key => $value) { + $this->__set($key, $value); + } + + return $this; + } + + /** + * Cast to array + * + * @return array + */ + public function toArray() + { + $array = []; + $transform = function ($letters) { + $letter = array_shift($letters); + return '_' . strtolower($letter); + }; + foreach ($this as $key => $value) { + if ($key === '__strictMode__') { + continue; + } + $normalizedKey = preg_replace_callback('/([A-Z])/', $transform, $key); + $array[$normalizedKey] = $value; + } + return $array; + } + + /** + * Set a configuration property + * + * @see ParameterObject::__set() + * @param string $key + * @param mixed $value + * @throws Exception\BadMethodCallException + * @return void + */ + public function __set($key, $value) + { + $setter = 'set' . str_replace('_', '', $key); + + if (is_callable([$this, $setter])) { + $this->{$setter}($value); + + return; + } + + if ($this->__strictMode__) { + throw new Exception\BadMethodCallException(sprintf( + 'The option "%s" does not have a callable "%s" ("%s") setter method which must be defined', + $key, + 'set' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))), + $setter + )); + } + } + + /** + * Get a configuration property + * + * @see ParameterObject::__get() + * @param string $key + * @throws Exception\BadMethodCallException + * @return mixed + */ + public function __get($key) + { + $getter = 'get' . str_replace('_', '', $key); + + if (is_callable([$this, $getter])) { + return $this->{$getter}(); + } + + throw new Exception\BadMethodCallException(sprintf( + 'The option "%s" does not have a callable "%s" getter method which must be defined', + $key, + 'get' . str_replace(' ', '', ucwords(str_replace('_', ' ', $key))) + )); + } + + /** + * Test if a configuration property is null + * @see ParameterObject::__isset() + * @param string $key + * @return bool + */ + public function __isset($key) + { + $getter = 'get' . str_replace('_', '', $key); + + return method_exists($this, $getter) && null !== $this->__get($key); + } + + /** + * Set a configuration property to NULL + * + * @see ParameterObject::__unset() + * @param string $key + * @throws Exception\InvalidArgumentException + * @return void + */ + public function __unset($key) + { + try { + $this->__set($key, null); + } catch (Exception\BadMethodCallException $e) { + throw new Exception\InvalidArgumentException( + 'The class property $' . $key . ' cannot be unset as' + . ' NULL is an invalid value for it', + 0, + $e + ); + } + } +} diff --git a/vendor/zendframework/zend-stdlib/src/ArrayObject.php b/vendor/zendframework/zend-stdlib/src/ArrayObject.php new file mode 100644 index 0000000..8c77c62 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/ArrayObject.php @@ -0,0 +1,434 @@ +setFlags($flags); + $this->storage = $input; + $this->setIteratorClass($iteratorClass); + $this->protectedProperties = array_keys(get_object_vars($this)); + } + + /** + * Returns whether the requested key exists + * + * @param mixed $key + * @return bool + */ + public function __isset($key) + { + if ($this->flag == self::ARRAY_AS_PROPS) { + return $this->offsetExists($key); + } + if (in_array($key, $this->protectedProperties)) { + throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + } + + return isset($this->$key); + } + + /** + * Sets the value at the specified key to value + * + * @param mixed $key + * @param mixed $value + * @return void + */ + public function __set($key, $value) + { + if ($this->flag == self::ARRAY_AS_PROPS) { + return $this->offsetSet($key, $value); + } + if (in_array($key, $this->protectedProperties)) { + throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + } + $this->$key = $value; + } + + /** + * Unsets the value at the specified key + * + * @param mixed $key + * @return void + */ + public function __unset($key) + { + if ($this->flag == self::ARRAY_AS_PROPS) { + return $this->offsetUnset($key); + } + if (in_array($key, $this->protectedProperties)) { + throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + } + unset($this->$key); + } + + /** + * Returns the value at the specified key by reference + * + * @param mixed $key + * @return mixed + */ + public function &__get($key) + { + $ret = null; + if ($this->flag == self::ARRAY_AS_PROPS) { + $ret =& $this->offsetGet($key); + + return $ret; + } + if (in_array($key, $this->protectedProperties)) { + throw new Exception\InvalidArgumentException('$key is a protected property, use a different key'); + } + + return $this->$key; + } + + /** + * Appends the value + * + * @param mixed $value + * @return void + */ + public function append($value) + { + $this->storage[] = $value; + } + + /** + * Sort the entries by value + * + * @return void + */ + public function asort() + { + asort($this->storage); + } + + /** + * Get the number of public properties in the ArrayObject + * + * @return int + */ + public function count() + { + return count($this->storage); + } + + /** + * Exchange the array for another one. + * + * @param array|ArrayObject $data + * @return array + */ + public function exchangeArray($data) + { + if (! is_array($data) && ! is_object($data)) { + throw new Exception\InvalidArgumentException( + 'Passed variable is not an array or object, using empty array instead' + ); + } + + if (is_object($data) && ($data instanceof self || $data instanceof \ArrayObject)) { + $data = $data->getArrayCopy(); + } + if (! is_array($data)) { + $data = (array) $data; + } + + $storage = $this->storage; + + $this->storage = $data; + + return $storage; + } + + /** + * Creates a copy of the ArrayObject. + * + * @return array + */ + public function getArrayCopy() + { + return $this->storage; + } + + /** + * Gets the behavior flags. + * + * @return int + */ + public function getFlags() + { + return $this->flag; + } + + /** + * Create a new iterator from an ArrayObject instance + * + * @return \Iterator + */ + public function getIterator() + { + $class = $this->iteratorClass; + + return new $class($this->storage); + } + + /** + * Gets the iterator classname for the ArrayObject. + * + * @return string + */ + public function getIteratorClass() + { + return $this->iteratorClass; + } + + /** + * Sort the entries by key + * + * @return void + */ + public function ksort() + { + ksort($this->storage); + } + + /** + * Sort an array using a case insensitive "natural order" algorithm + * + * @return void + */ + public function natcasesort() + { + natcasesort($this->storage); + } + + /** + * Sort entries using a "natural order" algorithm + * + * @return void + */ + public function natsort() + { + natsort($this->storage); + } + + /** + * Returns whether the requested key exists + * + * @param mixed $key + * @return bool + */ + public function offsetExists($key) + { + return isset($this->storage[$key]); + } + + /** + * Returns the value at the specified key + * + * @param mixed $key + * @return mixed + */ + public function &offsetGet($key) + { + $ret = null; + if (! $this->offsetExists($key)) { + return $ret; + } + $ret =& $this->storage[$key]; + + return $ret; + } + + /** + * Sets the value at the specified key to value + * + * @param mixed $key + * @param mixed $value + * @return void + */ + public function offsetSet($key, $value) + { + $this->storage[$key] = $value; + } + + /** + * Unsets the value at the specified key + * + * @param mixed $key + * @return void + */ + public function offsetUnset($key) + { + if ($this->offsetExists($key)) { + unset($this->storage[$key]); + } + } + + /** + * Serialize an ArrayObject + * + * @return string + */ + public function serialize() + { + return serialize(get_object_vars($this)); + } + + /** + * Sets the behavior flags + * + * @param int $flags + * @return void + */ + public function setFlags($flags) + { + $this->flag = $flags; + } + + /** + * Sets the iterator classname for the ArrayObject + * + * @param string $class + * @return void + */ + public function setIteratorClass($class) + { + if (class_exists($class)) { + $this->iteratorClass = $class; + + return ; + } + + if (strpos($class, '\\') === 0) { + $class = '\\' . $class; + if (class_exists($class)) { + $this->iteratorClass = $class; + + return ; + } + } + + throw new Exception\InvalidArgumentException('The iterator class does not exist'); + } + + /** + * Sort the entries with a user-defined comparison function and maintain key association + * + * @param callable $function + * @return void + */ + public function uasort($function) + { + if (is_callable($function)) { + uasort($this->storage, $function); + } + } + + /** + * Sort the entries by keys using a user-defined comparison function + * + * @param callable $function + * @return void + */ + public function uksort($function) + { + if (is_callable($function)) { + uksort($this->storage, $function); + } + } + + /** + * Unserialize an ArrayObject + * + * @param string $data + * @return void + */ + public function unserialize($data) + { + $ar = unserialize($data); + $this->protectedProperties = array_keys(get_object_vars($this)); + + $this->setFlags($ar['flag']); + $this->exchangeArray($ar['storage']); + $this->setIteratorClass($ar['iteratorClass']); + + foreach ($ar as $k => $v) { + switch ($k) { + case 'flag': + $this->setFlags($v); + break; + case 'storage': + $this->exchangeArray($v); + break; + case 'iteratorClass': + $this->setIteratorClass($v); + break; + case 'protectedProperties': + break; + default: + $this->__set($k, $v); + } + } + } +} diff --git a/vendor/zendframework/zend-stdlib/src/ArraySerializableInterface.php b/vendor/zendframework/zend-stdlib/src/ArraySerializableInterface.php new file mode 100644 index 0000000..dcf8471 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/ArraySerializableInterface.php @@ -0,0 +1,28 @@ +getArrayCopy(); + return new ArrayIterator(array_reverse($array)); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/ArrayUtils.php b/vendor/zendframework/zend-stdlib/src/ArrayUtils.php new file mode 100644 index 0000000..4edcacf --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/ArrayUtils.php @@ -0,0 +1,314 @@ + 0; + } + + /** + * Test whether an array contains one or more integer keys + * + * @param mixed $value + * @param bool $allowEmpty Should an empty array() return true + * @return bool + */ + public static function hasIntegerKeys($value, $allowEmpty = false) + { + if (! is_array($value)) { + return false; + } + + if (! $value) { + return $allowEmpty; + } + + return count(array_filter(array_keys($value), 'is_int')) > 0; + } + + /** + * Test whether an array contains one or more numeric keys. + * + * A numeric key can be one of the following: + * - an integer 1, + * - a string with a number '20' + * - a string with negative number: '-1000' + * - a float: 2.2120, -78.150999 + * - a string with float: '4000.99999', '-10.10' + * + * @param mixed $value + * @param bool $allowEmpty Should an empty array() return true + * @return bool + */ + public static function hasNumericKeys($value, $allowEmpty = false) + { + if (! is_array($value)) { + return false; + } + + if (! $value) { + return $allowEmpty; + } + + return count(array_filter(array_keys($value), 'is_numeric')) > 0; + } + + /** + * Test whether an array is a list + * + * A list is a collection of values assigned to continuous integer keys + * starting at 0 and ending at count() - 1. + * + * For example: + * + * $list = array('a', 'b', 'c', 'd'); + * $list = array( + * 0 => 'foo', + * 1 => 'bar', + * 2 => array('foo' => 'baz'), + * ); + * + * + * @param mixed $value + * @param bool $allowEmpty Is an empty list a valid list? + * @return bool + */ + public static function isList($value, $allowEmpty = false) + { + if (! is_array($value)) { + return false; + } + + if (! $value) { + return $allowEmpty; + } + + return (array_values($value) === $value); + } + + /** + * Test whether an array is a hash table. + * + * An array is a hash table if: + * + * 1. Contains one or more non-integer keys, or + * 2. Integer keys are non-continuous or misaligned (not starting with 0) + * + * For example: + * + * $hash = array( + * 'foo' => 15, + * 'bar' => false, + * ); + * $hash = array( + * 1995 => 'Birth of PHP', + * 2009 => 'PHP 5.3.0', + * 2012 => 'PHP 5.4.0', + * ); + * $hash = array( + * 'formElement, + * 'options' => array( 'debug' => true ), + * ); + * + * + * @param mixed $value + * @param bool $allowEmpty Is an empty array() a valid hash table? + * @return bool + */ + public static function isHashTable($value, $allowEmpty = false) + { + if (! is_array($value)) { + return false; + } + + if (! $value) { + return $allowEmpty; + } + + return (array_values($value) !== $value); + } + + /** + * Checks if a value exists in an array. + * + * Due to "foo" == 0 === TRUE with in_array when strict = false, an option + * has been added to prevent this. When $strict = 0/false, the most secure + * non-strict check is implemented. if $strict = -1, the default in_array + * non-strict behaviour is used. + * + * @param mixed $needle + * @param array $haystack + * @param int|bool $strict + * @return bool + */ + public static function inArray($needle, array $haystack, $strict = false) + { + if (! $strict) { + if (is_int($needle) || is_float($needle)) { + $needle = (string) $needle; + } + if (is_string($needle)) { + foreach ($haystack as &$h) { + if (is_int($h) || is_float($h)) { + $h = (string) $h; + } + } + } + } + return in_array($needle, $haystack, $strict); + } + + /** + * Convert an iterator to an array. + * + * Converts an iterator to an array. The $recursive flag, on by default, + * hints whether or not you want to do so recursively. + * + * @param array|Traversable $iterator The array or Traversable object to convert + * @param bool $recursive Recursively check all nested structures + * @throws Exception\InvalidArgumentException if $iterator is not an array or a Traversable object + * @return array + */ + public static function iteratorToArray($iterator, $recursive = true) + { + if (! is_array($iterator) && ! $iterator instanceof Traversable) { + throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable object'); + } + + if (! $recursive) { + if (is_array($iterator)) { + return $iterator; + } + + return iterator_to_array($iterator); + } + + if (method_exists($iterator, 'toArray')) { + return $iterator->toArray(); + } + + $array = []; + foreach ($iterator as $key => $value) { + if (is_scalar($value)) { + $array[$key] = $value; + continue; + } + + if ($value instanceof Traversable) { + $array[$key] = static::iteratorToArray($value, $recursive); + continue; + } + + if (is_array($value)) { + $array[$key] = static::iteratorToArray($value, $recursive); + continue; + } + + $array[$key] = $value; + } + + return $array; + } + + /** + * Merge two arrays together. + * + * If an integer key exists in both arrays and preserveNumericKeys is false, the value + * from the second array will be appended to the first array. If both values are arrays, they + * are merged together, else the value of the second array overwrites the one of the first array. + * + * @param array $a + * @param array $b + * @param bool $preserveNumericKeys + * @return array + */ + public static function merge(array $a, array $b, $preserveNumericKeys = false) + { + foreach ($b as $key => $value) { + if ($value instanceof MergeReplaceKeyInterface) { + $a[$key] = $value->getData(); + } elseif (isset($a[$key]) || array_key_exists($key, $a)) { + if ($value instanceof MergeRemoveKey) { + unset($a[$key]); + } elseif (! $preserveNumericKeys && is_int($key)) { + $a[] = $value; + } elseif (is_array($value) && is_array($a[$key])) { + $a[$key] = static::merge($a[$key], $value, $preserveNumericKeys); + } else { + $a[$key] = $value; + } + } else { + if (! $value instanceof MergeRemoveKey) { + $a[$key] = $value; + } + } + } + + return $a; + } + + /** + * @deprecated Since 3.2.0; use the native array_filter methods + * + * @param array $data + * @param callable $callback + * @param null|int $flag + * @return array + * @throws Exception\InvalidArgumentException + */ + public static function filter(array $data, $callback, $flag = null) + { + if (! is_callable($callback)) { + throw new Exception\InvalidArgumentException(sprintf( + 'Second parameter of %s must be callable', + __METHOD__ + )); + } + + return array_filter($data, $callback, $flag); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeRemoveKey.php b/vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeRemoveKey.php new file mode 100644 index 0000000..7c4d097 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeRemoveKey.php @@ -0,0 +1,14 @@ +data = $data; + } + + /** + * {@inheritDoc} + */ + public function getData() + { + return $this->data; + } +} diff --git a/vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeReplaceKeyInterface.php b/vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeReplaceKeyInterface.php new file mode 100644 index 0000000..725cf11 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/ArrayUtils/MergeReplaceKeyInterface.php @@ -0,0 +1,21 @@ +message`, + * `message`) + * - Write output to a specified stream, optionally with colorization. + * - Write a line of output to a specified stream, optionally with + * colorization, using the system EOL sequence.. + * - Write an error message to STDERR. + * + * Colorization will only occur when expected sequences are discovered, and + * then, only if the console terminal allows it. + * + * Essentially, provides the bare minimum to allow you to provide messages to + * the current console. + */ +class ConsoleHelper +{ + const COLOR_GREEN = "\033[32m"; + const COLOR_RED = "\033[31m"; + const COLOR_RESET = "\033[0m"; + + const HIGHLIGHT_INFO = 'info'; + const HIGHLIGHT_ERROR = 'error'; + + private $highlightMap = [ + self::HIGHLIGHT_INFO => self::COLOR_GREEN, + self::HIGHLIGHT_ERROR => self::COLOR_RED, + ]; + + /** + * @var string Exists only for testing. + */ + private $eol = PHP_EOL; + + /** + * @var resource Exists only for testing. + */ + private $stderr = STDERR; + + /** + * @var bool + */ + private $supportsColor; + + /** + * @param resource $resource + */ + public function __construct($resource = STDOUT) + { + $this->supportsColor = $this->detectColorCapabilities($resource); + } + + /** + * Colorize a string for use with the terminal. + * + * Takes strings formatted as `string` and formats them per the + * $highlightMap; if color support is disabled, simply removes the formatting + * tags. + * + * @param string $string + * @return string + */ + public function colorize($string) + { + $reset = $this->supportsColor ? self::COLOR_RESET : ''; + foreach ($this->highlightMap as $key => $color) { + $pattern = sprintf('#<%s>(.*?)#s', $key, $key); + $color = $this->supportsColor ? $color : ''; + $string = preg_replace($pattern, $color . '$1' . $reset, $string); + } + return $string; + } + + /** + * @param string $string + * @param bool $colorize Whether or not to colorize the string + * @param resource $resource Defaults to STDOUT + * @return void + */ + public function write($string, $colorize = true, $resource = STDOUT) + { + if ($colorize) { + $string = $this->colorize($string); + } + + $string = $this->formatNewlines($string); + + fwrite($resource, $string); + } + + /** + * @param string $string + * @param bool $colorize Whether or not to colorize the line + * @param resource $resource Defaults to STDOUT + * @return void + */ + public function writeLine($string, $colorize = true, $resource = STDOUT) + { + $this->write($string . $this->eol, $colorize, $resource); + } + + /** + * Emit an error message. + * + * Wraps the message in ``, and passes it to `writeLine()`, + * using STDERR as the resource; emits an additional empty line when done, + * also to STDERR. + * + * @param string $message + * @return void + */ + public function writeErrorMessage($message) + { + $this->writeLine(sprintf('%s', $message), true, $this->stderr); + $this->writeLine('', false, $this->stderr); + } + + /** + * @param resource $resource + * @return bool + */ + private function detectColorCapabilities($resource = STDOUT) + { + if ('\\' === DIRECTORY_SEPARATOR) { + // Windows + return false !== getenv('ANSICON') + || 'ON' === getenv('ConEmuANSI') + || 'xterm' === getenv('TERM'); + } + + return function_exists('posix_isatty') && posix_isatty($resource); + } + + /** + * Ensure newlines are appropriate for the current terminal. + * + * @param string + * @return string + */ + private function formatNewlines($string) + { + $string = str_replace($this->eol, "\0PHP_EOL\0", $string); + $string = preg_replace("/(\r\n|\n|\r)/", $this->eol, $string); + return str_replace("\0PHP_EOL\0", $this->eol, $string); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/DispatchableInterface.php b/vendor/zendframework/zend-stdlib/src/DispatchableInterface.php new file mode 100644 index 0000000..4f74d1e --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/DispatchableInterface.php @@ -0,0 +1,22 @@ +values[$priority][] = $value; + if (! isset($this->priorities[$priority])) { + $this->priorities[$priority] = $priority; + $this->maxPriority = $this->maxPriority === null ? $priority : max($priority, $this->maxPriority); + } + ++$this->count; + } + + /** + * Extract an element in the queue according to the priority and the + * order of insertion + * + * @return mixed + */ + public function extract() + { + if (! $this->valid()) { + return false; + } + $value = $this->current(); + $this->nextAndRemove(); + return $value; + } + + /** + * Remove an item from the queue + * + * This is different than {@link extract()}; its purpose is to dequeue an + * item. + * + * Note: this removes the first item matching the provided item found. If + * the same item has been added multiple times, it will not remove other + * instances. + * + * @param mixed $datum + * @return bool False if the item was not found, true otherwise. + */ + public function remove($datum) + { + $currentIndex = $this->index; + $currentSubIndex = $this->subIndex; + $currentPriority = $this->maxPriority; + + $this->rewind(); + while ($this->valid()) { + if (current($this->values[$this->maxPriority]) === $datum) { + $index = key($this->values[$this->maxPriority]); + unset($this->values[$this->maxPriority][$index]); + + // The `next()` method advances the internal array pointer, so we need to use the `reset()` function, + // otherwise we would lose all elements before the place the pointer points. + reset($this->values[$this->maxPriority]); + + $this->index = $currentIndex; + $this->subIndex = $currentSubIndex; + + // If the array is empty we need to destroy the unnecessary priority, + // otherwise we would end up with an incorrect value of `$this->count` + // {@see \Zend\Stdlib\FastPriorityQueue::nextAndRemove()}. + if (empty($this->values[$this->maxPriority])) { + unset($this->values[$this->maxPriority]); + unset($this->priorities[$this->maxPriority]); + if ($this->maxPriority === $currentPriority) { + $this->subIndex = 0; + } + } + + $this->maxPriority = empty($this->priorities) ? null : max($this->priorities); + --$this->count; + return true; + } + $this->next(); + } + return false; + } + + /** + * Get the total number of elements in the queue + * + * @return integer + */ + public function count() + { + return $this->count; + } + + /** + * Get the current element in the queue + * + * @return mixed + */ + public function current() + { + switch ($this->extractFlag) { + case self::EXTR_DATA: + return current($this->values[$this->maxPriority]); + case self::EXTR_PRIORITY: + return $this->maxPriority; + case self::EXTR_BOTH: + return [ + 'data' => current($this->values[$this->maxPriority]), + 'priority' => $this->maxPriority + ]; + } + } + + /** + * Get the index of the current element in the queue + * + * @return integer + */ + public function key() + { + return $this->index; + } + + /** + * Set the iterator pointer to the next element in the queue + * removing the previous element + */ + protected function nextAndRemove() + { + $key = key($this->values[$this->maxPriority]); + + if (false === next($this->values[$this->maxPriority])) { + unset($this->priorities[$this->maxPriority]); + unset($this->values[$this->maxPriority]); + $this->maxPriority = empty($this->priorities) ? null : max($this->priorities); + $this->subIndex = -1; + } else { + unset($this->values[$this->maxPriority][$key]); + } + ++$this->index; + ++$this->subIndex; + --$this->count; + } + + /** + * Set the iterator pointer to the next element in the queue + * without removing the previous element + */ + public function next() + { + if (false === next($this->values[$this->maxPriority])) { + unset($this->subPriorities[$this->maxPriority]); + reset($this->values[$this->maxPriority]); + $this->maxPriority = empty($this->subPriorities) ? null : max($this->subPriorities); + $this->subIndex = -1; + } + ++$this->index; + ++$this->subIndex; + } + + /** + * Check if the current iterator is valid + * + * @return boolean + */ + public function valid() + { + return isset($this->values[$this->maxPriority]); + } + + /** + * Rewind the current iterator + */ + public function rewind() + { + $this->subPriorities = $this->priorities; + $this->maxPriority = empty($this->priorities) ? 0 : max($this->priorities); + $this->index = 0; + $this->subIndex = 0; + } + + /** + * Serialize to an array + * + * Array will be priority => data pairs + * + * @return array + */ + public function toArray() + { + $array = []; + foreach (clone $this as $item) { + $array[] = $item; + } + return $array; + } + + /** + * Serialize + * + * @return string + */ + public function serialize() + { + $clone = clone $this; + $clone->setExtractFlags(self::EXTR_BOTH); + + $data = []; + foreach ($clone as $item) { + $data[] = $item; + } + + return serialize($data); + } + + /** + * Deserialize + * + * @param string $data + * @return void + */ + public function unserialize($data) + { + foreach (unserialize($data) as $item) { + $this->insert($item['data'], $item['priority']); + } + } + + /** + * Set the extract flag + * + * @param integer $flag + */ + public function setExtractFlags($flag) + { + switch ($flag) { + case self::EXTR_DATA: + case self::EXTR_PRIORITY: + case self::EXTR_BOTH: + $this->extractFlag = $flag; + break; + default: + throw new Exception\InvalidArgumentException("The extract flag specified is not valid"); + } + } + + /** + * Check if the queue is empty + * + * @return boolean + */ + public function isEmpty() + { + return empty($this->values); + } + + /** + * Does the queue contain the given datum? + * + * @param mixed $datum + * @return bool + */ + public function contains($datum) + { + foreach ($this->values as $values) { + if (in_array($datum, $values)) { + return true; + } + } + return false; + } + + /** + * Does the queue have an item with the given priority? + * + * @param int $priority + * @return bool + */ + public function hasPriority($priority) + { + return isset($this->values[$priority]); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/Glob.php b/vendor/zendframework/zend-stdlib/src/Glob.php new file mode 100644 index 0000000..ded3fc5 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/Glob.php @@ -0,0 +1,202 @@ + GLOB_MARK, + self::GLOB_NOSORT => GLOB_NOSORT, + self::GLOB_NOCHECK => GLOB_NOCHECK, + self::GLOB_NOESCAPE => GLOB_NOESCAPE, + self::GLOB_BRACE => defined('GLOB_BRACE') ? GLOB_BRACE : 0, + self::GLOB_ONLYDIR => GLOB_ONLYDIR, + self::GLOB_ERR => GLOB_ERR, + ]; + + $globFlags = 0; + + foreach ($flagMap as $internalFlag => $globFlag) { + if ($flags & $internalFlag) { + $globFlags |= $globFlag; + } + } + } else { + $globFlags = 0; + } + + ErrorHandler::start(); + $res = glob($pattern, $globFlags); + $err = ErrorHandler::stop(); + if ($res === false) { + throw new Exception\RuntimeException("glob('{$pattern}', {$globFlags}) failed", 0, $err); + } + return $res; + } + + /** + * Expand braces manually, then use the system glob. + * + * @param string $pattern + * @param int $flags + * @return array + * @throws Exception\RuntimeException + */ + protected static function fallbackGlob($pattern, $flags) + { + if (! $flags & self::GLOB_BRACE) { + return static::systemGlob($pattern, $flags); + } + + $flags &= ~self::GLOB_BRACE; + $length = strlen($pattern); + $paths = []; + + if ($flags & self::GLOB_NOESCAPE) { + $begin = strpos($pattern, '{'); + } else { + $begin = 0; + + while (true) { + if ($begin === $length) { + $begin = false; + break; + } elseif ($pattern[$begin] === '\\' && ($begin + 1) < $length) { + $begin++; + } elseif ($pattern[$begin] === '{') { + break; + } + + $begin++; + } + } + + if ($begin === false) { + return static::systemGlob($pattern, $flags); + } + + $next = static::nextBraceSub($pattern, $begin + 1, $flags); + + if ($next === null) { + return static::systemGlob($pattern, $flags); + } + + $rest = $next; + + while ($pattern[$rest] !== '}') { + $rest = static::nextBraceSub($pattern, $rest + 1, $flags); + + if ($rest === null) { + return static::systemGlob($pattern, $flags); + } + } + + $p = $begin + 1; + + while (true) { + $subPattern = substr($pattern, 0, $begin) + . substr($pattern, $p, $next - $p) + . substr($pattern, $rest + 1); + + $result = static::fallbackGlob($subPattern, $flags | self::GLOB_BRACE); + + if ($result) { + $paths = array_merge($paths, $result); + } + + if ($pattern[$next] === '}') { + break; + } + + $p = $next + 1; + $next = static::nextBraceSub($pattern, $p, $flags); + } + + return array_unique($paths); + } + + /** + * Find the end of the sub-pattern in a brace expression. + * + * @param string $pattern + * @param int $begin + * @param int $flags + * @return int|null + */ + protected static function nextBraceSub($pattern, $begin, $flags) + { + $length = strlen($pattern); + $depth = 0; + $current = $begin; + + while ($current < $length) { + if (! $flags & self::GLOB_NOESCAPE && $pattern[$current] === '\\') { + if (++$current === $length) { + break; + } + + $current++; + } else { + if (($pattern[$current] === '}' && $depth-- === 0) || ($pattern[$current] === ',' && $depth === 0)) { + break; + } elseif ($pattern[$current++] === '{') { + $depth++; + } + } + } + + return ($current < $length ? $current : null); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/Guard/AllGuardsTrait.php b/vendor/zendframework/zend-stdlib/src/Guard/AllGuardsTrait.php new file mode 100644 index 0000000..95bc516 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/Guard/AllGuardsTrait.php @@ -0,0 +1,20 @@ +metadata[$spec] = $value; + return $this; + } + if (! is_array($spec) && ! $spec instanceof Traversable) { + throw new Exception\InvalidArgumentException(sprintf( + 'Expected a string, array, or Traversable argument in first position; received "%s"', + (is_object($spec) ? get_class($spec) : gettype($spec)) + )); + } + foreach ($spec as $key => $value) { + $this->metadata[$key] = $value; + } + return $this; + } + + /** + * Retrieve all metadata or a single metadatum as specified by key + * + * @param null|string|int $key + * @param null|mixed $default + * @throws Exception\InvalidArgumentException + * @return mixed + */ + public function getMetadata($key = null, $default = null) + { + if (null === $key) { + return $this->metadata; + } + + if (! is_scalar($key)) { + throw new Exception\InvalidArgumentException('Non-scalar argument provided for key'); + } + + if (array_key_exists($key, $this->metadata)) { + return $this->metadata[$key]; + } + + return $default; + } + + /** + * Set message content + * + * @param mixed $value + * @return Message + */ + public function setContent($value) + { + $this->content = $value; + return $this; + } + + /** + * Get message content + * + * @return mixed + */ + public function getContent() + { + return $this->content; + } + + /** + * @return string + */ + public function toString() + { + $request = ''; + foreach ($this->getMetadata() as $key => $value) { + $request .= sprintf( + "%s: %s\r\n", + (string) $key, + (string) $value + ); + } + $request .= "\r\n" . $this->getContent(); + return $request; + } +} diff --git a/vendor/zendframework/zend-stdlib/src/MessageInterface.php b/vendor/zendframework/zend-stdlib/src/MessageInterface.php new file mode 100644 index 0000000..28d8857 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/MessageInterface.php @@ -0,0 +1,44 @@ +exchangeArray($values); + } + + /** + * Populate from query string + * + * @param string $string + * @return void + */ + public function fromString($string) + { + $array = []; + parse_str($string, $array); + $this->fromArray($array); + } + + /** + * Serialize to native PHP array + * + * @return array + */ + public function toArray() + { + return $this->getArrayCopy(); + } + + /** + * Serialize to query string + * + * @return string + */ + public function toString() + { + return http_build_query($this->toArray()); + } + + /** + * Retrieve by key + * + * Returns null if the key does not exist. + * + * @param string $name + * @return mixed + */ + public function offsetGet($name) + { + if ($this->offsetExists($name)) { + return parent::offsetGet($name); + } + return; + } + + /** + * @param string $name + * @param mixed $default optional default value + * @return mixed + */ + public function get($name, $default = null) + { + if ($this->offsetExists($name)) { + return parent::offsetGet($name); + } + return $default; + } + + /** + * @param string $name + * @param mixed $value + * @return Parameters + */ + public function set($name, $value) + { + $this[$name] = $value; + return $this; + } +} diff --git a/vendor/zendframework/zend-stdlib/src/ParametersInterface.php b/vendor/zendframework/zend-stdlib/src/ParametersInterface.php new file mode 100644 index 0000000..feeda58 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/ParametersInterface.php @@ -0,0 +1,86 @@ +items[$name])) { + $this->count++; + } + + $this->sorted = false; + + $this->items[$name] = [ + 'data' => $value, + 'priority' => (int) $priority, + 'serial' => $this->serial++, + ]; + } + + /** + * @param string $name + * @param int $priority + * + * @return $this + * + * @throws \Exception + */ + public function setPriority($name, $priority) + { + if (! isset($this->items[$name])) { + throw new \Exception("item $name not found"); + } + + $this->items[$name]['priority'] = (int) $priority; + $this->sorted = false; + + return $this; + } + + /** + * Remove a item. + * + * @param string $name + * @return void + */ + public function remove($name) + { + if (isset($this->items[$name])) { + $this->count--; + } + + unset($this->items[$name]); + } + + /** + * Remove all items. + * + * @return void + */ + public function clear() + { + $this->items = []; + $this->serial = 0; + $this->count = 0; + $this->sorted = false; + } + + /** + * Get a item. + * + * @param string $name + * @return mixed + */ + public function get($name) + { + if (! isset($this->items[$name])) { + return; + } + + return $this->items[$name]['data']; + } + + /** + * Sort all items. + * + * @return void + */ + protected function sort() + { + if (! $this->sorted) { + uasort($this->items, [$this, 'compare']); + $this->sorted = true; + } + } + + /** + * Compare the priority of two items. + * + * @param array $item1, + * @param array $item2 + * @return int + */ + protected function compare(array $item1, array $item2) + { + return ($item1['priority'] === $item2['priority']) + ? ($item1['serial'] > $item2['serial'] ? -1 : 1) * $this->isLIFO + : ($item1['priority'] > $item2['priority'] ? -1 : 1); + } + + /** + * Get/Set serial order mode + * + * @param bool|null $flag + * + * @return bool + */ + public function isLIFO($flag = null) + { + if ($flag !== null) { + $isLifo = $flag === true ? 1 : -1; + + if ($isLifo !== $this->isLIFO) { + $this->isLIFO = $isLifo; + $this->sorted = false; + } + } + + return 1 === $this->isLIFO; + } + + /** + * {@inheritDoc} + */ + public function rewind() + { + $this->sort(); + reset($this->items); + } + + /** + * {@inheritDoc} + */ + public function current() + { + $this->sorted || $this->sort(); + $node = current($this->items); + + return $node ? $node['data'] : false; + } + + /** + * {@inheritDoc} + */ + public function key() + { + $this->sorted || $this->sort(); + return key($this->items); + } + + /** + * {@inheritDoc} + */ + public function next() + { + $node = next($this->items); + + return $node ? $node['data'] : false; + } + + /** + * {@inheritDoc} + */ + public function valid() + { + return current($this->items) !== false; + } + + /** + * @return self + */ + public function getIterator() + { + return clone $this; + } + + /** + * {@inheritDoc} + */ + public function count() + { + return $this->count; + } + + /** + * Return list as array + * + * @param int $flag + * + * @return array + */ + public function toArray($flag = self::EXTR_DATA) + { + $this->sort(); + + if ($flag == self::EXTR_BOTH) { + return $this->items; + } + + return array_map( + function ($item) use ($flag) { + return ($flag == PriorityList::EXTR_PRIORITY) ? $item['priority'] : $item['data']; + }, + $this->items + ); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/PriorityQueue.php b/vendor/zendframework/zend-stdlib/src/PriorityQueue.php new file mode 100644 index 0000000..2a16287 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/PriorityQueue.php @@ -0,0 +1,301 @@ +items[] = [ + 'data' => $data, + 'priority' => $priority, + ]; + $this->getQueue()->insert($data, $priority); + return $this; + } + + /** + * Remove an item from the queue + * + * This is different than {@link extract()}; its purpose is to dequeue an + * item. + * + * This operation is potentially expensive, as it requires + * re-initialization and re-population of the inner queue. + * + * Note: this removes the first item matching the provided item found. If + * the same item has been added multiple times, it will not remove other + * instances. + * + * @param mixed $datum + * @return bool False if the item was not found, true otherwise. + */ + public function remove($datum) + { + $found = false; + foreach ($this->items as $key => $item) { + if ($item['data'] === $datum) { + $found = true; + break; + } + } + if ($found) { + unset($this->items[$key]); + $this->queue = null; + + if (! $this->isEmpty()) { + $queue = $this->getQueue(); + foreach ($this->items as $item) { + $queue->insert($item['data'], $item['priority']); + } + } + return true; + } + return false; + } + + /** + * Is the queue empty? + * + * @return bool + */ + public function isEmpty() + { + return (0 === $this->count()); + } + + /** + * How many items are in the queue? + * + * @return int + */ + public function count() + { + return count($this->items); + } + + /** + * Peek at the top node in the queue, based on priority. + * + * @return mixed + */ + public function top() + { + return $this->getIterator()->top(); + } + + /** + * Extract a node from the inner queue and sift up + * + * @return mixed + */ + public function extract() + { + return $this->getQueue()->extract(); + } + + /** + * Retrieve the inner iterator + * + * SplPriorityQueue acts as a heap, which typically implies that as items + * are iterated, they are also removed. This does not work for situations + * where the queue may be iterated multiple times. As such, this class + * aggregates the values, and also injects an SplPriorityQueue. This method + * retrieves the inner queue object, and clones it for purposes of + * iteration. + * + * @return SplPriorityQueue + */ + public function getIterator() + { + $queue = $this->getQueue(); + return clone $queue; + } + + /** + * Serialize the data structure + * + * @return string + */ + public function serialize() + { + return serialize($this->items); + } + + /** + * Unserialize a string into a PriorityQueue object + * + * Serialization format is compatible with {@link Zend\Stdlib\SplPriorityQueue} + * + * @param string $data + * @return void + */ + public function unserialize($data) + { + foreach (unserialize($data) as $item) { + $this->insert($item['data'], $item['priority']); + } + } + + /** + * Serialize to an array + * + * By default, returns only the item data, and in the order registered (not + * sorted). You may provide one of the EXTR_* flags as an argument, allowing + * the ability to return priorities or both data and priority. + * + * @param int $flag + * @return array + */ + public function toArray($flag = self::EXTR_DATA) + { + switch ($flag) { + case self::EXTR_BOTH: + return $this->items; + case self::EXTR_PRIORITY: + return array_map(function ($item) { + return $item['priority']; + }, $this->items); + case self::EXTR_DATA: + default: + return array_map(function ($item) { + return $item['data']; + }, $this->items); + } + } + + /** + * Specify the internal queue class + * + * Please see {@link getIterator()} for details on the necessity of an + * internal queue class. The class provided should extend SplPriorityQueue. + * + * @param string $class + * @return PriorityQueue + */ + public function setInternalQueueClass($class) + { + $this->queueClass = (string) $class; + return $this; + } + + /** + * Does the queue contain the given datum? + * + * @param mixed $datum + * @return bool + */ + public function contains($datum) + { + foreach ($this->items as $item) { + if ($item['data'] === $datum) { + return true; + } + } + return false; + } + + /** + * Does the queue have an item with the given priority? + * + * @param int $priority + * @return bool + */ + public function hasPriority($priority) + { + foreach ($this->items as $item) { + if ($item['priority'] === $priority) { + return true; + } + } + return false; + } + + /** + * Get the inner priority queue instance + * + * @throws Exception\DomainException + * @return SplPriorityQueue + */ + protected function getQueue() + { + if (null === $this->queue) { + $this->queue = new $this->queueClass(); + if (! $this->queue instanceof \SplPriorityQueue) { + throw new Exception\DomainException(sprintf( + 'PriorityQueue expects an internal queue of type SplPriorityQueue; received "%s"', + get_class($this->queue) + )); + } + } + return $this->queue; + } + + /** + * Add support for deep cloning + * + * @return void + */ + public function __clone() + { + if (null !== $this->queue) { + $this->queue = clone $this->queue; + } + } +} diff --git a/vendor/zendframework/zend-stdlib/src/Request.php b/vendor/zendframework/zend-stdlib/src/Request.php new file mode 100644 index 0000000..7c08403 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/Request.php @@ -0,0 +1,15 @@ +serial--]; + } + parent::insert($datum, $priority); + } + + /** + * Serialize to an array + * + * Array will be priority => data pairs + * + * @return array + */ + public function toArray() + { + $array = []; + foreach (clone $this as $item) { + $array[] = $item; + } + return $array; + } + + /** + * Serialize + * + * @return string + */ + public function serialize() + { + $clone = clone $this; + $clone->setExtractFlags(self::EXTR_BOTH); + + $data = []; + foreach ($clone as $item) { + $data[] = $item; + } + + return serialize($data); + } + + /** + * Deserialize + * + * @param string $data + * @return void + */ + public function unserialize($data) + { + $this->serial = PHP_INT_MAX; + foreach (unserialize($data) as $item) { + $this->serial--; + $this->insert($item['data'], $item['priority']); + } + } +} diff --git a/vendor/zendframework/zend-stdlib/src/SplQueue.php b/vendor/zendframework/zend-stdlib/src/SplQueue.php new file mode 100644 index 0000000..51d8daf --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/SplQueue.php @@ -0,0 +1,55 @@ +toArray()); + } + + /** + * Unserialize + * + * @param string $data + * @return void + */ + public function unserialize($data) + { + foreach (unserialize($data) as $item) { + $this->push($item); + } + } +} diff --git a/vendor/zendframework/zend-stdlib/src/SplStack.php b/vendor/zendframework/zend-stdlib/src/SplStack.php new file mode 100644 index 0000000..6ba2739 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/SplStack.php @@ -0,0 +1,55 @@ +toArray()); + } + + /** + * Unserialize + * + * @param string $data + * @return void + */ + public function unserialize($data) + { + foreach (unserialize($data) as $item) { + $this->unshift($item); + } + } +} diff --git a/vendor/zendframework/zend-stdlib/src/StringUtils.php b/vendor/zendframework/zend-stdlib/src/StringUtils.php new file mode 100644 index 0000000..79a22a4 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/StringUtils.php @@ -0,0 +1,187 @@ +setEncoding($encoding, $convertEncoding); + return $wrapper; + } + } + + throw new Exception\RuntimeException( + 'No wrapper found supporting "' . $encoding . '"' + . (($convertEncoding !== null) ? ' and "' . $convertEncoding . '"' : '') + ); + } + + /** + * Get a list of all known single-byte character encodings + * + * @return string[] + */ + public static function getSingleByteEncodings() + { + return static::$singleByteEncodings; + } + + /** + * Check if a given encoding is a known single-byte character encoding + * + * @param string $encoding + * @return bool + */ + public static function isSingleByteEncoding($encoding) + { + return in_array(strtoupper($encoding), static::$singleByteEncodings); + } + + /** + * Check if a given string is valid UTF-8 encoded + * + * @param string $str + * @return bool + */ + public static function isValidUtf8($str) + { + return is_string($str) && ($str === '' || preg_match('/^./su', $str) == 1); + } + + /** + * Is PCRE compiled with Unicode support? + * + * @return bool + */ + public static function hasPcreUnicodeSupport() + { + if (static::$hasPcreUnicodeSupport === null) { + ErrorHandler::start(); + static::$hasPcreUnicodeSupport = defined('PREG_BAD_UTF8_OFFSET_ERROR') && preg_match('/\pL/u', 'a') == 1; + ErrorHandler::stop(); + } + return static::$hasPcreUnicodeSupport; + } +} diff --git a/vendor/zendframework/zend-stdlib/src/StringWrapper/AbstractStringWrapper.php b/vendor/zendframework/zend-stdlib/src/StringWrapper/AbstractStringWrapper.php new file mode 100644 index 0000000..cf26250 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/StringWrapper/AbstractStringWrapper.php @@ -0,0 +1,269 @@ +convertEncoding = $convertEncodingUpper; + } else { + $this->convertEncoding = null; + } + $this->encoding = $encodingUpper; + + return $this; + } + + /** + * Get the defined character encoding to work with + * + * @return string + * @throws Exception\LogicException If no encoding was defined + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Get the defined character encoding to convert to + * + * @return string|null + */ + public function getConvertEncoding() + { + return $this->convertEncoding; + } + + /** + * Convert a string from defined character encoding to the defined convert encoding + * + * @param string $str + * @param bool $reverse + * @return string|false + */ + public function convert($str, $reverse = false) + { + $encoding = $this->getEncoding(); + $convertEncoding = $this->getConvertEncoding(); + if ($convertEncoding === null) { + throw new Exception\LogicException( + 'No convert encoding defined' + ); + } + + if ($encoding === $convertEncoding) { + return $str; + } + + $from = $reverse ? $convertEncoding : $encoding; + $to = $reverse ? $encoding : $convertEncoding; + throw new Exception\RuntimeException(sprintf( + 'Converting from "%s" to "%s" isn\'t supported by this string wrapper', + $from, + $to + )); + } + + /** + * Wraps a string to a given number of characters + * + * @param string $string + * @param int $width + * @param string $break + * @param bool $cut + * @return string|false + */ + public function wordWrap($string, $width = 75, $break = "\n", $cut = false) + { + $string = (string) $string; + if ($string === '') { + return ''; + } + + $break = (string) $break; + if ($break === '') { + throw new Exception\InvalidArgumentException('Break string cannot be empty'); + } + + $width = (int) $width; + if ($width === 0 && $cut) { + throw new Exception\InvalidArgumentException('Cannot force cut when width is zero'); + } + + if (StringUtils::isSingleByteEncoding($this->getEncoding())) { + return wordwrap($string, $width, $break, $cut); + } + + $stringWidth = $this->strlen($string); + $breakWidth = $this->strlen($break); + + $result = ''; + $lastStart = $lastSpace = 0; + + for ($current = 0; $current < $stringWidth; $current++) { + $char = $this->substr($string, $current, 1); + + $possibleBreak = $char; + if ($breakWidth !== 1) { + $possibleBreak = $this->substr($string, $current, $breakWidth); + } + + if ($possibleBreak === $break) { + $result .= $this->substr($string, $lastStart, $current - $lastStart + $breakWidth); + $current += $breakWidth - 1; + $lastStart = $lastSpace = $current + 1; + continue; + } + + if ($char === ' ') { + if ($current - $lastStart >= $width) { + $result .= $this->substr($string, $lastStart, $current - $lastStart) . $break; + $lastStart = $current + 1; + } + + $lastSpace = $current; + continue; + } + + if ($current - $lastStart >= $width && $cut && $lastStart >= $lastSpace) { + $result .= $this->substr($string, $lastStart, $current - $lastStart) . $break; + $lastStart = $lastSpace = $current; + continue; + } + + if ($current - $lastStart >= $width && $lastStart < $lastSpace) { + $result .= $this->substr($string, $lastStart, $lastSpace - $lastStart) . $break; + $lastStart = $lastSpace = $lastSpace + 1; + continue; + } + } + + if ($lastStart !== $current) { + $result .= $this->substr($string, $lastStart, $current - $lastStart); + } + + return $result; + } + + /** + * Pad a string to a certain length with another string + * + * @param string $input + * @param int $padLength + * @param string $padString + * @param int $padType + * @return string + */ + public function strPad($input, $padLength, $padString = ' ', $padType = STR_PAD_RIGHT) + { + if (StringUtils::isSingleByteEncoding($this->getEncoding())) { + return str_pad($input, $padLength, $padString, $padType); + } + + $lengthOfPadding = $padLength - $this->strlen($input); + if ($lengthOfPadding <= 0) { + return $input; + } + + $padStringLength = $this->strlen($padString); + if ($padStringLength === 0) { + return $input; + } + + $repeatCount = floor($lengthOfPadding / $padStringLength); + + if ($padType === STR_PAD_BOTH) { + $repeatCountLeft = $repeatCountRight = ($repeatCount - $repeatCount % 2) / 2; + + $lastStringLength = $lengthOfPadding - 2 * $repeatCountLeft * $padStringLength; + $lastStringLeftLength = $lastStringRightLength = floor($lastStringLength / 2); + $lastStringRightLength += $lastStringLength % 2; + + $lastStringLeft = $this->substr($padString, 0, $lastStringLeftLength); + $lastStringRight = $this->substr($padString, 0, $lastStringRightLength); + + return str_repeat($padString, $repeatCountLeft) . $lastStringLeft + . $input + . str_repeat($padString, $repeatCountRight) . $lastStringRight; + } + + $lastString = $this->substr($padString, 0, $lengthOfPadding % $padStringLength); + + if ($padType === STR_PAD_LEFT) { + return str_repeat($padString, $repeatCount) . $lastString . $input; + } + + return $input . str_repeat($padString, $repeatCount) . $lastString; + } +} diff --git a/vendor/zendframework/zend-stdlib/src/StringWrapper/Iconv.php b/vendor/zendframework/zend-stdlib/src/StringWrapper/Iconv.php new file mode 100644 index 0000000..bc791b8 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/StringWrapper/Iconv.php @@ -0,0 +1,289 @@ +getEncoding()); + } + + /** + * Returns the portion of string specified by the start and length parameters + * + * @param string $str + * @param int $offset + * @param int|null $length + * @return string|false + */ + public function substr($str, $offset = 0, $length = null) + { + return iconv_substr($str, $offset, $length, $this->getEncoding()); + } + + /** + * Find the position of the first occurrence of a substring in a string + * + * @param string $haystack + * @param string $needle + * @param int $offset + * @return int|false + */ + public function strpos($haystack, $needle, $offset = 0) + { + return iconv_strpos($haystack, $needle, $offset, $this->getEncoding()); + } + + /** + * Convert a string from defined encoding to the defined convert encoding + * + * @param string $str + * @param bool $reverse + * @return string|false + */ + public function convert($str, $reverse = false) + { + $encoding = $this->getEncoding(); + $convertEncoding = $this->getConvertEncoding(); + if ($convertEncoding === null) { + throw new Exception\LogicException( + 'No convert encoding defined' + ); + } + + if ($encoding === $convertEncoding) { + return $str; + } + + $fromEncoding = $reverse ? $convertEncoding : $encoding; + $toEncoding = $reverse ? $encoding : $convertEncoding; + + // automatically add "//IGNORE" to not stop converting on invalid characters + // invalid characters triggers a notice anyway + return iconv($fromEncoding, $toEncoding . '//IGNORE', $str); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/StringWrapper/Intl.php b/vendor/zendframework/zend-stdlib/src/StringWrapper/Intl.php new file mode 100644 index 0000000..4644db3 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/StringWrapper/Intl.php @@ -0,0 +1,88 @@ +getEncoding()); + } + + /** + * Returns the portion of string specified by the start and length parameters + * + * @param string $str + * @param int $offset + * @param int|null $length + * @return string|false + */ + public function substr($str, $offset = 0, $length = null) + { + return mb_substr($str, $offset, $length, $this->getEncoding()); + } + + /** + * Find the position of the first occurrence of a substring in a string + * + * @param string $haystack + * @param string $needle + * @param int $offset + * @return int|false + */ + public function strpos($haystack, $needle, $offset = 0) + { + return mb_strpos($haystack, $needle, $offset, $this->getEncoding()); + } + + /** + * Convert a string from defined encoding to the defined convert encoding + * + * @param string $str + * @param bool $reverse + * @return string|false + */ + public function convert($str, $reverse = false) + { + $encoding = $this->getEncoding(); + $convertEncoding = $this->getConvertEncoding(); + + if ($convertEncoding === null) { + throw new Exception\LogicException( + 'No convert encoding defined' + ); + } + + if ($encoding === $convertEncoding) { + return $str; + } + + $fromEncoding = $reverse ? $convertEncoding : $encoding; + $toEncoding = $reverse ? $encoding : $convertEncoding; + return mb_convert_encoding($str, $toEncoding, $fromEncoding); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/StringWrapper/Native.php b/vendor/zendframework/zend-stdlib/src/StringWrapper/Native.php new file mode 100644 index 0000000..b01bb86 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/StringWrapper/Native.php @@ -0,0 +1,134 @@ +convertEncoding = $encodingUpper; + } + + if ($convertEncoding !== null) { + if ($encodingUpper !== strtoupper($convertEncoding)) { + throw new Exception\InvalidArgumentException( + 'Wrapper doesn\'t support to convert between character encodings' + ); + } + + $this->convertEncoding = $encodingUpper; + } else { + $this->convertEncoding = null; + } + $this->encoding = $encodingUpper; + + return $this; + } + + /** + * Returns the length of the given string + * + * @param string $str + * @return int|false + */ + public function strlen($str) + { + return strlen($str); + } + + /** + * Returns the portion of string specified by the start and length parameters + * + * @param string $str + * @param int $offset + * @param int|null $length + * @return string|false + */ + public function substr($str, $offset = 0, $length = null) + { + return substr($str, $offset, $length); + } + + /** + * Find the position of the first occurrence of a substring in a string + * + * @param string $haystack + * @param string $needle + * @param int $offset + * @return int|false + */ + public function strpos($haystack, $needle, $offset = 0) + { + return strpos($haystack, $needle, $offset); + } +} diff --git a/vendor/zendframework/zend-stdlib/src/StringWrapper/StringWrapperInterface.php b/vendor/zendframework/zend-stdlib/src/StringWrapper/StringWrapperInterface.php new file mode 100644 index 0000000..f25b325 --- /dev/null +++ b/vendor/zendframework/zend-stdlib/src/StringWrapper/StringWrapperInterface.php @@ -0,0 +1,111 @@ +